事务是一系列作为一个逻辑单元来执行的操作集合。

它是数据库维护数据一致性的单位,它讲数据库从一个一致状态,转变为新的另外一个一致状态。说的简单一点就是:如果一组处理步骤要么全部成功,要么全部失败,这样就保证了数据始终一致的状态。

维护了数据的完整和可靠性。

ZXeH41.png

事务的4个属性:原子性、一致性、隔离性、持续性。这四个属性通常称为ACID特性。

  • 原子性(atomicity):一个事务是一个不可分割的工作单位,事务中包括的所有操作要么都做,要么都不做。
  • 一致性(consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
  • 隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,
    并发执行的各个事务之间不能互相干扰。
  • 持久性(durability):持续性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

事务的传播特性指处于不同事物的方法在相互调用时,执行期间事务的维护情况。

一般有两种方式:

  • PROPAGATION_REQUIRED :如果存在一个事务,则支持当前事务,如果没有事务则开启
  • PROPAGATION_REQUIRED_NEW :总是开启一个新事务

事务管理分为两种类型,分为编程式事务和声明式事务。

  • 编程式事务
    编程式事务管理可以清楚的定义事务的边界
    可以实现细粒度的事务控制,开发者可以通过程序代码来控制你的事务何时开始,何时结束,类似于7.2节实例
    但是事务的控制与业务代码耦合在一起,不能共用 ,而且开发效率低,不便于维护
  • 声明式事务
    声明式事务可以结合spring AOP的概念,只需要通过配置在指定的方法中加入事务控制.
    如果需要修改或者删除事务,移除配置即可,解除了系统服务(事务)与业务代码的耦合,而且方便管理
    在实际开发中,一般提倡用声明式事务管理

1、事务原本是数据库中的概念,是在dao层。

但是一般情况下,我们会把事务管理提升到我们的业务层(service),这是为了更好的是用事务来管理我们的业务逻辑。

2、spring并不直接管理事务,而是提供了多种事务管理器。

根据不同的数据应用方式,(jdbc(自定义封装的jdbc,jdbctemplate,mybatis)/hibernate)提供了对应的事务管理器:

  • jdbc: DataSourceTransactionManager
  • hibenate: HibernateTranactionManager

所有的事务管理器实现了一个共同的接口:org.springframework.transaction.PlatformTransactionManager

它提供了三个方法:

commit/rollback/getTransaction(TransactionDefinition(用来定义事务的一系列属性))

  • 基于xml配置
  • 注解
  • aspectJ

下面先建表、写Dao层、Service层;本例数据库连接采用c3p0的方式(使用jdbc.properties)。

ZXmJrF.png
ZXmGKU.png

1、Dao层接口

  1. package com.steven.spring.sysmgr.dao;
  2. public interface IAccountDao {
  3. /**
  4. * 往某个账户转入金额
  5. * @param account 转入账户姓名
  6. * @param money 转入金额
  7. * @return
  8. */
  9. public boolean addAccountMoney(String account, double money);
  10. /**
  11. * 在某个账户转出金额
  12. * @param account 转出账户姓名
  13. * @param money 转出金额
  14. * @return
  15. */
  16. public boolean subAccountMoney(String account, double money);
  17. }

2、Dao接口实现类

  1. package com.steven.spring.sysmgr.dao.impl;
  2. import org.springframework.jdbc.core.JdbcTemplate;
  3. import com.steven.spring.sysmgr.dao.IAccountDao;
  4. public class AccountDao implements IAccountDao{
  5. private JdbcTemplate jdbcTemplate;
  6. public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
  7. this.jdbcTemplate = jdbcTemplate;
  8. }
  9. @Override
  10. public boolean addAccountMoney(String account, double money) {
  11. boolean updateFlag = false;
  12. String sql = "UPDATE M_ACCOUNT SET AC_MONEY = AC_MONEY + ? WHERE AC_NAME = ?";
  13. int rows = this.jdbcTemplate.update(sql, money, account);
  14. if(rows > 0) updateFlag = true;
  15. return updateFlag;
  16. }
  17. @Override
  18. public boolean subAccountMoney(String account, double money) {
  19. boolean updateFlag = false;
  20. String sql = "UPDATE M_ACCOUNT SET AC_MONEY = AC_MONEY - ? WHERE AC_NAME = ?";
  21. int rows = this.jdbcTemplate.update(sql, money, account);
  22. if(rows > 0) updateFlag = true;
  23. return updateFlag;
  24. }
  25. }

3、服务类接口

  1. package com.steven.spring.sysmgr.service;
  2. /**
  3. * 对外提供转账服务的接口
  4. * @author chenyang
  5. *
  6. */
  7. public interface IAccountService {
  8. /**
  9. * 从一个账户转账到另外一个账户
  10. * @param fromAccount 转出账户
  11. * @param toAccount 转入账户
  12. * @param money 转账金额
  13. * @return
  14. */
  15. public boolean operateAccount(String fromAccount, String toAccount, double money);
  16. }

4、服务类实现

  1. package com.steven.spring.sysmgr.service.impl;
  2. import com.steven.spring.sysmgr.dao.IAccountDao;
  3. import com.steven.spring.sysmgr.service.IAccountService;
  4. public class AccountService implements IAccountService{
  5. private IAccountDao accountDao;
  6. public void setAccountDao(IAccountDao accountDao) {
  7. this.accountDao = accountDao;
  8. }
  9. @Override
  10. public boolean operateAccount(String fromAccount, String toAccount, double money) {
  11. boolean operateFlag = false;
  12. System.out.println("-------转账开始--------");
  13. //转账一般是先减后加
  14. boolean toFlag = accountDao.subAccountMoney(toAccount, money);
  15. System.out.println("转账金额:" + money);
  16. //模拟故障
  17. int i = 1 / 0;
  18. boolean fromFlag = accountDao.addAccountMoney(fromAccount, money);
  19. if(toFlag && fromFlag) operateFlag = true;
  20. System.out.println("-------转账结束--------");
  21. return operateFlag;
  22. }
  23. }

1、环境搭建:引入jar,引入aop约束

aopalliance-1.0.jar
spring-aop-4.2.1.RELEASE.jar
spring-tx.jar

2、配置事务

1)配置文件applicationContext-SpringXml.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xsi:schemaLocation="
  7. http://www.springframework.org/schema/beans
  8. http://www.springframework.org/schema/beans/spring-beans.xsd
  9. http://www.springframework.org/schema/context
  10. http://www.springframework.org/schema/context/spring-context.xsd
  11. http://www.springframework.org/schema/aop
  12. http://www.springframework.org/schema/aop/spring-aop.xsd">
  13. <!-- 注册服务类,并描述依赖关系 -->
  14. <bean id="accountService" class="com.steven.spring.sysmgr.service.impl.AccountService">
  15. <property name="accountDao" ref="accountDao"/>
  16. </bean>
  17. <!-- 注册DAO类 -->
  18. <bean id="accountDao" class="com.steven.spring.sysmgr.dao.impl.AccountDao">
  19. <property name="jdbcTemplate" ref="jdbcTemplate"/>
  20. </bean>
  21. <!-- 注册springJdbc查询模板,模板依赖于数据源 -->
  22. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  23. <property name="dataSource" ref="dataSource"/>
  24. </bean>
  25. <context:property-placeholder location = "classpath:jdbc.properties"/>
  26. <!-- 注册spring自带的管理数据库连接的数据源 -->
  27. <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
  28. <property name="driverClass" value="${jdbc.driver}"/>
  29. <property name="jdbcUrl" value="${jdbc.url}"/>
  30. <property name="user" value="${jdbc.user}"/>
  31. <property name="password" value="${jdbc.password}"/>
  32. </bean>
  33. <!-- 注册事务管理器 -->
  34. <bean id="txMgr" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  35. <property name="dataSource" ref="dataSource"/>
  36. </bean>
  37. <!-- 注册事务代理 -->
  38. <bean id="transProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
  39. <property name="transactionManager" ref="txMgr"/>
  40. <property name="target" ref="accountService"/>
  41. <property name="transactionAttributes">
  42. <props>
  43. <prop key="oper*">ISOLATION_DEFAULT,PROPAGATION_REQUIRED</prop>
  44. </props>
  45. </property>
  46. </bean>
  47. </beans>

ZXmjGq.png

2)测试

  1. package com.steven.spring.sysmgr.test;
  2. import org.junit.Before;
  3. import org.junit.Test;
  4. import org.springframework.context.ApplicationContext;
  5. import org.springframework.context.support.ClassPathXmlApplicationContext;
  6. import com.steven.spring.sysmgr.service.IAccountService;
  7. public class AccountWithTransUsingSpringXml {
  8. private ApplicationContext ac;
  9. @Before
  10. public void init(){
  11. ac = new ClassPathXmlApplicationContext("applicationContext-SpringXml.xml");
  12. }
  13. //模拟增加功能
  14. @Test
  15. public void testOperateAccount(){
  16. //注意这里为代理对象的bean的id
  17. IAccountService accountService = (IAccountService) ac.getBean("transProxy");
  18. accountService.operateAccount("Mike", "Tom", 100);
  19. }
  20. }

3)测试结果
程序报错回滚,账目金额不变,达到目的。

在第7.8.1基础上引入tx约束。

详见 applicationContext-annotation.xml和AccountWithTransAnnoTest.Java。

1)配置文件applicationContext-SpringAnnotation.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xmlns:tx="http://www.springframework.org/schema/tx"
  7. xsi:schemaLocation="
  8. http://www.springframework.org/schema/beans
  9. http://www.springframework.org/schema/beans/spring-beans.xsd
  10. http://www.springframework.org/schema/context
  11. http://www.springframework.org/schema/context/spring-context.xsd
  12. http://www.springframework.org/schema/aop
  13. http://www.springframework.org/schema/aop/spring-aop.xsd
  14. http://www.springframework.org/schema/tx
  15. http://www.springframework.org/schema/tx/spring-tx.xsd">
  16. <!-- 注册服务类,并描述依赖关系 -->
  17. <bean id="accountService" class="com.steven.spring.sysmgr.service.impl.AccountService">
  18. <property name="accountDao" ref="accountDao"/>
  19. </bean>
  20. <!-- 注册DAO类 -->
  21. <bean id="accountDao" class="com.steven.spring.sysmgr.dao.impl.AccountDao">
  22. <property name="jdbcTemplate" ref="jdbcTemplate"/>
  23. </bean>
  24. <!-- 注册springJdbc查询模板,模板依赖于数据源 -->
  25. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  26. <property name="dataSource" ref="dataSource"/>
  27. </bean>
  28. <context:property-placeholder location = "classpath:jdbc.properties"/>
  29. <!-- 注册spring自带的管理数据库连接的数据源 -->
  30. <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
  31. <property name="driverClass" value="${jdbc.driver}"/>
  32. <property name="jdbcUrl" value="${jdbc.url}"/>
  33. <property name="user" value="${jdbc.user}"/>
  34. <property name="password" value="${jdbc.password}"/>
  35. </bean>
  36. <!-- 注册事务管理器 -->
  37. <bean id="txMgr" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  38. <property name="dataSource" ref="dataSource"/>
  39. </bean>
  40. <!-- 开启事务的注解驱动 -->
  41. <tx:annotation-driven transaction-manager="txMgx"/>
  42. </beans>

2)在目标类的目标方法上插入注解

  1. package com.steven.spring.sysmgr.service.impl;
  2. import org.springframework.transaction.annotation.Isolation;
  3. import org.springframework.transaction.annotation.Propagation;
  4. import org.springframework.transaction.annotation.Transactional;
  5. import com.steven.spring.sysmgr.dao.IAccountDao;
  6. import com.steven.spring.sysmgr.service.IAccountService;
  7. public class AccountService implements IAccountService{
  8. private IAccountDao accountDao;
  9. public void setAccountDao(IAccountDao accountDao) {
  10. this.accountDao = accountDao;
  11. }
  12. @Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED)
  13. public boolean operateAccount(String fromAccount, String toAccount, double money) {
  14. boolean operateFlag = false;
  15. System.out.println("-------转账开始--------");
  16. boolean toFlag = accountDao.subAccountMoney(toAccount, money);
  17. System.out.println("转账金额:" + money);
  18. //模拟故障
  19. int i = 1 / 0;
  20. boolean fromFlag = accountDao.addAccountMoney(fromAccount, money);
  21. if(toFlag && fromFlag) operateFlag = true;
  22. System.out.println("-------转账结束--------");
  23. return operateFlag;
  24. }
  25. }

3)测试

  1. package com.steven.spring.sysmgr.test;
  2. import org.junit.Before;
  3. import org.junit.Test;
  4. import org.springframework.context.ApplicationContext;
  5. import org.springframework.context.support.ClassPathXmlApplicationContext;
  6. import com.steven.spring.sysmgr.service.IAccountService;
  7. public class AccountWithTransUsingSpringAnnotation {
  8. private ApplicationContext ac;
  9. @Before
  10. public void init(){
  11. ac = new ClassPathXmlApplicationContext("applicationContext-SpringAnnotation.xml");
  12. }
  13. //模拟增加功能
  14. @Test
  15. public void testOperateAccount(){
  16. //注意这里为目标对象的bean的id,与xml的方式不同
  17. IAccountService accountService = (IAccountService) ac.getBean("accountService");
  18. accountService.operateAccount("Mike", "Tom", 100);
  19. }
  20. }

在7.8.1的基础上引入AspectJ的相关jar包。

1)配置文件applicationContext-AspectJXml.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xmlns:tx="http://www.springframework.org/schema/tx"
  7. xsi:schemaLocation="
  8. http://www.springframework.org/schema/beans
  9. http://www.springframework.org/schema/beans/spring-beans.xsd
  10. http://www.springframework.org/schema/context
  11. http://www.springframework.org/schema/context/spring-context.xsd
  12. http://www.springframework.org/schema/aop
  13. http://www.springframework.org/schema/aop/spring-aop.xsd
  14. http://www.springframework.org/schema/tx
  15. http://www.springframework.org/schema/tx/spring-tx.xsd">
  16. <!-- 注册服务类,并描述依赖关系 -->
  17. <bean id="accountService" class="com.steven.spring.sysmgr.service.impl.AccountService">
  18. <property name="accountDao" ref="accountDao"/>
  19. </bean>
  20. <!-- 注册DAO类 -->
  21. <bean id="accountDao" class="com.steven.spring.sysmgr.dao.impl.AccountDao">
  22. <property name="jdbcTemplate" ref="jdbcTemplate"/>
  23. </bean>
  24. <!-- 注册springJdbc查询模板,模板依赖于数据源 -->
  25. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  26. <property name="dataSource" ref="dataSource"/>
  27. </bean>
  28. <context:property-placeholder location = "classpath:jdbc.properties"/>
  29. <!-- 注册spring自带的管理数据库连接的数据源 -->
  30. <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
  31. <property name="driverClass" value="${jdbc.driver}"/>
  32. <property name="jdbcUrl" value="${jdbc.url}"/>
  33. <property name="user" value="${jdbc.user}"/>
  34. <property name="password" value="${jdbc.password}"/>
  35. </bean>
  36. <!-- 注册事务管理器 -->
  37. <bean id="txMgr" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  38. <property name="dataSource" ref="dataSource"/>
  39. </bean>
  40. <!-- 注册通知-->
  41. <tx:advice id="txAdvice" transaction-manager="txMgr">
  42. <tx:attributes>
  43. <tx:method name="oper*" isolation="DEFAULT" propagation="REQUIRED"/>
  44. </tx:attributes>
  45. </tx:advice>
  46. <!-- 配置aop:切入点表达式,加入通知 -->
  47. <aop:config>
  48. <aop:pointcut expression="execution(* *..AccountService.*(..))" id="accountPointCut"/>
  49. <aop:advisor advice-ref="txAdvice" pointcut-ref="accountPointCut"/>
  50. </aop:config>
  51. </beans>

ps: 上面的切入点表达式可灵活处理。

2)测试

  1. package com.steven.spring.sysmgr.test;
  2. import org.junit.Before;
  3. import org.junit.Test;
  4. import org.springframework.context.ApplicationContext;
  5. import org.springframework.context.support.ClassPathXmlApplicationContext;
  6. import com.steven.spring.sysmgr.service.IAccountService;
  7. public class AccountWithTransUsingAspectJ {
  8. private ApplicationContext ac;
  9. @Before
  10. public void init(){
  11. ac = new ClassPathXmlApplicationContext("applicationContext-AspectJXml.xml");
  12. }
  13. //模拟增加功能
  14. @Test
  15. public void testOperateAccount(){
  16. IAccountService accountService = (IAccountService) ac.getBean("accountService");
  17. accountService.operateAccount("Mike", "Tom", 100);
  18. }
  19. }

版权声明:本文为steven0325原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/steven0325/p/11207570.html