SpringMVC+Mybatis声明式事务分析
由于本人最近刚刚开始使用SpringMVC+Mybatis,遇到了一个问题:执行查询http://localhost:8080/zcrm/query
后台日志
DEBUG: - Creating SqlSession with JDBC Connection
DEBUG: - ooo Connection Opened
DEBUG: - JDBC Connection will not be managed by Spring
DEBUG: - SqlSession was not registered for synchronization because synchronization is not active
DEBUG: - ==> Executing: SELECT a.* FROM CRM_USER a WHERE a.username = ? AND a.password = ?
DEBUG: - ==> Parameters: canon(String), canon(String)
DEBUG: - <== Columns: ID, USERNAME, PASSWORD, NICKNAME, CREATE_DATE, STATE, REMARKS
DEBUG: - <== Row: 1, canon, canon, 卡农, 2015-04-28 00:00:00.0, U, null
DEBUG: - {conn-10001, pstmt-20000} enter cache
DEBUG: - Closing no transactional SqlSession
执行查询http://localhost:8080/zcrm/add?id=334455
后台日志
DEBUG: - Creating SqlSession with JDBC Connection
DEBUG: - ooo Connection Opened
DEBUG: - JDBC Connection will not be managed by Spring
DEBUG: - SqlSession was not registered for synchronization because synchronization is not active
DEBUG: - ==> Executing: insert into CRM_USER (ID, USERNAME, PASSWORD, NICKNAME, CREATE_DATE, STATE, REMARKS) values (?, ?, ?, ?, ?, ?, ?)
DEBUG: - ==> Parameters: 334455(Long), null, null, null, null, null, null
DEBUG: - {conn-10001, pstmt-20001} enter cache
DEBUG: - Closing no transactional SqlSession
问题1:数据库中有没有插入数据呢?
数据库中插入了数据,这点我也没有想明白?
危险点(1):context:component-scan设置全局扫描
修改点:
注意web.xml的执行顺序 context-param -> listener -> filter -> servlet
个人认为Spring也恰巧利用了这一点,利用listener先去加载service层的bean,然后为controller加载成bean提供依赖注入。
这也是Spring设计完美的地点
通过【org.springframework.web.context.ContextLoaderListener】扫描对应路径下的@Service等注解的
<context:component-scan base-package="com.zcrm.service" />
通过【org.springframework.web.servlet.DispatcherServlet】执行Controller注入
<!-- controller包自动注入 -->
<context:component-scan base-package="com.zcrm.controller" >
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
危险点(2):拦截器方式配置事务,容易配置错误
(2-1):expression="execution(* *..*SVImpl.*(..))" 的使用
第一个【*】:任意返回值类型
第二个【*】:任意包名
第一个【..】:通配包路径 可以有0个或者多个包路径
第三个【*】:任意字符+SVImpl的class
第四个【*】:任意方法名
第二个【..】:通配方法可以有0个或者多个参数
(2-2):事务的传播级别
1、PROPAGATION_REQUIRED:如果存在一个事务,则支持当前事务。如果没有事务则开启。
2、PROPAGATION_SUPPORTS:如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。
3、PROPAGATION_MANDATORY:如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
4、PROPAGATION_REQUIRES_NEW:总是开启一个新的事务。如果一个事务存在,则将这个存在的事务挂起。
5、PROPAGATION_NOT_SUPPORTED:总是非事务地执行,并挂起任何存在的事务。
6、PROPAGATION_NEVER:总是非事务地执行,如果存在一个活动事务,则抛出异常。
7、 PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中,如果没有活动事务,则按TransactionDefinition.PROPAGATION_REQUIRED属性执行
<!-- 拦截器方式配置事务 -->
<!-- 配置事务传播级别 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="create*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="merge*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="remove*" propagation="REQUIRED" />
<tx:method name="put*" propagation="REQUIRED" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
<tx:method name="count*" propagation="SUPPORTS" read-only="true" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="list*" propagation="SUPPORTS" read-only="true" />
<tx:method name="*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 只对业务逻辑层实施事务 -->
<aop:pointcut id="txPointcut"
expression="execution(* *..*SVImpl.*(..))" />
<!-- Advisor定义,切入点和通知分别为txPointcut、txAdvice -->
<aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice" />
</aop:config>
http://blog.csdn.net/wubai250/article/details/8102194
在这里特别感谢【开涛】:http://jinnianshilongnian.iyunv.com/
修改后的效果:
执行查询http://localhost:8080/zcrm/add?id=334455
后台日志
DEBUG: - Creating SqlSession with JDBC Connection
DEBUG: - ooo Connection Opened
DEBUG: - JDBC Connection will be managed by Spring
DEBUG: - Registering transaction synchronization for SqlSession
DEBUG: - ==> Executing: insert into CRM_USER (ID, USERNAME, PASSWORD, NICKNAME, CREATE_DATE, STATE, REMARKS) values (?, ?, ?, ?, ?, ?, ?)
DEBUG: - ==> Parameters: 2(Long), null, null, null, null, null, null
DEBUG: - {conn-10001, pstmt-20000} enter cache
DEBUG: - Releasing transactional SqlSession
DEBUG: - Transaction synchronization committing SqlSession
DEBUG: - Transaction synchronization closing SqlSession
执行查询http://localhost:8080/zcrm/query
后台日志:关闭SqlSession,但是没有事务的提交
DEBUG: - Creating SqlSession with JDBC Connection
DEBUG: - ooo Connection Opened
DEBUG: - JDBC Connection will be managed by Spring
DEBUG: - Registering transaction synchronization for SqlSession
DEBUG: - ==> Executing: SELECT a.* FROM CRM_USER a WHERE a.username = ? AND a.password = ?
DEBUG: - ==> Parameters: canon(String), canon(String)
DEBUG: - {conn-10001, pstmt-20001} enter cache
DEBUG: - Releasing transactional SqlSession
DEBUG: - Transaction synchronization closing SqlSession
问题2:为什么这个地方还会开启事务?
求大神解决
实践(1):测试事务回滚
@RequestMapping(value={"/test"},method=RequestMethod.GET)
public String test(BOCrmUser BOCrmUser) {
String flag = "error";
try {
IUserSV.deleteByPrimaryKey(1l);//id=1
IUserSV.insert(BOCrmUser);//id=2 2已经存在了,会爆主键重复的异常
//问题:1的数据有没有被删除
flag = "success";
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
结果:1的数据被删除了,因为删除的操作和新增的操作不再一个事务中
日志:
注意SqlSession
新增的时候SqlSession是[org.apache.ibatis.session.defaults.DefaultSqlSession@5751de43]
删除的时候SqlSession是[org.apache.ibatis.session.defaults.DefaultSqlSession@474c8cd6]
注意事务提交
新增的时候,连接打开,事务提交
删除的时候,连接打开,事务没有提交
DEBUG: - Creating SqlSession with JDBC Connection
DEBUG: - ooo Connection Opened
DEBUG: - JDBC Connection will be managed by Spring
DEBUG: - Registering transaction synchronization for SqlSession
DEBUG: - ==> Executing: delete from CRM_USER where ID = ?
DEBUG: - ==> Parameters: 1(Long)
DEBUG: - Releasing transactional SqlSession
DEBUG: - Transaction synchronization committing SqlSession
DEBUG: - Transaction synchronization closing SqlSession
DEBUG: - Creating SqlSession with JDBC Connection
DEBUG: - ooo Connection Opened
DEBUG: - JDBC Connection will be managed by Spring
DEBUG: - Registering transaction synchronization for SqlSession
DEBUG: - ==> Executing: insert into CRM_USER (ID, USERNAME, PASSWORD, NICKNAME, CREATE_DATE, STATE, REMARKS) values (?, ?, ?, ?, ?, ?, ?)
DEBUG: - ==> Parameters: 2(Long), null, null, null, null, null, null
DEBUG: - Releasing transactional SqlSession
DEBUG: - Transaction synchronization closing SqlSession
org.springframework.dao.DuplicateKeyException:
### Error updating database. Cause: java.sql.SQLIntegrityConstraintViolationException: ORA-00001: 违反唯一约束条件 (ZCRM.PK_CRM_USER)
实践(2):将删除(REQUIRED)和新增(REQUIRED)封装到一个新方法update(
REQUIRED)中
public void update(BOCrmUser record){
this.deleteByPrimaryKey(1l);//删除id=1
this.insert(record);//新增id=2,主键冲突
}
@RequestMapping(value={"/update"},method=RequestMethod.GET)
public String update(BOCrmUser BOCrmUser) {
String flag = "error";
try {
IUserSV.update(BOCrmUser);
flag = "success";
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
结果(2):1的数据没有被删除,事务回滚
日志(2)
DEBUG: - Creating SqlSession with JDBC Connection
DEBUG: - ooo Connection Opened
DEBUG: - JDBC Connection will be managed by Spring
DEBUG: - Registering transaction synchronization for SqlSession
DEBUG: - ==> Executing: delete from CRM_USER where ID = ?
DEBUG: - ==> Parameters: 1(Long)
DEBUG: - {conn-10001, pstmt-20000} enter cache
DEBUG: - Releasing transactional SqlSession
DEBUG: - Fetched SqlSession from current transaction
DEBUG: - ==> Executing: insert into CRM_USER (ID, USERNAME, PASSWORD, NICKNAME, CREATE_DATE, STATE, REMARKS) values (?, ?, ?, ?, ?, ?, ?)
DEBUG: - ==> Parameters: 2(Long), null, null, null, null, null, null
DEBUG: - {conn-10001, pstmt-20001} enter cache
INFO : - Loading XML bean definitions from class path resource
INFO : - SQLErrorCodes loaded:
DEBUG: - Releasing transactional SqlSession
DEBUG: - Transaction synchronization closing SqlSession
事务同步关闭SqlSession
org.springframework.dao.DuplicateKeyException:
### Error updating database. Cause: java.sql.SQLIntegrityConstraintViolationException: ORA-00001: 违反唯一约束条件 (ZCRM.PK_CRM_USER)
### The error may involve com.zcrm.dao.BOCrmUserMapper.insert-Inline
### The error occurred while setting parameters
### Cause: java.sql.SQLIntegrityConstraintViolationException: ORA-00001: 违反唯一约束条件 (ZCRM.PK_CRM_USER)
; SQL []; ORA-00001: 违反唯一约束条件 (ZCRM.PK_CRM_USER)
分析:
http://my.oschina.net/realfighter/blog/366089
页:
[1]