activiti5 为啥选择 ibatis

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了activiti5 为啥选择 ibatis相关的知识,希望对你有一定的参考价值。

1. iBatis 易于掌握。拿来文档看半天到两天就可以掌握了。
Hibernate 可能需要 3 倍以上的时间来掌握。

2. iBatis 更容易进行 sql 的 优化。

这个应该大家都有共识了。另外 Hibernate 生成的 sql 也实在是太难看了。鉴
于有的朋友提到了 sql 不太重要。我想在这里强调一下我的经验,一般系统性能
的瓶颈都在数据库上。所以这一点是 iBatis 非常重要的一个优势。

3. iBatis 可以进行细粒度的优化

3.1 比如说我有一个表,这个表有几个或者几十个字段,我需要更新其中
的一个字段,iBatis 很简单,执行一个sql
UPDATE TABLE_A SET column_1=#column_1# WHERE id=#id#
但是用 Hibernate 的话就比较麻烦了,缺省的情况下 hibernate 会更新所有字段。
当然我记得 hibernate 有一个选项可以控制只保存修改过的字段,但是我不太确
定这个功能的负面效果。

3.2 我需要列出一个表的部分内容,用 iBatis 的时候,这里面的好处是可以少从数据
库读很多数据,节省流量
SELECT ID, NAME FROM TABLE_WITH_A_LOT_OF_COLUMN WHERE ...

3.2.1 一般情况下
Hibernate 会把所有的字段都选出来。比如说有一个上面表有8个字段,
其中有一两个比较大的字段,varchar(255)/text。上面的场景中我为什么要把他
们也选出来呢?

3.2.2 用 hibernate 的话,你又不能把这两个不需要的字段设置为 lazy load,因
为还有很多地方需要一次把整个 domain object 加载出来。这个时候就能显现出
ibatis 的好处了

3.2.3 Hibernate 还有一个方案,就是生成 javabean/map/object[](感谢
leelun/cjmm),但是这样的话就可能会产生大量的多余 class。map/object[] 的方式
应该不错,我比较喜欢这种方式。

3.3 如果我需要更新一条记录(一个对象),如果使用 hibernate,需要现把对
象 select 出来,然后再做 update。这对数据库来说就是两条 sql。而 iBatis
只需要一条 update 的 sql 就可以了。减少一次与数据库的交互,对于性能的
提升是非常重要。

4. 开发方面
4.1 开发效率上,我觉得两者应该差不多
4.2 可维护性方面,我觉得 iBatis 更好一些。因为 iBatis 的 sql 都保存到
单独的文件中。而 Hibernate 在有些情况下可能会在 java 代码中保存
sql/hql。

5. 运行效率
5.1 在不考虑 cache 的情况下,iBatis 应该会比hibernate 快一些或者很多
(根据实际情况会有所不同)。

当然 iBatis 也有比较大的缺点
1. 不同数据库类型的支持不好,如果你要开发的系统是要在对中数据间移植,那可能用 hibernate 比较好。
2. 缺省的 cache 支持不好,但是 hibernate 的 cache 支持其实也不是很好,而且很复杂。尤其是对于大并发量的应用。所以我更倾向于自己管理 cache。追问

activiti5 为什么选择 ibatis

追答

因为这些优点啊

追问

谢谢

参考技术A 因为所以,科学原理....追问

谢谢

为啥没有找到处理 Intent 的 Activity?

【中文标题】为啥没有找到处理 Intent 的 Activity?【英文标题】:Why is no Activity found to handle Intent?为什么没有找到处理 Intent 的 Activity? 【发布时间】:2019-06-26 19:26:59 【问题描述】:

我不想采用常规的getPackageManager().getLaunchIntentForPackage("com.example.app") 方式,而是想自己创建启动意图。

Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setPackage("com.example.app");
startActivity(intent);

如果 com.example.app 已安装、启用且清单正确,为什么 Android 找不到 Activity? (它与getLaunchIntentForPackage 完美配合。)

【问题讨论】:

com.example.app 用清单文件中的 package="com.example...." 检查这个。 欢迎访问:***.com/a/30617251/6672577 @Opriday com.example.app 的清单文件是正确的,它包含正确的包名称 (com.example.app)。这与我尝试与intent.setPackage("com.example.app"); 一起使用的包名称相同。没有错字。 @Opriday 我已经访问了您的链接,但在那里找不到任何相关内容。我应该查找哪些信息? +1 。这实际上是一个很好的问题。让我们想知道您的意图和 getLaunchIntentForPackage() 创建的意图之间有什么区别。试试 Log.d(TAG, intent.toString() + " vs " + intent2.toString())。 (我添加了我的解决方法作为答案。) 【参考方案1】:

'要接收隐式意图,您必须在意图过滤器中包含 CATEGORY_DEFAULT 类别。' - 你的接收应用有这个吗?

例子:

<activity android:name="ShareActivity">
     <intent-filter>
         <action android:name="android.intent.action.SEND"/>
         <category android:name="android.intent.category.DEFAULT"/>
         <data android:mimeType="text/plain"/>
     </intent-filter>
</activity>

摘自: https://developer.android.com/guide/components/intents-filters#Receiving

您还可以检查以确保有一个活动可以接收您的广播:

 PackageManager packageManager = getPackageManager();
 List<ResolveInfo> activities = packageManager.queryIntentActivities(intent,PackageManager.MATCH_DEFAULT_ONLY);
 boolean isIntentSafe = activities.size() > 0;

摘自: https://developer.android.com/training/basics/intents/sending#java

【讨论】:

这不是一个隐含的意图。这是明确的,因为包名是使用intent.setPackage("com.example.app") 指定的。 "通过提供目标应用的包名或完全限定的组件类名,显式意图指定哪个应用程序将满足该意图。" - source 接收应用在其LAUNCHER 意图过滤器中没有DEFAULT 类别,只有其他地方。 如果我尝试使用其他具有DEFAULT 类别的接收器应用程序,那将是完美的开始。但是DEFAULT 不应该是必需的,因为我的意图是明确的。如果我可以通过查看接收器应用程序的清单找到正确的活动,为什么 Android 不能为我做呢?它还知道类别、操作和包名称。如果getLaunchIntentForPackage 在没有DEFAULT 的情况下也能完美运行,那么我的方法也应该可以。【参考方案2】:

startActivity所有意图视为声明CATEGORY_DEFAULT

即使您的代码中没有intent.addCategory(Intent.CATEGORY_DEFAULT);

即使你加了intent.removeCategory(Intent.CATEGORY_DEFAULT);

即使您的意图很明确*:intent.setPackage("com.example.app");. * 提供“目标应用的包名或完全限定的组件类名”。

...除非没有

如果你设置了目标activity的类名,系统将不会去寻找CATEGORY_DEFAULT

Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setClassName("com.example.app", "com.example.app.NameOfTheActivityToBeStarted");
startActivity(intent);

标题来源:the <category> element's page 上的蓝色注释。 显式意图的定义来源:developer.android.com。

【讨论】:

即使intent没有action或category,系统也会找到activity,比如:Intent intent = new Intent(); intent.setClassName("com.example.app", "com.example.app.NameOfTheActivityToBeStarted"); startActivity(intent);【参考方案3】:

我了解到您正在尝试启动具有已知包名称 (com.example.app) 的已知应用程序的启动器活动。我假设您有有关该应用程序的信息。因此,您可以像这样通过显式意图启动它:

Intent intent = new Intent();
intent.setComponent(new ComponentName("com.example.app", "com.example.app.MainActivity"));
if(intent.resolveActivity(getPackageManager()) != null) 
    startActivity(intent);

编辑:对两个意图对象进行分析(intent1 == 你自己的意图 VS intent2 == 从getLaunchIntentForPackage() 创建的意图),区别是

意图1:

act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] pkg=com.example.app

意图2:

act=android.intent.action.MAIN 猫=[android.intent.category.LAUNCHER] flg=0x10000000 pkg=com.example.app cmp=com.example.app/.MainActivity

我将不得不相信,您为创建自己的意图对象所做的一切不足以使显式意图起作用。您必须向 Android 提供有关您的意图的更多信息,例如具体的组件名称(如我上面的回答所示)。

【讨论】:

+1 为您提供帮助,您了解 我的示例代码的目标resolveActivity 将我引导至其文档,其中包含有用的详细信息。但您的回答不是关于我的问题的目标:更深入地了解为什么找不到活动。 My own answer 已经有了使用setClassName 的解决方法(与您的setComponent 相同,只是更方便)。您的评论中有一个好主意(比较意图),但您的回答目前不包含新的细节,只是我回答的已知信息的一个子集。 (顺便说一句,你上面有一条评论starting with“我考虑过使用intent.setComponent(...),但是”,这与这个答案。) 谢谢。我确实认识到这不是您目标的答案。我现在没有足够的时间,所以我把我脑子里想的都给了你。我会在今天晚些时候回复你。干杯! 我更新了我的答案。查看由 getLaunchIntentForPackage() 创建的显式意图 VS 意图,看起来您缺少工作意图的值。应使用 setClassName() 或 setComponent() 来完成有关您的意图的信息。 有助于查看差异:0x10000000 标志 (Intent.FLAG_ACTIVITY_NEW_TASK) 和组件名称。这证明系统应该在这两种情况下都找到正确的活动。但事实并非如此,所以问题仍然存在:为什么。活动存在,即使是孩子也能找到(知道动作、类别和包)。系统是否在寻找我告诉它寻找的东西?我们已经有了答案:不,it's looking for the default category too。但目前还不清楚为什么。如果有明确的意图,为什么要查找默认类别?【参考方案4】:

这是将 android.content.Intent#CATEGORY_DEFAULT 添加到所有 startActivity 代码的函数。

ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags) 
        try 
            return AppGlobals.getPackageManager().resolveIntent(intent, resolvedType,
                    PackageManager.MATCH_DEFAULT_ONLY | flags
                    | ActivityManagerService.STOCK_PM_FLAGS, userId);
         catch (RemoteException e) 
        
        return null;
    

/**
     * Resolution and querying flag: if set, only filters that support the
     * @link android.content.Intent#CATEGORY_DEFAULT will be considered for
     * matching.  This is a synonym for including the CATEGORY_DEFAULT in your
     * supplied Intent.
     */
    public static final int MATCH_DEFAULT_ONLY  = 0x00010000;

这是一切开始的代码 http://androidxref.com/7.1.2_r36/xref/frameworks/base/core/java/android/app/ContextImpl.java#766

【讨论】:

+1。很高兴看到代码(在调用startActivity 之后运行并且)在组件不为空时忽略标志(使setComponentsetClassName 方法工作而不是不足的setPackage 方法)。我想这与described here 的逻辑相同,但我不确定也找不到代码。我们现在非常接近在代码级别上完全了解平台的行为。【参考方案5】:

您要求查看在startActivity 之后执行的代码,就在这里。

在您的应用中:Activity.startActivity(Intent) 调用Activity.startActivity(Intent, Bundle),调用Activity.startActivityForResult(Intent, int),调用FragmentActivity.startActivityForResult(Intent, int),调用Activity.startActivityForResult(Intent, int),调用Activity.startActivityForResult(Intent, int, Bundle),调用Instrumentation.execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle),调用 IActivityManager.startActivity(IApplicationThread, String, Intent, String, IBinder, String, int, int, ProfilerInfo, Bundle)

最后一行的调用是远程进程调用,这意味着在您的应用进程中,该方法在代理 IActivityManager 实例上调用,该实例将其转发到另一个进程,在本例中为系统进程。

到目前为止,还没有进行 Intent 过滤。

在Android的系统进程中IActivityManager解析为ActivityManagerService并且:

ActivityManagerService.startivity(IApplicationThread, String, Intent, String, IBinder, String, int, int, ProfilerInfo, Bundle) 调用ActivityManagerService.startActivityAsUser(IApplicationThread, String, Intent, String, IBinder, String, int, int, ProfilerInfo, Bundle, int),调用ActivityStackSupervisor.startActivityMayWait(IApplicationThread, int, String, Intent, String, IVoiceInteractionSession, IVoiceInteractor, IBinder, String, int, int, ProfilerInfo, WaitResult, Configuration, Bundle, boolean, int, IActivityContainer, TaskRecord),调用ActivityStackSupervisor.resolveActivity(Intent, String, int, ProfilerInfo, int),调用IPackageManager.resolveIntent(Intent, String, int, int)

这是添加 MATCH_DEFAULT_ONLY 的地方,正如 nkalra0123 所说。

另外,这是另一个远程方法调用。 IPackageManager 被解析为 PackageManagerService,然后从那里变成这样:

PackageManagerService.resolveIntent(Intent, String, int, int) 调用PackageManagerService.queryIntentActivities(Intent, String, int, int),它尝试获取 Intent 包的所有活动。这会从你的包中获取 Activity,然后调用PackageService.ActivityIntentResolver.queryIntentForPackage(Intent, String, int, ArrayList&lt;PackageParser.Activity&gt;, int),它会获取你的包中的 IntentFilters,然后调用PackageService.ActivityIntentResolver.queryIntentFromList(Intent, String, boolean , ArrayList&lt;F[]&gt;, int),它调用IntentResolver.buildResolveList(...),它针对您的 Intent 中的数据运行它找到的所有 IntentFilter,考虑我们是否需要 CATEGORY_DEFAULT,并相应地将匹配的 IntentFilter 添加到列表中。

所有这些调用方法调用然后返回,最终某个对象会发现没有匹配的 IntentFilters。我在这里省略了,因为这是答案的相关部分。

【讨论】:

【参考方案6】:

您需要为所需的应用程序创建一个组件名称,例如:

Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setComponent(ComponentName.createRelative("com.whatsapp",".Main"));
intent.setPackage("com.whatsapp");

组件名代表你需要打开的activity,完整的包名,第二个参数是该包的类名。

【讨论】:

【参考方案7】:

让我在此添加一些其他信息。如here 所述,没有组件名称,意图是隐含的。

组件名称是可选的,但它是关键信息 这使意图明确,这意味着意图应该是 仅交付给组件名称定义的应用组件。 没有组件名称,意图是隐含的,系统 决定哪个组件应该根据另一个接收意图 意图信息(例如动作、数据和类别——描述 以下)。如果你需要在你的应用中启动一个特定的组件,你 应指定组件名称。

Context.startActivity 方法需要 DEFAULT 类别,以在未明确指定其组件名称时解析您的活动。查看this link 中的第一个示例。

希望这会有所帮助。

【讨论】:

以上是关于activiti5 为啥选择 ibatis的主要内容,如果未能解决你的问题,请参考以下文章

activiti5 如何设置流程变量

Activiti5 获取当前流程实例运行图

Activiti5 添加/查询审批批注(审批意见)

Activiti5 输出指定流程模型的xml

SSM + Activiti5 简单OA系统

BPMN2新规范与Activiti5