Spring--事务管理
1、通常在java ee编程中,有两种事务管理的方式:全局事务和本地事务。
全局事务:可以同时管理多个transaction,但是通常都需要依赖于容器的事务管理
本地事务:直接使用jdbc的事务管理但是不能同时管理多个事务
2、但是在spring的事务管理中,它解决了全局事务和本地事务的缺点,它是的程序员可以在任何开发环境中都使用一致的编程模式,而且不依赖与任何的事务管理api。spring提供了声明式事务管理(推荐使用)和编程式事务管理。
spring的事务抽象中,依赖的最重要的一个类就是:org.springframework.transaction.PlatformTransactionManager接口
public interfact PlatformTransactionManager{ TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; void commit(TransactionStatus status) throws TransactionException; void rollback(TransactionStatus status) throws TransactionException;p }
这是一个最基本的服务提供接口(SPI)。
getTransaction()方法依赖于一个TransactionDefinition类型的参数,返回一个TransactionStatus类型的对象,返回的TransactionStatus可以是一个新的transaction也有可能是一个已存的transaction。一个TransactionStatus总是和一个线程的执行相关联的。
TransactionDefinition接口指明了以下内容:
Isolation(隔离性):当前事务和其他事务是隔离开的
Propagation(传播):通常在事务范围里面执行的代码都会在这个事务里执行,但是,当一个事务已经存在的时候,你可以选择指定在事务方法被执行的事件中的行为,例如:代码可以在已存的事务中持续执行,或者已存的事务可以被挂起,然后一个创建一个新的事务。
Timeout(持久性):当前事务执行多久结束或者自动回滚
Read-only status(只读):但你的代码只是读操作,并没有对数据做修改的时候,就可以选择使用只读事务,只读事务操作在使用hibernate之类的情况下会是一个最佳的优化。
TransactionStatus接口提供了一种简单的方式去控制事务的执行和查询事务状态
public interface TransactionStatus extends SavepointManager { boolean isNewTransaction(); boolean hasSavepoint(); void setRollbackOnly(); boolean isRollbackOnly(); void flush(); boolean isCompleted(); }
不论使用声明式事务管理还是编程式事务管理,都需要正确的实现PlatformTransactionManager接口,通常都是通过依赖注入来实现。
下面是一种使用最基本的JDBC来实现本地PlatformTransactionManager的例子。
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean>
相关联的PlatformTransactionManager类的定义需要注入一个dataSource
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
当你使用Java EE容器中的JTA的时候,你可以使用容器的DataSource,下面是一个使用JTA和JNDI的例子:
<?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:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd"> <jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/> <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" /> <!-- other <bean/> definitions here --> </beans>
JtaTransactionManager不需要知道DataSource,或者其他特定的资源,因为它使用的是容器的全局事务管理结构。
你也可以更简单使用Hibernate的本地事务管理,但是你需要定义一个hibernate的LocalSessionFactoryBean,用来实例化Hibernate的Session实例。
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mappingResources"> <list> <value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect=${hibernate.dialect} </value> </property> </bean> <bean id="txManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean>
3、事务中的资源同步
①高级同步实现
首选的实现是使用spring的最高级模板。这个模板是基于持久综合APIs或者使用本地的ORM APIs和食物通知工厂类或者代理。通常使用JdbcTemplate来实现。
②低级同步实现
像DataSourceUtils(JDBC用),EntityManagerFactoryUtils(JPA用),SessionFactoryUtils(Hibernate用)PersistenceManagerFactoryUtils(JDO用)等等之类的既存的低级事务管理类。当你想程序代码直接处理本地持久APIs资源类型的时候,你可以使用这些类来保证获得合适的Spring Framework-Managed实例。
例如:在JDBC的时候,使用Spring的org.springframework.jdbc.datasource.DataSourceUtils类来代理传统的JDTC调用getConnection()方法来实现
Connection conn = DataSourceUtils.getConnection(dataSource);
当已经存在一个相关联的同步连接的时候,那么这个连接会被返回,否则的话则会返回一个新的连接。
③TransactionAwareDataSourceProxy
在很底层存在一个TransactionAwareDataSourceProxy类,这是一个DataSource的代理类,对DataSource类实行了一个包装并且加了一个对Spring管理的事务的认识。
通常我们都不需要使用这个类,除了既存的代码必须调用并且传递一个标准的DataSource接口实现。
4、声明式事务管理
下面是一个实现转账的具体实例:
表结构定义:
package com.fuwh.dao; //定义一个出入帐的接口类 public interface MoneyDao { public abstract void inMoney(int money,int id); public abstract void outMoney(int money,int id); }
1 package com.fuwh.dao.impl; 2 3 import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; 4 import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; 5 6 import com.fuwh.dao.MoneyDao; 7 8 //出入帐接口类的具体实现类 9 public class MoneyDaoImpl implements MoneyDao{ 10 11 private NamedParameterJdbcTemplate namedParameterJdbcTemplate; 12 13 public void setNamedParameterJdbcTemplate(NamedParameterJdbcTemplate namedParameterJdbcTemplate) { 14 this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; 15 } 16 17 @Override 18 public void inMoney(int money,int id) { 19 // TODO Auto-generated method stub 20 String sql="update t_money set money=money+:money where id=:id"; 21 MapSqlParameterSource msps=new MapSqlParameterSource("money", money); 22 msps.addValue("id", id); 23 namedParameterJdbcTemplate.update(sql, msps); 24 } 25 26 @Override 27 public void outMoney(int money,int id) { 28 // TODO Auto-generated method stub 29 String sql="update t_money set money=money-:money where id=:id"; 30 MapSqlParameterSource msps=new MapSqlParameterSource("money",money); 31 msps.addValue("id", id); 32 namedParameterJdbcTemplate.update(sql, msps); 33 } 34 35 }
package com.fuwh.Service; //定义一个转账服务类 public interface MoneyService { public void transferMoney(int money,int inId,int outId); }
1 package com.fuwh.Service.impl; 2 3 import com.fuwh.dao.MoneyDao; 4 import com.fuwh.Service.MoneyService; 5 6 //转账服务类的具体实现 7 public class MoneyServiceImpl implements MoneyService { 8 9 private MoneyDao moneyDao; 10 11 public void setMoneyDao(MoneyDao moneyDao) { 12 this.moneyDao = moneyDao; 13 } 14 15 @Override 16 public void transferMoney(int money, int inId, int outId) { 17 // TODO Auto-generated method stub 18 moneyDao.outMoney(money, outId); 19 moneyDao.inMoney(money, inId); 20 } 21 22 }
package com.fuwh.test; import org.junit.Before; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.fuwh.Service.MoneyService; //Junit测试类 public class Test { private ApplicationContext ac; @Before public void setUp() throws Exception { ac=new ClassPathXmlApplicationContext("beans.xml"); } @org.junit.Test public void testTransfer() { MoneyService md=(MoneyService)ac.getBean("moneyService"); md.transferMoney(50, 1, 2); } }
1 <?xml version="1.0" encoding="UTF-8"?> 2 3 <beans xmlns="http://www.springframework.org/schema/beans" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xmlns:context="http://www.springframework.org/schema/context" 7 xmlns:tx="http://www.springframework.org/schema/tx" 8 xsi:schemaLocation="http://www.springframework.org/schema/beans 9 http://www.springframework.org/schema/beans/spring-beans.xsd 10 http://www.springframework.org/schema/aop 11 http://www.springframework.org/schema/aop/spring-aop.xsd 12 http://www.springframework.org/schema/context 13 http://www.springframework.org/schema/context/spring-context.xsd 14 http://www.springframework.org/schema/tx 15 http://www.springframework.org/schema/tx/spring-tx.xsd"> 16 17 <!-- 配置DataSource 18 这里使用的是apache的dbcp连接池 19 需要引入apache的dbcp连接池jarbao 20 commons-dbcp2-2.1.1.jar 和 commons-pool2-2.4.2.jar 21 --> 22 <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"> 23 <property name="driverClassName" value="${jdbc.driverClassName}"/> 24 <property name="url" value="${jdbc.url}"/> 25 <property name="username" value="${jdbc.username}"/> 26 <property name="password" value="${jdbc.password}"/> 27 </bean> 28 29 <context:property-placeholder location="jdbc.properties"/> 30 31 <bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> 32 <constructor-arg ref="dataSource"></constructor-arg> 33 </bean> 34 <bean id="moneyDao" class="com.fuwh.dao.impl.MoneyDaoImpl"> 35 <property name="namedParameterJdbcTemplate" ref="namedParameterJdbcTemplate"></property> 36 </bean> 37 <bean id="moneyService" class="com.fuwh.Service.impl.MoneyServiceImpl"> 38 <property name="moneyDao" ref="moneyDao"></property> 39 </bean> 40 41 <!-- 声明所有的方法都使用默认的事务定义 --> 42 <tx:advice id="txAdvice" transaction-manager="txManager"> 43 <tx:attributes> 44 <tx:method name="*"/> 45 </tx:attributes> 46 </tx:advice> 47 48 <aop:config> 49 <aop:pointcut id="txPoint" expression="execution(* com.fuwh.Service.*.*(..))" /> 50 <aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/> 51 </aop:config> 52 53 <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 54 <property name="dataSource" ref="dataSource"></property> 55 </bean> 56 57 </beans>
#jdbc连接配置属性文件 jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/db_spring jdbc.username=root jdbc.password=mysqladmin
5、编程式事务管理
在spring中提供了两种方式来实现编程式事务管理
|-使用TransactionTemplate(推荐使用)
|-使用PlatformTransaxtionManager实现
具体实现实例:
1 package com.fuwh.Service.impl; 2 3 import com.fuwh.dao.MoneyDao; 4 5 import org.springframework.transaction.TransactionStatus; 6 import org.springframework.transaction.support.TransactionCallbackWithoutResult; 7 import org.springframework.transaction.support.TransactionTemplate; 8 9 import com.fuwh.Service.MoneyService; 10 11 //转账服务类的具体实现 12 public class MoneyServiceImpl implements MoneyService { 13 14 private TransactionTemplate transactionTemplate; 15 16 public void setTransactionTemplate(TransactionTemplate transactionTemplate) { 17 this.transactionTemplate = transactionTemplate; 18 } 19 20 private MoneyDao moneyDao; 21 22 public void setMoneyDao(MoneyDao moneyDao) { 23 this.moneyDao = moneyDao; 24 } 25 26 @Override 27 public void transferMoney(final int money, final int inId, final int outId) { 28 // TODO Auto-generated method stub 29 transactionTemplate.execute(new TransactionCallbackWithoutResult() { 30 31 @Override 32 protected void doInTransactionWithoutResult(TransactionStatus status) { 33 // TODO Auto-generated method stub 34 moneyDao.outMoney(money, outId); 35 moneyDao.inMoney(money, inId); 36 } 37 }); 38 } 39 40 }
<?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" xmlns:tx="http://www.springframework.org/schema/tx" 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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 配置DataSource 这里使用的是apache的dbcp连接池 需要引入apache的dbcp连接池jarbao commons-dbcp2-2.1.1.jar 和 commons-pool2-2.4.2.jar --> <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <context:property-placeholder location="jdbc.properties"/> <bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> <constructor-arg ref="dataSource"></constructor-arg> </bean> <bean id="moneyDao" class="com.fuwh.dao.impl.MoneyDaoImpl"> <property name="namedParameterJdbcTemplate" ref="namedParameterJdbcTemplate"></property> </bean> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="txManager"></property> </bean> <bean id="moneyService" class="com.fuwh.Service.impl.MoneyServiceImpl"> <property name="moneyDao" ref="moneyDao"></property> <property name="transactionTemplate" ref="transactionTemplate"></property> </bean> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
6、<tx:advice>配置详解
默认的<tx:advice>是:
传播(propagation):REQUIRED
隔离(Isolation):DEFAULT
事务(Transaction):read/write
持久性(timeout):默认的timeout是底层的事务系统
例如:
<tx:advice id="txAdvice" transaction-manager="txManager" > <tx:attributes> <tx:method name="*" /> </tx:attributes> </tx:advice>
在<tx:method>标签中,可以加入以下的配置来更改默认的设置
属性 | 必须? | 默认 | 描述 |
name | 必须 | 匹配的方法,*:所有的方法,get*:以get开头的方法等等 | |
propagation | 非必须 | REQUIRED | 食物传播行为 |
isolation | 非必须 | DEFAULT | 事务隔离等级 |
timeout | 非必须 | -1 | timeout的时间(秒单位) |
read-only | 非必须 | false | 是否只读 |
rollback-for | 非必须 | rollback的时候执行的方法 | |
no-rollback-for | 非必须 | 没有触发rollback的时候执行的方法 |