目录
2.3、PlatformTransactionManager 事务管理器
1、什么是事务
- 事务:一组业务操作,要么全部成功,要么全部失败
- 四大特性:ACID
- 原子性:一个事务是一个不可分割单位,要么全发生,要么全不发生
- 一致性:事务必须是使数据库从一个一致性状态变到另一个一致性状态
- 隔离性:一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性:持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
- 隔离问题:
- 脏读:一个事务读到另一个事务没有提交的数据
- 不可重复读:一个事务读到另一个事务已提交的数据(update)
- 幻读:一个事务读到另一个事务已经提交的数据(insert)
- 隔离级别:
- read uncommitted:读未提交,存在3个问题
- read committed:读已提交,存在2个问题
- repeatable read:可重复读,存在一个问题
- serializable:串行化,可以解决三个问题
- 事务的操作
ABCD一个事务 Connection conn = null; try{ //1、获得连接 conn = //2、开启事务 conn.setAutoCommit(false); A B C D //3、提交事务 conn.commit(); }catch{ //回滚事务 conn.rollback(); }
- mysql事务操作--Savepoint
- Savepoint:保存点,记录操作的当前位置,之后可以回滚到该位置
需求:AB(必须),CD(可选) Connection conn = null; Savepoint savepoint = null;//保存点:记录操作的当前位置,之后可以回滚到指定的位置(可以回滚一部分) try{ //1、获得连接 conn = //2、开启事务 conn.setAutoCommit(false); A B savepoint = conn.setSavepoint(); C D //3、提交事务 conn.commit(); }catch{ if(savepoint!=null){//AB没有出错,那么savepoint是有值的 //CD异常 //回滚到CD之前 conn.rollback(savepoint); //提交AB conn.commit(); }else{//savepoint为空,说明AB异常 //回滚AB conn.rollback(); } }
2、事务管理介绍
2.1、导入jar包
2.2、三个顶级接口
- PlatformTransactionManager:平台事务管理器,spring要管理事务,必须使用事务管理器。(进行事务配置时,必须配置事务管理器)
- TransactionDefinition:事务定义(事务详情、事务的属性),spring用于确定事务的具体详情的,例如:隔离级别、是否只读、超时时间等等。(之后进行事务配置时,必须配置详情,spring将配置封装到该对象实例)
- TransactionStatus:事务状态,Spring用于记录当前事务的运行状态。例如:是否有保存点,事务是否已经完成等。(spring底层根据状态进行相应的操作。)
2.3、PlatformTransactionManager 事务管理器
- 导入jar包:需要的是平台事务管理器的实现类
- 常见的事务管理器
1、DataSourceTransactionManager:jdbc开发时事务管理器,采用JDBCTemplate
2、 HibernateTransactionManager:hibernate开发时事务管理器,整合hibernate
- 接口:PlatformTransactionManager源码详解
package org.springframework.transaction; public interface PlatformTransactionManager { //1、事务管理器,通过”事务详情“,获得”事务状态“,从而管理事务 TransactionStatus getTransaction(TransactionDefinition var1) throws TransactionException; //2、根据状态提交 void commit(TransactionStatus var1) throws TransactionException; //3、根据状态回滚 void rollback(TransactionStatus var1) throws TransactionException; }
2.4、TrancationStatus 事务状态
- 接口:TransactionStatus源码详解
package org.springframework.transaction; import java.io.Flushable; public interface TransactionStatus extends SavepointManager, Flushable { //1、是否是新的事务 boolean isNewTransaction(); //2、是否有保存点 boolean hasSavepoint(); //3、设置回滚 void setRollbackOnly(); //4、是否回滚 boolean isRollbackOnly(); //5、刷新 void flush(); //6、是否完成 boolean isCompleted(); }
2.5、TransactionDefinition事务详情
- 接口:TransactionDefinition详解
package org.springframework.transaction; public interface TransactionDefinition { //传播行为 int PROPAGATION_REQUIRED = 0;//必须:支持当前事务,A如果有事务,B将使用该事务;A若没有事务,B将创建一个新的事务 int PROPAGATION_SUPPORTS = 1;//支持:支持当前事务,A如果有事务,B将使用该事务;A若没有事务,B将以非事务执行 int PROPAGATION_MANDATORY = 2;//强制:支持当前事务,A如果有事务,B将使用该事务;A若没有事务,B将抛异常 int PROPAGATION_REQUIRES_NEW = 3;//必须新的:必创建一个新的事务,A如果有事务,将A的事务挂起,B创建一个新的事务;A如果没有事务,B直接创建一个新的事务 int PROPAGATION_NOT_SUPPORTED = 4;//不支持:不支持当前事务,总是以非事务执行。如果A有事务,将A的事务挂起,B将以非事务执行;如果A没有事务,B直接以非事务执行 int PROPAGATION_NEVER = 5;//从不:不支持当前事务,如果有的话会抛出异常。如果A有事务,那么B将抛出异常,如果A没有事务,B将以非事务执行 int PROPAGATION_NESTED = 6;//嵌套:A和B底层采用保存点的机制,形成嵌套事务 //隔离级别 int ISOLATION_DEFAULT = -1;默认的,oracle默认为2,mysql默认为4 int ISOLATION_READ_UNCOMMITTED = 1; int ISOLATION_READ_COMMITTED = 2;// int ISOLATION_REPEATABLE_READ = 4;// int ISOLATION_SERIALIZABLE = 8; int TIMEOUT_DEFAULT = -1;//默认的超时时间,使用的是数据库底层的超时时间 //1、传播行为:******* int getPropagationBehavior(); //2、隔离级别 int getIsolationLevel(); //3、获得超时时间 int getTimeout(); //4、是否只读(一般情况下增删改:读写,查询:只读) boolean isReadOnly(); //5、配置事务详情的名称,一般是方法名称。例如:save String getName(); }
传播行为:在两个业务之间,如何来共享事务:
- PROPAGATION_REQUIRED :必须,支持当前事务,A如果有事务,B将使用该事务;A若没有事务,B将创建一个新的事务【默认值】【掌握】
- PROPAGATION_SUPPORTS:支持,支持当前事务,A如果有事务,B将使用该事务;A若没有事务,B将以非事务执行
- PROPAGATION_MANDATORY :强制,支持当前事务,A如果有事务,B将使用该事务;A若没有事务,B将抛异常
- PROPAGATION_REQUIRES_NEW:必须新的,必创建一个新的事务,A如果有事务,将A的事务挂起,B创建一个新的事务;A如果没有事务,B直接创建一个新的事务【掌握】
- PROPAGATION_NOT_SUPPORTED :不支持,不支持当前事务,总是以非事务执行。如果A有事务,将A的事务挂起,B将以非事务执行;如果A没有事务,B直接以非事务执行
- PROPAGATION_NEVER = 5:从不,不支持当前事务,如果有的话会抛出异常。如果A有事务,那么B将抛出异常,如果A没有事务,B将以非事务执行
- PROPAGATION_NESTED :嵌套,A和B底层采用保存点的机制,形成嵌套事务【掌握】
3、案例:转账
3.1、搭建环境
3.1.1创建表
create table account( id int primary key auto_increment, username varchar(50), money int ); insert into account(username,money) values ('jack',1000),('rose',1000);
3.1.2、导入jar包
- 核心:4+1(core、context、expression、bean +loggin)
- aop:4(aop联盟、Spring aop、aspectJ规范、spring aspect)
- 数据库:2(jdbc + tx)
- 驱动:mysql驱动
- 连接池:c3p0
3.1.3、Dao层(接口+实现类)
接口
package tx_dao; public interface AccountDao { public void out(String outer,Integer money);//转账 public void in(String inner,Integer money);//收款 }
实现类:
package tx_dao_impl; import org.springframework.jdbc.core.support.JdbcDaoSupport; import tx_dao.AccountDao; public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { @Override public void out(String outer, Integer money) { super.getJdbcTemplate().update("update account set money = money - ? where username = ?",money,outer); } @Override public void in(String inner, Integer money) { super.getJdbcTemplate().update("update account set money = money + ? where username = ?",money,inner); } }
3.1.4、Service层(接口+实现类)
接口:
package tx_service; public interface AccountService { //转账 public void transfer(String outer,String inner,Integer money); }
实现类:
package tx_service_impl; import tx_dao.AccountDao; import tx_service.AccountService; public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void transfer(String outer, String inner, Integer money) { accountDao.out(outer,money); accountDao.in(inner,money); } }
3.1.5、spring配置
<?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:context="http://www.springframework.org/schema/context" 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/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"> <!--配置datasource--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value = "com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value = "jdbc:mysql://localhost:3306/spring03"></property> <property name="user" value = "root"></property> <property name="password" value = "hmx123456"></property> </bean> <!--配置dao public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { @Override public void out(String outer, Integer money) { super.getJdbcTemplate().update("update account set money = money - ? where username = ?",money,outer); } @Override public void in(String inner, Integer money) { super.getJdbcTemplate().update("update account set money = money + ? where username = ?",money,inner); } } --> <bean id="accountDaoId" class="tx_dao_impl.AccountDaoImpl"> <property name="dataSource" ref = "dataSource"></property> </bean> <!--配置service public class AccountServiceImpl implements AccountService{ private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void transfer(String outer, String inner, Integer money) { accountDao.out(outer,money); accountDao.in(inner,money); } } --> <bean id="accountServiceId" class="tx_service_impl.AccountServiceImpl"> <property name="accountDao" ref = "accountDaoId"></property> </bean> </beans>
3.1.6、测试类
package tx_test; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import tx_service.AccountService; public class TestApp { @Test public void demo01(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); AccountService accountService = applicationContext.getBean("accountServiceId",AccountService.class); accountService.transfer("jack","rose",20); } }
3.2、手动管理事务
- spring底层使用TransactionTemplate事务模板进行操作
- 操作
- service需要获得TransactionTemplate
- spring配置模版,并注入给service
- 模版需要注入事务管理器
- 配置事务管理器
3.2.1、修改service
package tx2_service_impl; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import tx2_dao.AccountDao; import tx2_service.AccountService; public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } //需要spring注入模版 private TransactionTemplate transactionTemplate;//事务模版 public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } @Override public void transfer(String outer, String inner, Integer money) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { accountDao.outer(outer,money); //模拟一个异常 int i = 1/0; accountDao.inner(inner,money); } }); } }
3.2.2、修改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:context="http://www.springframework.org/schema/context" 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/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"> <!--1、配置datasource--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value = "com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value = "jdbc:mysql://localhost:3306/spring03"></property> <property name="user" value = "root"></property> <property name="password" value = "hmx123456"></property> </bean> <!--2、dao--> <bean id="accountDao" class="tx2_dao_impl.AccountDaoImpl"> <property name="dataSource" ref = "dataSource"></property> </bean> <!--3、service--> <bean id="accountService" class="tx2_service_impl.AccountServiceImpl"> <property name="accountDao" ref = "accountDao"></property> <property name="transactionTemplate" ref = "transactionTemplate"></property> </bean> <!--4、创建模版--> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref = "txManager"></property> </bean> <!--5、配置事务管理器:管理器需要事务,事务从Connection获得,连接从连接池DataSource获得--> <bean id="txManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref = "dataSource"></property> </bean> </beans>
3.3、工厂bean生成代理(半自动)
- spring提供 管理事务的代理工厂 bean TransactionProxyFactoryBean
1、getBean()获得代理对象
2、在spring中配置一个代理
3.3.1、Spring的配置
<?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:context="http://www.springframework.org/schema/context" 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/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"> <!--1、配置datasource--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value = "com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value = "jdbc:mysql://localhost:3306/spring03"></property> <property name="user" value = "root"></property> <property name="password" value = "hmx123456"></property> </bean> <!--2、配置dao--> <bean id = "accountDaoId" class="tx3_dao_impl.AccountDaoImpl"> <property name="dataSource" ref = "dataSource"></property> </bean> <!--3、配置service--> <bean id = "accountServiceId" class="tx3_service_impl.AccountServiceImpl"> <property name="accountDao" ref = "accountDaoId"></property> </bean> <!--4、生成代理 1:接口:proxyInterfaces-目标类接口 2:目标类:target-目标类的引用 3:事务管理器:transactionManager 4:事务属性:transactionAttributes prop.key:确定哪些方法使用当前的事务配置 prop.text:用于配置事务详情 格式:PROPAGATION(传播行为)、ISOLATTON(隔离级别)、readOnly(是否只读)、-Excepion(异常回滚:发生异常回滚)、+Exception(异常提交:发生异常仍然要提交) 例如:<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop> 默认的传播行为和默认的隔离级别 <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly</prop>只读 --> <bean id = "proxyAccountServiceId" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="proxyInterfaces" value = "tx3_service.AccountService"></property> <property name="target" ref = "accountServiceId"></property> <property name="transactionManager" ref = "txManager"></property> <property name="transactionAttributes"> <props> <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly</prop> </props> </property> </bean> <!--5、配置事务管理器--> <bean id = "txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
3.3.2、测试类(使用代理对象)
package tx3_test; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import tx3_service.AccountService; public class TestApp { @Test public void demo01(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); AccountService accountService = applicationContext.getBean("proxyAccountServiceId",AccountService.class); accountService.transfer("rose","jack",20); } }
3.4、AOP配置基于XML【掌握】
- 在spring xml 配置 aop自动生成代理,进行事务管理
1、配置管理器
2、配置事务详情
3、配置aop
3.4.1、配置文件
<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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"> <!--1、配置datasource--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value = "com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value = "jdbc:mysql://localhost:3306/spring03"></property> <property name="user" value = "root"></property> <property name="password" value = "hmx123456"></property> </bean> <!--2、配置dao--> <bean id = "accountDaoId" class="tx4_dao_impl.AccountDaoImpl"> <property name="dataSource" ref = "dataSource"></property> </bean> <!--3、配置service--> <bean id = "accountServiceId" class="tx4_service_impl.AccountServiceImpl"> <property name="accountDao" ref = "accountDaoId"></property> </bean> <!--4、事务管理--> <!--4.1 事务管理器 --> <bean id = "txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!--4.2 事务详情:(事务通知):在aop筛选的基础上,对ABC三个确定使用什么样的事务。例如:A和C是读写的,B是只读的 <tx:attributes>:用于配置事务的详情(属性) <tx:method name=""/>:详情的具体配置 propagation 传播行为 isolation:隔离级别 --> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT" /> </tx:attributes> </tx:advice> <!--4.3 AOP编程,目标类有ABCD4个方法(即四个连接点),切入点表达式:确定需要增强的连接点,从而获得切入点:--> <aop:config> <aop:advisor advice-ref="txAdvice" pointcut="execution(* tx4_service..*.*(..))"></aop:advisor> </aop:config> </beans
3.4.2、测试类
package tx4_test; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import tx4_service.AccountService; public class TestApp { @Test public void demo(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); AccountService accountService = (AccountService)applicationContext.getBean("accountServiceId"); accountService.transfer("jack","rose",100); } }
3.5、AOP配置基于注解
- 配置事务管理器,将事务管理器交与Spring
- 在目标类或者目标方法添加注解 @Transactional
3.5.1、Spring的配置
<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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"> <!--1、配置datasource--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value = "com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value = "jdbc:mysql://localhost:3306/spring03"></property> <property name="user" value = "root"></property> <property name="password" value = "hmx123456"></property> </bean> <bean id = "accountDao" class="tx5_dao_impl.AccountDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <bean id = "accountService" class="tx5_service_impl.AccountServiceImpl"> <property name="accountDao" ref = "accountDao"></property> </bean> <!--配置事务管理 1、事务管理器 2、将事务管理器交予spring --> <bean id = "txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref = "dataSource"></property> </bean> <!--将管理器交与spring--> <tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven> </beans>
3.5.2、实现类中添加注解
package tx5_service_impl; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import tx5_dao.AccountDao; import tx5_service.AccountService; //@Transactional:如果给类加注解,那么就是对类,该类中的所有方法都适用 public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override @Transactional(propagation= Propagation.REQUIRED,isolation = Isolation.DEFAULT) public void transfer(String outer, String inner, Integer money) { accountDao.outer(outer,money); accountDao.inner(inner,money); } }