批量操作-Spring事务处理解决方案
版本号 |
主要作者 |
完成日期 |
说明 |
时间hours |
0.1 |
LevinSoft |
2006-8-10 |
文件基本架构、主题内容 |
3 |
V1.0 |
LevinSoft |
2006-11-8 |
细化步骤 |
2 |
V1.1 |
LevinSoft |
2007-8-10 |
细化步骤、改造结构、结论、删除多余的分析 |
2 |
本文档通过一个Spring事务问题,通过渐进式的分析和解决,最终得出结论。同时,对Hibernate的Session刷新机制、Spring的事务处理机制、批量操作和单条数据的操作的相同和不同点也进行了分析。
主要包括的内容:
1. 介绍
2. 问题描述
3. 问题分析,配合介绍了相关Spring和Hibernate的技术点。
4. 不同的方案
5. 方案的比较
6. 结论、展望、限制
7. 参考资源
对于系统,存在着对大量数据操作需求。对于大量数据的操作,主要包括:批量导入、批量导出、批量删除。
批量的操作,主要涉及到技术主要包括:文件处理、事务管理、批量日志管理、处理和其他系统的接口。这些操作相关配合才能完成。
Spring & Hibernate事务管理,是其他操作的基础。如下图所示:
Spring & Hibernate事务管理 |
文件处理 |
其他接口处理 |
批量日志管理 |
整个的过程是按照一步一步分析和解决的思维方式进行组织的。
1. 问题描述
2. 分析问题,找到解决问题的必须要满足的条件。同时,有相关技术的分析。
3. 尝试的一些解决办法。仅描述了关键的步骤。
4. 修改code和配置文件。
5. 阶段性测试,测试结果分析。
6. 进一步修改
7. 最终成功结果
8. 解决方法评估。
1.每处理一条数据时,都需要调用其他系统的一个webservice接口,才能完成。
对于批量操作,可以设计抽象类,例如:BatchProcessDao,采用Template Method设计批量操作的一些基本步骤。
基本流程:
1. 在控制(Action)层,利用相关的工具类,完成源文件的上传操作。
2. 通过Abastract BatchProcessDao和具体子类配合,处理文件中数据,然后入库。
a) 循环从文件中读取数据,每次读取一行数据。
b) 对这行数据,进行验证。把错误数据保存。这在子类Dao实现。
c) 对正确格式数据,组成PO,
d) 验证PO。在子类完成。
e) 创建PO和插入到DB。
f) 调用其他系统webservice接口。
3. 如果存在不合法的数据。保存错误数据到文件,产生新的错误文件名称,然后上传到Webserver。
4. 记录操作日志。
2006-08-09 23:55:23,048 DEBUG - [BatchProcessDao]
== the errMessage is:批量导入操作的批次号为:310 批处理数据的总行数为:2 成功操作的纪录行数为:0 不成功的记录数为:2
1:6366,0901:Write operations are not allowed in read-only mode (FlushMode.NEVER) - turn your Session into FlushMode.AUTO respectively remove 'readOnly' marker from transaction definition
2:6326,0901:Write operations are not allowed in read-only mode (FlushMode.NEVER) - turn your Session into FlushMode.AUTO respectively remove 'readOnly' marker from transaction definition
2006-08-09 23:55:23,058 DEBUG - [BatchProcessDao]
== end of the saveBatchErrResultToFile()
2006-08-09 23:55:23,058 DEBUG - [BatchProcessDao]
== start of the logBatchResult()
2006-08-09 23:55:23,058 DEBUG - [BaseHibernateDao] 保存Hibernate的PO对象:com.qnuse.usboss.po.CfgBatchOperationLog
2006-08-09 23:55:23,058 WARN - [BaseHibernateDao] Write operations are not allowed in read-only mode (FlushMode.NEVER) - turn your Session into FlushMode.AUTO respectively remove 'readOnly' marker from transaction definition
在Spring的ApplicationContext.xml文件中,transactionManager配置中,有这么一行配置:
a) 表示方法必须是运行在事务中、并且是只读事务。
b) readOnly表示:如果采用hibernate作为持久化机制,声明一个只读事务将使Hibernate的flush模式(FlushMode)为FLUSH_NEVER。这就告诉Hibernate避免和数据库进行不必要的对象同步,将所有的更新延迟到事务的结束。
c) 通过测试验证,在系统启动时,Hibernate Session的刷新模式就是:FLUSH_NEVER。
d) 参考了:《精通Hibernate:Java对象持久化技术详解》177页。对Hibernate的Session FlushMode进行了详细的解释。
详细的细节参见:《精通Hibernate:Java对象持久化技术详解》177页
1. Session在清理缓存时(flush())时,将按照一定的顺序执行Sql。
2. Session在一些默认情况下将清理缓存。
3. Session的setFlushMode可以设定清理缓存的时间点。
4. Session的commit方法会先调用flush(),然后提交事务。提交事务意味着对数据库所做的更新被永久保存下来。
通过上面的分析:
1. 从异常提示来看,如果使得一个PO入库,应该:取掉readOnly模式(FlushMode.NEVER)的方式。 进行修改:
a) 方法一:修改ApplicationContext.xml配置文件,增加两行:
这样就去掉了,readOnly模式(FlushMode.NEVER)。即改为一个为非readOnly的事务。
b) 方法二、在代码级别上,直接的进行设置。它将覆盖applicationContext.xml中的配置。因为代码级别上,可以直接的控制最终的处理方式。
2. 进一步的修改:因为默认设置为:FLUSH_NEVER。告诉Hibernate避免和数据库进行不必要的对象同步,将所有的更新延迟到事务的结束。
a) 尝试的手工刷新内存:然后修改:在BatchProcessDao中的,批量导入中,Create(PO),后添加了:getSession().flush();进行了清理缓存。
在debug模式下,开始调试,处理完一条数据后,没有发现这条合法的数据。
l 原因:整个的操作过程中,都是在一个事务的管理下进行的。根据事务具有原子性原理可知:Hibernate提交事务意味着对数据库所做的更新被永久保存下来。 每处理完文件中的一条正确的数据时,并没有入库,而是当全部处理完所有的文件中数据时,才入库。 这必将造成,调用webservice接口失败。
l 解决思路:这说明每处理完一条数据,必须强制的入库。需要进一步的修改。
l 在BatchProcessDao类中,处理一条数据时,在Code级别上,强制启动一个新事务,来完成单条数据的及时的入库。
可以正常的运行。,处理一条数据时,及时的提交到DB,也保证了调用webservice接口成功。
优点 |
缺点 |
1.不用修改spring的自动生成配置文件的模板机制。
2.修改的代码量很小。 |
在配置文件中,已经声明了对批量操作是有事务的,然后到处理到文件中,每一行的数据时,又启动了一个新的事务。可能存在安全隐患。例如:引起数据的死锁、涉及到事务的回滚。 |
1. 把支持批量操作的service类,在配置文件中配置为:非事务的(no-transactional)的形式。
前后的比较:
图一、支持事务配置
图二、非事务的配置
直接的在BatchProcessDao中的batchImport中增加相关的代码,修改相关的Session的刷新模式为FlushMode.AUTO,
原因是:Spring默认情况下,是FulshMode.NEVER,(可以参见OpenSessionInViewFilter类,下面将进行详细描述),而在读取文件中行数据时,或在记录日志时,需要创建PO入库。在FulshMode.NEVER模式下是不允许的。
因此要在代码级别上,设置Session刷新模式(FlushMode)为FlushMode.AUTO,配置文件中的刷新模式将自动失效。实例代码如图:
MyOpenSessionInViewFilter类是继承来自Spring的OpenSessionInViewFilter类。默认设置下:Session的刷新模式为:FlushMode.NEVER。如果修改刷新模式。需要重载OpenSessionInViewFilter的getSession方法把Session的刷新模式修改为:FlushMode.AUTO,
在系统初始化时,就已经把Session的刷新模式设置为了:FlushMode.AUTO(默认情况下是:FlushMode.NEVER)。然而,我们在spring的配置文件applicationContext.xml对txProxyTemplate的设置为如图:
其中
:
这样设置的本意是:让一些非指定的一些方法或方法集外,都是只读事务。因为现在如果在MyOpenSessionInViewFilter中设置刷新模式为:FlushMode.AUTO,这个文件中这个对方法的一些事务上约束,将变的无效。在Service中所有的函数,都将变为支持FlushMode.AUTO的事务。显然在MyOpenSessionInViewFilter的设置是不合适的。
结论:在 MyOpenSessionInViewFilter修改刷新模式为FlushMode.AUTO不合适。不能这么修改。
优点 |
缺点 |
1.由于配置为非事务的处理机制。这样在批量操作数据时,能够使得在文件中得到的合法数据及时的入库,也能及时的调用其他webservice接口。 2.功能分类比较清晰。把批量操作单独放到一个Service类。扩展性比较好。 |
1. 修改Spring自动生成配置文件的模板,添加进去相关的非事务的service配置。 |
1. Spring启动时,默认把hibernate的刷新模型FlushMode设置为:Never
2. 对于批量操作,对于每一条数据处理,如果调用webservice接口才能完成时,把此操作配置为非事务的。
3. 可以通过spring的代码来启动和关闭一个事务,它将覆盖配置文件中的事务配置。
4. 对于批量操作,步骤性很想,应用Template Method Pattern可以带来很高的生产力。
感谢集体智慧。
1.《精通Hibernate:Java对象持久化技术详解》177页
2.Spring的事务管理