Spring学习笔记Day03——spring事务管理
1.什么是事务?
事务:一组业务操作ABCD,要么全部成功,要么全部不成功。
- A(atomicity)原子性:整体
- C(consistency)一致性:完成。事务执行前后涉及到的相关数据保持一致
- I(isolation)隔离性:并发。事务正确提交之前结果不应被其他事务所见
- D(durability)持久性:结果。执行结果得到持久保存
隔离问题:
- 脏读:一个事务读到另一个事务没有提交的数据
- 不可重复读:一个事务读到另一个事务已提交的数据(update)
- 虚读(幻读):一个事务读到另一个事务已提交的数据(insert)
隔离级别:
- read uncommitted:读未提交。存在3个问题
- read committed:读已提交。解决脏读,存在2个问题
- repeatable read:可重复读。解决:脏读、不可重复读,存在1个问题。
- serializable :串行化。都解决,单事务。
2.spring事务管理框架
Spring事务管理框架对JDBC、JTA事务处理进行了更高一层抽象,向开发人员提供了统一的开发接口,面对本地事务和全局事务使用一种编程模型,从而大大降低了开发的复杂度。
2.1 jar包
spring-tx-4.3.15.RELEASE.jar
2.2 三个顶级接口
- PlatformTransactionManager:平台事务管理器,spring要管理事务,必须使用事务管理器进行事务配置时,必须配置事务管理器。
- TransactionDefinition:事务详情(事务定义、事务属性),spring用于确定事务具体详情,例如:隔离级别、是否只读、超时时间 等
进行事务配置时,必须配置详情。spring将配置项封装到该对象实例。 - TransactionStatus:事务状态,spring用于记录当前事务运行状态例如:是否有保存点,事务是否完成。
spring底层根据状态进行相应操作。
2.2.1 PlatformTransactionManager 事务管理器
- 常见的事务管理器
TransactionStatus接口中有以下几种方法
- isNewTransactionStatus()返回boolean类型,判断是否为新的事务
- hasSavepoint()返回boolean类型,判断是否有保存点
- setRollbackOnly()返回值void,设置回滚
- isRollbackOnly()返回boolean类型,是否回滚
- flush(),返回void,刷新
- isCompleted(),返回boolean,判断是否完成
2.2.3 TransactionDefinition
传播行为:在两个业务之间如何共享事务。
- PROPAGATION_REQUIRED , required , 必须 【默认值】
- 支持当前事务,A如果有事务,B将使用该事务。
- 如果A没有事务,B将创建一个新的事务。
- PROPAGATION_SUPPORTS ,supports ,支持
- 支持当前事务,A如果有事务,B将使用该事务。
- 如果A没有事务,B将以非事务执行。
- PROPAGATION_MANDATORY,mandatory ,强制
- 支持当前事务,A如果有事务,B将使用该事务。
- 如果A没有事务,B将抛异常。
- PROPAGATION_REQUIRES_NEW , requires_new ,必须新的
- 如果A有事务,将A的事务挂起,B创建一个新的事务
- 如果A没有事务,B创建一个新的事务
- PROPAGATION_NOT_SUPPORTED ,not_supported ,不支持
- 如果A有事务,将A的事务挂起,B将以非事务执行
- 如果A没有事务,B将以非事务执行
- PROPAGATION_NEVER ,never,从不
- 如果A有事务,B将抛异常
- 如果A没有事务,B将以非事务执行
- PROPAGATION_NESTED ,nested ,嵌套
- A和B底层采用保存点机制,形成嵌套事务。
3.案例:转账
3.1 环境搭建
3.1.1 导入jar包
aspectjrt-1.6.8.jar
com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.apache.commons.logging-1.1.1.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
mysql-connector-java-5.1.21.jar
spring-aop-4.3.15.RELEASE.jar
spring-aspects-4.3.15.RELEASE.jar
spring-beans-4.3.15.RELEASE.jar
spring-context-4.3.15.RELEASE.jar
spring-core-4.3.15.RELEASE.jar
spring-expression-4.3.15.RELEASE.jar
spring-jdbc-4.3.15.RELEASE.jar
spring-tx-4.3.15.RELEASE.jar
3.1.2 创建表
CREATE TABLE account (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR (50),
money INT (10)
);
INSERT INTO account (username, money)
VALUES
('Jack', 10000);
INSERT INTO account (username, money)
VALUES
('Luna', 20000);
3.1.3 编写dao层
dao接口IAccountDao
public interface IAccountDao {
void out(String outer, Integer money);
void in(String inner, Integer money);
}
dao实现类AccountDaoImpl
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
@Override
public void out(String outer, Integer money) {
// TODO Auto-generated method stub
this.getJdbcTemplate().update("update account set money = money - ? where username = ?", money, outer);
}
@Override
public void in(String inner, Integer money) {
// TODO Auto-generated method stub
this.getJdbcTemplate().update("update account set money = money + ? where username = ?", money, inner);
}
}
3.1.4 编写service层
service接口IAccountService
public interface IAccountService {
void transfer(String outer, String inner, Integer money);
}
service实现类AccountServiceImpl
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao;
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String outer, String inner, Integer money) {
// TODO Auto-generated method stub
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">
<!-- 创建c3p0数据源 -->
<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/test"></property>
<property name="user" value="root"></property>
<property name="password" value=""></property>
</bean>
<!-- dao -->
<bean id="accountDao" class="com.spring.tx.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- service -->
<bean id="accountService" class="com.spring.tx.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
</beans>
3.1.6 测试
@Test
public void test() {
String xmlPath = "com/spring/tx/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
IAccountService bean = (IAccountService) applicationContext.getBean("accountService");
bean.transfer("Jack", "Luna", 123);
}
3.2 基于xml配置的aop事务管理
修改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">
<!-- 创建c3p0数据源 -->
<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/test"></property>
<property name="user" value="root"></property>
<property name="password" value=""></property>
</bean>
<!-- dao -->
<bean id="accountDao" class="com.spring.tx.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- service -->
<bean id="accountService" class="com.spring.tx.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 事务管理器 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 事务详情(事务通知),在aop筛选基础上,对ABC三个确定使用什么样的事务。例如:AC读写、B只读 等 <tx:attributes>
用于配置事务详情(属性属性) <tx:method name=""/> 详情具体配置 propagation 传播行为 , REQUIRED:必须;REQUIRES_NEW:必须是新的
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编程,目标类有ABCD(4个连接点),切入点表达式 确定增强的连接器,从而获得切入点:ABC -->
<aop:config>
<aop:advisor advice-ref="txAdvice"
pointcut="execution(* com.spring.tx.*.*(..))" />
</aop:config>
</beans>
3.3 基于注解的aop事务管理
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" 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">
<!-- 创建c3p0数据源 -->
<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/test"></property>
<property name="user" value="root"></property>
<property name="password" value=""></property>
</bean>
<!-- dao -->
<bean id="accountDao" class="com.spring.tx.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- service -->
<bean id="accountService" class="com.spring.tx.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 事务管理器 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 将管理器交予spring * transaction-manager 配置事务管理器 * proxy-target-class true:底层强制使用cglib
代理 -->
<tx:annotation-driven transaction-manager="txManager" />
</beans>
测试
@Test
public void test() {
String xmlPath = "com/spring/tx/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
IAccountService bean = (IAccountService) applicationContext.getBean("accountService");
bean.transfer("Jack", "Luna", 1000);
}
3.3.2 在service实现类上添加@Transactional
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao;
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String outer, String inner, Integer money) {
// TODO Auto-generated method stub
accountDao.out(outer, money);
accountDao.in(inner, money);
}
}
测试
@Test
public void test() {
String xmlPath = "com/spring/tx/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
IAccountService bean = (IAccountService) applicationContext.getBean("accountService");
bean.transfer("Jack", "Luna", 1000);
}