Spring事务实践
程序员文章站
2022-05-03 08:18:05
...
大家都知晓的Spring的事物是基于动态机制的,支持CGLIB和JDK动态代理两种。如下所示:
一.CGLIB方式
CGLIB代理无须必须实现接口。
package com.bijian.study.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.bijian.study.dao.IUserMapper; import com.bijian.study.model.User; @Service("userServiceAOP") public class UserServiceAOP { private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class); @Autowired private IUserMapper mapper; @Transactional public int txUpdateUsersWhenException() { // 事务性 return innerMethod(); } private int innerMethod() { User user = new User(1, "Before exception"); int affectedCount = mapper.updateUser(user); // 因后面的异常而回滚 log.info("{} new record was inserted successfully whose id: {}", affectedCount, user.getId()); User user2 = new User(1, "After exception"); int i = 1 / 0; // 抛出运行时异常,事务回滚 int affectedCount2 = mapper.updateUser(user2); // 未执行 log.info("{} new record was inserted successfully whose id: {}", affectedCount2, user.getId()); if (affectedCount == 1 && affectedCount2 == 1) { return 1; } return 0; } }
调试发现确实是CglibAop代理,此时事务是生效的。
二.JDK动态代理
package com.bijian.study.service; public interface UserService { public int txUpdateUsersWhenException(); }
package com.bijian.study.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.bijian.study.dao.IUserMapper; import com.bijian.study.model.User; @Service("userService") public class UserServiceImpl implements UserService { private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class); @Autowired private IUserMapper mapper; @Transactional public int txUpdateUsersWhenException() { // 事务性 return innserMethod(); } private int innserMethod() { User user = new User(1, "Before exception"); int affectedCount = mapper.updateUser(user); // 因后面的异常而回滚 log.info("{} new record was inserted successfully whose id: {}", affectedCount, user.getId()); User user2 = new User(1, "After exception"); int i = 1 / 0; // 抛出运行时异常,事务回滚 int affectedCount2 = mapper.updateUser(user2); // 未执行 log.info("{} new record was inserted successfully whose id: {}", affectedCount2, user.getId()); if (affectedCount == 1 && affectedCount2 == 1) { return 1; } return 0; } }
调试发现确实是JDK动态代理,此时事务是生效的。
三.测试代码
package com.bijian.study.test; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.bijian.study.service.UserService; import com.bijian.study.service.UserServiceAOP; public class SpringIntegrationTest { private static ApplicationContext ctx; static { ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); } @Test public void txUpdateUsersExceptionTest() { UserService userService = (UserService) ctx.getBean("userService"); userService.txUpdateUsersWhenException(); } @Test public void txUpdateUsersExceptionAOPTest() { UserServiceAOP userServiceAOP = (UserServiceAOP) ctx.getBean("userServiceAOP"); userServiceAOP.txUpdateUsersWhenException(); } }
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 数据库配置文件 --> <context:property-placeholder location="classpath:/database.properties" /> <!-- 数据源配置 --> <!--本示例采用DBCP连接池,应预先把DBCP的jar包复制到工程的lib目录下。 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" p:driverClassName="${driverClassName}" p:url="${url}" p:username="${user_name}" p:password="${password}" /> <!-- sqlSessionFactory对象 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!--dataSource属性指定要用到的连接池 --> <property name="dataSource" ref="dataSource" /> <!--configLocation属性指定mybatis的核心配置文件 --> <property name="configLocation" value="classpath:Configuration.xml" /> <!-- 可以在Configuration.xml或此处配置映射文件,但其中不能有相同id的parameterMap, resultMap或sql等 --> <property name="mapperLocations" value="classpath*:com/bijian/study/model/*.xml" /> </bean> <!-- 扫描指定包以获取映射器 --> <bean id="mapperConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.bijian.study.dao" /> </bean> <!-- 事务管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 事务注解驱动,标注@Transactional的类和方法将具有事务性 --> <tx:annotation-driven transaction-manager="txManager" /> <bean id="userService" class="com.bijian.study.service.UserServiceImpl" /> <bean id="userServiceAOP" class="com.bijian.study.service.UserServiceAOP" /> <bean id="txTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <constructor-arg type="org.springframework.transaction.PlatformTransactionManager" ref="txManager" /> </bean> </beans>
database.properties
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://192.168.235.1:3306/hbatis?characterEncoding=utf8 user_name=bijian password=123456
四.将@Transactional放到非外部直接调用的方法上
将@Transactional放到非外部直接调用的方法上,调试发现无代理,事务失效。
1.修改CGLIB方式的服务方法
package com.bijian.study.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.bijian.study.dao.IUserMapper; import com.bijian.study.model.User; @Service("userServiceAOP") public class UserServiceAOP { private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class); @Autowired private IUserMapper mapper; public int txUpdateUsersWhenException() { // 事务性 return innerMethod(); } @Transactional private int innerMethod() { User user = new User(1, "Before exception"); int affectedCount = mapper.updateUser(user); // 因后面的异常而回滚 log.info("{} new record was inserted successfully whose id: {}", affectedCount, user.getId()); User user2 = new User(1, "After exception"); int i = 1 / 0; // 抛出运行时异常,事务回滚 int affectedCount2 = mapper.updateUser(user2); // 未执行 log.info("{} new record was inserted successfully whose id: {}", affectedCount2, user.getId()); if (affectedCount == 1 && affectedCount2 == 1) { return 1; } return 0; } }
@Transactional方法不是服务类的入口方法,无CGLIB代理,事务失效。
2.修改JDK动态代理方式的服务方法
package com.bijian.study.service; public interface UserService { public int txUpdateUsersWhenException(); }
package com.bijian.study.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.bijian.study.dao.IUserMapper; import com.bijian.study.model.User; @Service("userService") public class UserServiceImpl implements UserService { private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class); @Autowired private IUserMapper mapper; public int txUpdateUsersWhenException() { // 事务性 return innserMethod(); } @Transactional private int innserMethod() { User user = new User(1, "Before exception"); int affectedCount = mapper.updateUser(user); // 因后面的异常而回滚 log.info("{} new record was inserted successfully whose id: {}", affectedCount, user.getId()); User user2 = new User(1, "After exception"); int i = 1 / 0; // 抛出运行时异常,事务回滚 int affectedCount2 = mapper.updateUser(user2); // 未执行 log.info("{} new record was inserted successfully whose id: {}", affectedCount2, user.getId()); if (affectedCount == 1 && affectedCount2 == 1) { return 1; } return 0; } }
@Transactional方法不是服务类的入口方法,无JDK动态代理,事务失效。
测试前数据库中user表数据如下:
mysql> select * from user; +----+--------+------+-------------------+ | id | name | age | address | +----+--------+------+-------------------+ | 1 | bijian | 120 | hangzhou,westlake | +----+--------+------+-------------------+ 1 row in set (0.00 sec)
测试方法的innserMethod中虽然抛出异常,但事务并没有回滚,测试后数据库中user表数据如下:
mysql> select * from user; +----+------+------+------------------+ | id | name | age | address | +----+------+------+------------------+ | 1 | NULL | 0 | Before exception | +----+------+------+------------------+ 1 row in set (0.00 sec)