事务一网打尽"/>
Spring事务一网打尽
Spring事务一网打尽
- 什么是事务
- 首先说一个坑
- Spring 中的事务
- 两种用法
- 三大基础设施
- 编程性事务
- TransactionManager 实现编程性事务
- TransactionTemplate 实现编程性事务
- 声明式事务
- XML配置声明式事务
- 注解配置声明式事务
- 注解+XML混合配置声明式事务
什么是事务
这里要额外补充一点:只有保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。也就是说 A、I、D 是手段,C 是目的!
首先说一个坑
在单元测试方法里面不能用事务注解,不然,代码增删改永远不会生效
Spring 中的事务
两种用法
三大基础设施
-
PlatformTransactionManager
PlatformTransactionManager 就像以前学的 JDBC,它就是一个接口,一个规范
public interface PlatformTransactionManager extends TransactionManager {TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;void commit(TransactionStatus status) throws TransactionException;void rollback(TransactionStatus status) throws TransactionException; }
-
TransactionDefinition
TransactionDefinition 主要定义的一些事务的属性,看源码就清楚了
-
TransactionStatus
你可以理解为事务本身,当然也可以说是事务状态
编程性事务
TransactionManager 实现编程性事务
- 导入必须的依赖
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.20</version></dependency><!--jdbc事务相关的代码--><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.30</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.25</version></dependency>
- 全部代码如下
@Service public class UserServie {@Autowiredprivate JdbcTemplate jdbcTemplate;@Autowiredprivate PlatformTransactionManager transactionManager;@Autowiredprivate TransactionTemplate transactionTemplate;public void transfer() {// 定义默认的事务属性DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();// 获取 transactionStatusTransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);try {jdbcTemplate.update("update user set money = ? where username = ?;", 33, "hok");// 提交事务transactionManagermit(transactionStatus);} catch (DataAccessException e) {e.printStackTrace();transactionManager.rollback(transactionStatus);}}
public class App {public static void main(String[] args) {ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");UserServie userServie = ctx.getBean(UserServie.class);userServie.transfer();} }
<?xml version="1.0" encoding="UTF-8"?><beans xmlns=""xmlns:xsi=""xmlns:context=""xmlns:tx=""xmlns:aop=""xsi:schemaLocation=" .xsd .xsd .xsd .xsd"><!--配置包扫描--><context:component-scan base-package="com.lhg.springtx" /><!-- 配置数据源 --><bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource" ><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://120.26.161.184:3306/wxpay" /><property name="username" value="root"/><property name="password" value="root"/></bean><!--配置事务管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"><property name="transactionManager" ref="transactionManager" /></bean><!--JdbcTemplate是 Spring 对 JDBC 的封装,用于操作数据库及事务的--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" ><property name="dataSource" ref="dataSource" /></bean> </beans>
- 运行结果如下,可以看到数据正确修改
- 如果转账过程中出现了问题会怎么样?
如下图可以看到数据库更改并没有生效,事务回滚成功
TransactionTemplate 实现编程性事务
把 transfer 方法改成如下,实地验证会发现有同样的效果@Service public class UserServie {@Autowiredprivate JdbcTemplate jdbcTemplate;@Autowiredprivate PlatformTransactionManager transactionManager;@Autowiredprivate TransactionTemplate transactionTemplate;public void transfer() {transactionTemplate.execute(new TransactionCallbackWithoutResult() {@Overrideprotected void doInTransactionWithoutResult(TransactionStatus status) {try {jdbcTemplate.update("update user set money = ? where username = ?;", 888, "hok");int i = 1/0;} catch (DataAccessException e) {status.setRollbackOnly();throw new RuntimeException(e);}}});} }
从上面可以看到,编程性事务对业务方法侵入性太强了,实际项目开发一般不会去用
声明式事务
XML配置声明式事务
首先在上面的基础上再加个依赖
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.7</version>
</dependency>
applicationContext.xml配置
<?xml version="1.0" encoding="UTF-8"?><beans xmlns=""xmlns:xsi=""xmlns:context=""xmlns:tx=""xmlns:aop=""xsi:schemaLocation=" .xsd .xsd .xsd .xsd"><!--配置包扫描--><context:component-scan base-package="com.lhg.springtx" /><!-- 配置数据源 --><bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource" ><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://120.26.161.184:3306/wxpay" /><property name="username" value="root"/><property name="password" value="root"/></bean><!--配置事务管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"><property name="transactionManager" ref="transactionManager" /></bean><!--JdbcTemplate是 Spring 对 JDBC 的封装,用于操作数据库及事务的--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" ><property name="dataSource" ref="dataSource" /></bean><!-- XML 配置事务分为三个步骤:1、配置事务管理器2、配置事务通知3、配置AOP--><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><!--配置事务的属性:isolation:用于指定事务的隔离级别,默认值是DEFAULT,表示使用数据库的默认隔离级别propagation:用于指定事务的传播行为,默认值是REQUERD,表示一定会有事务,增删改的选择,查询方法可以使用SUPPORTSread-only:用于指定事务是否只读,只有查询方法才能设置为true,默认值是false,表示读写rollback-for:用于指定一个异常,当该异常产生时,事务回滚,产生其它异常时事务不回滚,没有默认值,表示任何异常都回滚no-rollback-for:用于指定一个异常,当该异常产生时事务不回滚,产生其它异常时事务回滚,没有默认值,表示任何异常都回滚--><tx:method name="add*" propagation="REQUIRED" read-only="false"/><!--通用匹配--><tx:method name="find*" propagation="SUPPORTS" read-only="true" /><!--匹配以find开头的方法,优先级更高--><tx:method name="transfer*" /></tx:attributes></tx:advice><aop:config><!--配置切入点表达式--><aop:pointcut id="pt" expression="execution(* com.lhg.springtx.UserServie.*(..))"/><!--建立切入点表达式与事务通知的对应关系--><aop:advisor advice-ref="txAdvice" pointcut-ref="pt" /></aop:config>
</beans>
业务逻辑代码改成如下,实际运行发现已正确修改
@Service
public class UserServie {@Autowiredprivate JdbcTemplate jdbcTemplate;public void transfer() {jdbcTemplate.update("update user set money = ? where username = ?;", 666, "hok");}
}
当然如果执行过程中出异常了,事务是能够正确回滚的,这里就不再截图了
public void transfer() {jdbcTemplate.update("update user set money = ? where username = ?;", 999, "hok");int i = 1/0;
}
注解配置声明式事务
依赖和上面一样,无需更改,然后定义一个 Java配置类
@Configuration
@ComponentScan(basePackages = "com.lhg.springtx")
@EnableTransactionManagement
public class JavaConfig {@Beanpublic DataSource dataSource(){DriverManagerDataSource dataSource = new DriverManagerDataSource();dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");dataSource.setUrl("jdbc:mysql://120.26.161.184:3306/wxpay?serverTimezone=Asia/Shanghai");dataSource.setUsername("root");dataSource.setPassword("root");return dataSource;}@Beanpublic PlatformTransactionManager transactionManager() {return new DataSourceTransactionManager(dataSource());}@Beanpublic JdbcTemplate jdbcTemplate() {return new JdbcTemplate(dataSource());}
}
业务层代码如下,需要在哪个方法上加事务就在哪个方法上加个@Transactional注解
@Service
public class UserServie {@Autowiredprivate JdbcTemplate jdbcTemplate;@Transactionalpublic void transfer() {jdbcTemplate.update("update user set money = ? where username = ?;", 222, "hok");int i = 1/0;}
}
启动执行
public class App {public static void main(String[] args) {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);UserServie userServie = ctx.getBean(UserServie.class);userServie.transfer();}
}
运行后会发现事务依然能够生效,这里就不一一截图了
注解+XML混合配置声明式事务
说白了就是想把这一坨干掉,换成<tx:annotation-driven />
来看看 xml 配置
<?xml version="1.0" encoding="UTF-8"?><beans xmlns=""xmlns:xsi=""xmlns:context=""xmlns:tx=""xmlns:aop=""xsi:schemaLocation=" .xsd .xsd .xsd .xsd"><!--配置包扫描--><context:component-scan base-package="com.lhg.springtx" /><!-- 配置数据源 --><bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource" ><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://120.26.161.184:3306/wxpay" /><property name="username" value="root"/><property name="password" value="root"/></bean><!--配置事务管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"><property name="transactionManager" ref="transactionManager" /></bean><!--JdbcTemplate是 Spring 对 JDBC 的封装,用于操作数据库及事务的--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" ><property name="dataSource" ref="dataSource" /></bean><!--事务注解支持--><tx:annotation-driven />
</beans>
业务代码如下
@Service
public class UserServie {@Autowiredprivate JdbcTemplate jdbcTemplate;@Transactionalpublic void transfer() {jdbcTemplate.update("update user set money = ? where username = ?;", 333, "hok");int i = 1/0;}
}
启动运行
public class App {public static void main(String[] args) {ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");UserServie userServie = ctx.getBean(UserServie.class);userServie.transfer();}
}
当然最终效果还是一样,事务正常
注解+XML混合配置需要根据实际项目需要,不一定是像上面这样…
更多推荐
Spring事务一网打尽
发布评论