最近公司项目是用的java的ssm框架,然后需要用到事务管理,数据库是sqlserver,然后遇到一些问题,写个博客记录下。
首先项目需求是在控制器中对若干个数据库进行数据的操作,然后当其中某一个操作发生异常,则进行事务回滚,并且其中某些操作需要读取前面对数据库增加的数据,所以数据库事务级别需要设置为READ_UNCOMMITTED
因为是第一次接触事务管理,所以按照官方文档示例,在server层采用注解的方式,首先在applicationContext.xml配置文件中添加事务管理支持
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
1
2
3
4
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
然后在server实现类上添加注解
@Override
@Transactional(
propagation=Propagation.REQUIRES_NEW,
isolation=Isolation.READ_UNCOMMITTED,
rollbackForClassName="Exception"
)
public String add(PO_Podetails[] data) {
// ...
}
1
2
3
4
5
6
7
@Override
@Transactional(
propagation=Propagation.REQUIRES_NEW,
isolation=Isolation.READ_UNCOMMITTED,
rollbackForClassName="Exception"
)
public String add(PO_Podetails[] data) {
其中在注解中设置了事务的传播属性,事务隔离级别和回滚
但是运行发现后事务回滚虽然生效了,但是仍然查询不到本次提交的数据,初步判断事务隔离级别没有生效,在网上也并没有找到相应的解决办法,然后想尝试在controller中添加事务,采用编程式事务管理
@Resource
private PlatformTransactionManager transactionManager;
@ResponseBody
@RequestMapping("/submitPO_Podetails")
public String submitPO_Podetails(@RequestBody PO_Podetails[] data) {
DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = transactionManager.getTransaction(defaultTransactionDefinition);
try{
//这里是对数据库的一系列操作
//没有异常便手动提交事务
transactionManager.commit(status);
printJson(response,result(200,"ok"));
}catch (Exception e) {
//有异常便回滚事务
transactionManager.rollback(status);
e.printStackTrace();
printJson(response,result(500,"false"));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Resource
private PlatformTransactionManager transactionManager;
@ResponseBody
@RequestMapping("/submitPO_Podetails")
public String submitPO_Podetails(@RequestBody PO_Podetails[] data) {
DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = transactionManager.getTransaction(defaultTransactionDefinition);
try{
//这里是对数据库的一系列操作
//没有异常便手动提交事务
transactionManager.commit(status);
printJson(response,result(200,"ok"));
}catch (Exception e) {
//有异常便回滚事务
transactionManager.rollback(status);
e.printStackTrace();
printJson(response,result(500,"false"));
}
然而结果还是跟前面的一样,还是读取不带脏数据,然后在控制台一条一条查看sql执行语句,发现一个奇怪的问题,同一条语句如果执行第二次则不会再执行,会调用第一次查到的数据,上网看到了相同问题,有解决方法,
使用spring @Transaction事务时,在for循环中需要多次执行同一查询语句,第一次查询出对象后,对对象进行修改后,结果再进行第二次查询的时候,查询返回的数据是自己第一次修改后的数据。因为业务需要每次查询都需要取更改数据库,以后的查询都会根据上一次循环修改后的值进行操作。
解决方法:
在xml文件 select语句添加 flushCache=”true” ,告诉mybatis查询结束后刷新缓存,不记录查询结果到一级缓存中
原因:
mybatis对查询的语句会存在一级缓存中,如果在一个事务中,mybatis对同一个session多次查询同一个sql语句就会去找缓存而不是再去查一次数据库