Spring事务管理的三个核心接口
Spring的事务管理是基于AOP实现的,而AOP是以方法为单位的。
Spring的事务属性分别为传播行为、隔离级别、只读和超时属性。所有这些属性提供了事务应用的方法和描述策略。
事务管理的三个核心接口:PlatformTransactionManager、TransactionDefinition、TransactionStatus。
1. PlatformTransactionManager
PlatformTransactionManager接口是Spring提供的平台事务管理器,用于管理事务。该接口中提供了三个事务操作方法,具体如下:
(1)TransactionStatus getTransaction(TransactionDefinition definition):用于获取事务状态信息。
(2)void commit(TransactionStatus status):用于提交事务。
(3)void rollback(TransactionStatus status):用于回滚事务。
在项目中通过XML配置事务的详细信息,Spring将这些信息封装到对象TransactionDefinition中,通过事务管理器的getTransaction()方法获得事务的状态TransactionStatus,就可以对事务进行下一步的操作。
2. TransactionDefinition
TransactionDefinition接口是事务定义(描述)对象,提供事务相关信息获取的方法,包括5个操作,具体如下:
(1)String getName():获取事务对象名称。
(2)int getIsolationLevel():获取事务的隔离级别。
(3)int getPropagationBehavior():获取事务的传播行为。
(4)int getTimeout():获取事务的超时时间。
(5)boolean isReadOnly():获取事务是否只读。
需要注意:事务传播行为的概念,事务的传播行为是指在同一个方法中,不同操作前后所使用的事务。
在事务管理过程中,传播行为可以控制是否需要创建事务以及如何创建事务,通常情况下,数据的查询不会影响原数据的改变,所以不需要进行事务管理,而对于数据的增加、修改、删除等操作,必须进行事务管理,如果没有指定事务的传播行为,默认传播行为是required。
传播行为的种类:
属性名称 | 值 | 描述 |
PROPAGATION_REQUIRED | required | 支持当前事务。如果A方法已经在事务中,B将直接使用。如果没有将创建新事务。 |
PROPAGATION_SUPPORTS | supports | 支持当前事务。如果A方法已经在事务中,B将直接使用。如果没有将以非事务状态执行。 |
PROPAGATION_MANDATORY | mandatory | 支持当前事务。如果A方法没有事务,将抛异常。 |
PROPAGATION_REQUIRES_NEW | requires_new | 将创建新的事务,如果A方法已经在事务中,将A事务挂起。 |
PROPAGATION_NOT_SUPPORTED | not_supported | 不支持当前事务,总是以非事务状态执行。如果A方法已经在事务中,将挂起。 |
PROPAGATION_NEVER | never | 不支持当前事务,如果A方法在事务中,将抛异常。 |
PROPAGATION_NESTED | nested | 嵌套事务,底层将使用Savepoint形成嵌套事务。 |
3. TransactionStatus
TransactionStatus接口是事务的状态,描述了某一时间点上事务的状态信息,包含6个操作,具体如下:
(1)void flush():刷新事务。
(2)boolean hasSavepoint():获取是否存在保存点。
(3)boolean isCompleted():获取事务是否完成。
(4)boolean isNewTransaction():获取是否是新事务。
(5)boolean isRollbackOnly():获取是否回滚。
(6)void setRollbackOnly():设置事务回滚。
TransactionProxyFactoryBean实现声明式事务管理
Spring的事务管理分为两种方式,声明式事务管理和编程式事务管理。
编程式事务管理
使用事务模板TransactionTemplate手动地管理事务,在实际开发中一般不使用,这里了解即可。
声明式事务管理
是Spring最原始的事务管理方式,我们需要在配置文件中定义数据源和事务管理器,然后把事务管理器注入到TransactionProxyFactoryBean中,设置目标类和事务的相关属性,使用TransactionProxyFactoryBean生成代理,它的优势在于代码中无须关注事务逻辑,而是交给Spring容器进行事务控制。
数据库:表名:acount 字段:id name money 存两组值:1,jack,1000/2,rose,1000。
c3p0-db.properties
jdbc.driverClass=com.mysql.jdbc.Driver jdbc.jdbcUrl=jdbc:mysql:///spring jdbc.user=root jdbc.password=root
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" 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/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"> <!--加载properties文件 --> <context:property-placeholder location="classpath:c3p0-db.properties"/> <!--配置数据源,读取properties文件信息 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!--配置JDBC模板 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="jdbcTemplate" ref="dataSource"></property> </bean> <!--配置dao --> <bean id="accountDao" class="cn.tm.dao.impl.AccountDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean> <!--配置service --> <bean id="accountService" class="cn.tm.dao.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> </bean> <!--事务管理器,依赖于数据源 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!--生成代理类,让代理管理事务,依赖于事务管理器 --> <bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!--提供事务管理器 --> <property name="transactionManager" ref="transactionManager"></property> <!--目标类 --> <property name="target" ref="accountService"></property> <!--提供接口 --> <property name="proxyInterfaces" value="cn.tm.service.AccountService"></property> <!--事务的详情配置,给TransactionDefinition进行赋值 --> <property name="transactionAttributes"> <props> <!--key属性用来配置对目标内的哪些方法进行增强,*代表所有方法,如果是save*,表示以save开头的方法 --> <!--text文本按照固定的格式编写事务的详情及TransactionDefinition的内容,例如传播行为、隔离界别等,值之间用逗号隔开 --> <prop key="*">PROPAGATION_REQUIRED,ISOLATION_REPEATABLE_READ</prop> </props> </property> </bean> </beans>
AcountDao.java+AccountDaoImpl.java
package cn.tm.dao; public interface AccountDao{ //汇款 public void out(String outUser,int money); //收款 public void in(String inUser,int money); } package cn.tm.dao.impl; public class AccountDaoImpl implements AccountDao{ private JdbcTemplate jdbcTemplate; public void setJdbcTemplate(JdbcTemplate jdbcTemplate){ this.jdbcTemplate = jdbcTemplate; } //汇款的实现方法 public void out(String outUser, int money) { this.jdbcTemplate.update("update account set money = money-?"+"where name=?",money,outUser); } //收款的实现方法 public void in(String inUser, int money) { this.jdbcTemplate.update("update account set money = money+?"+"where name=?",money,inUser); } }
AccountService.java+AccountServiceImpl.java
package cn.tm.service; public interface AccountService{ //转账方法 public void transfer(String outUser,String inUser,int money); } package cn.tm.service.impl; public class AccountServiceImpl implements AccountService{ private AccountDao accountDao; public void setAccountDao(AccountDao accountDao){ this.accountDao = accountDao; } @Override public void transfer(String outUser, String inUser, int money) { this.accountDao.out(outUser,money); this.accountDao.in(inUser,money); } }
测试类
public class Test{ public static void main(String[] args) { //xmlPath为applicationContext.xml文件的路径 ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); AccountService accountService = (AccountService)applicationContext.getBean("accountServiceProxy"); //从jack的账户转100到rose的账户上 accountService.transfer("jack","rose",100); System.out.println("ok"); } }
效果:
jack剩余900元,rose剩余1100元。如果转账的代码有问题,比如出现1/0这种情况,则两人钱仍然是1000元。
Spring AOP XML方式
上面的TransactionProxyFactoryBean实现声明式事务管理缺点是配置文件过于臃肿、难以阅读。因此,Spring提供了基于tx/AOP配置的声明式事务管理方式,也是实际开发中最常用的一种方式。其他代码同上,只修改了applicationContext.xml和测试类的代码。
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" 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" xmlns:tx="http://www.springframework.org/schema/cache" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" http://www.springframework.org/schema/tx http://www.springframework.org/schema/aop/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"> <!--加载properties文件 --> <context:property-placeholder location="classpath:c3p0-db.properties"/> <!--配置数据源,读取properties文件信息 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!--配置JDBC模板 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="jdbcTemplate" ref="dataSource"></property> </bean> <!--配置dao --> <bean id="accountDao" class="cn.tm.dao.impl.AccountDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean> <!--配置service --> <bean id="accountService" class="cn.tm.dao.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> </bean> <!--事务管理器,依赖于数据源 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!--编写通知:对事物进行增强(通知),需要编写对切入点和具体执行事务细节 --> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <!-- tx:method给切入点方法添加事务详情 name:方法名称,*表示任意方法名称,save* 即是以save开头 --> <!-- propagation:设置传播行为 isolation:隔离级别 read-only:是否只读 --> <tx:method name="*" propagation="REQUIRED" isolation="DEFAULT" read-only="false"/> </tx:attributes> </tx:advice> <!--aop编写,让Spring自动对目标生成代理,需要使用AspectJ的表达式 --> <aop:config> <!--切入点 --> <aop:pointcut expression="execution(* cn.tm.service.*.*(..))" id="txPointCut"/> <!--切面:将切入点与通知整合 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> </aop:config> </beans>
测试类:(getBean内的内容做了修改)
public class Test{ public static void main(String[] args) { //xmlPath为applicationContext.xml文件的路径 ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); AccountService accountService = (AccountService)applicationContext.getBean("accountService"); //从jack的账户转100到rose的账户上 accountService.transfer("jack","rose",100); System.out.println("ok"); } }
Spring AOP Annotation方式
Spring的声明式事务管理还可以通过Annotation注解的方式,这种方式很简单。我们需要做的只有两步:Spring容器中注册驱动,在需要使用事务的业务类或者方法上添加注解@Transactional。
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" 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" xmlns:tx="http://www.springframework.org/schema/cache" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/cache" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" http://www.springframework.org/schema/tx http://www.springframework.org/schema/aop/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"> <!--加载properties文件 --> <context:property-placeholder location="classpath:c3p0-db.properties"/> <!--配置数据源,读取properties文件信息 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!--配置JDBC模板 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="jdbcTemplate" ref="dataSource"></property> </bean> <!--配置dao --> <bean id="accountDao" class="cn.tm.dao.impl.AccountDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean> <!--配置service --> <bean id="accountService" class="cn.tm.dao.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> </bean> <!--事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!--注册事务管理驱动 --> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven> </beans>
AccountServiceImpl.java
package cn.tm.service.impl; /*这里添加Transactional注解,并且使用注解的参数配置了事务详情,参数之间用逗号进行分隔*/ @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,readOnly = false) public class AccountServiceImpl implements AccountService{ private AccountDao accountDao; public void setAccountDao(AccountDao accountDao){ this.accountDao = accountDao; } @Override public void transfer(String outUser, String inUser, int money) { this.accountDao.out(outUser,money); this.accountDao.in(inUser,money); } }
参考:
1. 《SSH框架整合实战教程》
持续更新!!!
原文地址:https://www.cnblogs.com/flyinghome/p/12628525.html