事务概述
- 逻辑上的一组操作,组成这组操作的各个单元,要么全都成功,要么全都失败
- 也就是一套完整的业务代码,要么在出现问题的时候全部成功,要么全部失败
事务的特性
原子性
:事务不可被分割
一致性
:事务执行前,和执行后的数据是完整性的保持一致的
隔离性
:一个事务的执行不应该受到其他事务的干扰
持久性
:一旦事务结束,数据就会持久化的到数据库中
脏读
:一个事务读取到另一个事务中未提交的数据
不可重复读
:一个事务读取到另一个事务中已经提交的 update
的数据,导致一个事务中多次查询的结果不一致
虚读
、幻读
:一个事务读取到另一个事务中已经提交的 insert
的数据,导致一个事务中多次查询的结果不一致
解决读问题
Read uncommitted
:未提交读,任何读的问题都解决不了
Read committed
:已提交读,解决了 脏读
,但是 不可重复读
和 虚读
有可能会发生
Repeatable read
:重复读,解决了 脏读
和 不可重复读
,但是 虚读
有可能会发生
Serializable
:解决所有读问题
Spring事务管理相关API
PlatformTransactionManage
:平台事务管理器是一个接口,下面有两个实现类,如下:
DataSourceTransactionManager
:底层使用的是 JDBC管理事务
HibernateTransactionManager
:底层使用是 Hibernate管理事务
TransactionDefinition
- 事务定义信息:用于定义事务相关的信息如,
隔离级别
、超时信息
、传播行为
、是否只读
TransactionStatus
事务管理中API之间的关系
- Spring 进行事务管理的时候,首先平台事务管理器(PlatformTransactionManager)根据事务定义信息(TransactionDefinition)进行事务的管理
- 在事务管理过程中,产生各种状态,将这些状态的信息记录到事务状态的对象(TransactionStatus)中
Spring事务的传播行为
- Spring中提供了
7
种事务的传播行为,如下:
保证多个操作在同一个事务中
属性 |
作用 |
PROPAGATION_REQUIRED |
默认值,如果A中有事务,使用A中的事务,如果A没有,创建一个新的事务,将操作包含进来 |
PROPAGATION_SUPPORTS |
支持事务,如果A中有事务,使用A中的事务,如果A没有事务,不使用事务 |
PROPAGATION_MANDATORY |
如果A中有事务,使用A中的事务,如果A没有事务,抛出异常 |
保证多个操作不在同一个事务中
属性 |
作用 |
PROPAGATION_REQUIRES_NEW |
如果A中有事务,将A的事务挂起(暂停)创建新事务,只包含自身操作,如果A中没有事务,创建一个新事务,包含自身操作 |
PROPAGATION_NOT_SUPPORTED |
如果A中有事务,将A的事务挂起,不使用事务管理 |
PROPAGATION_NEVER |
如果A中有事务,报异常 |
嵌套式事务
属性 |
作用 |
PROPAGATION_NESTED |
嵌套事务,如果A中有事务,按照A的事务执行,执行完成后,设置一个保存点 执行B中的操作,如果没有异常,执行通过,如果有异常,可以选择回滚到最初始位置,也可以回滚到之前设置的保存点 |
Spring事务管理
搭建Spring事务管理环境
- 创建
AoccuntDao
接口,定义两个方法规范
/**
* @author: BNTang
**/
public interface AoccuntDao {
void OutMoney(String from, BigDecimal money);
void Inmoney(String to, BigDecimal money);
}
- 实现
AoccuntDao
接口,创建 AoccuntDaoImpl
实现类,内容如下:
/**
* @author: BNTang
**/
public class AoccuntDaoImpl extends JdbcDaoSupport implements AoccuntDao {
@Override
public void OutMoney(String from, BigDecimal money) {
this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money, from);
}
@Override
public void Inmoney(String to, BigDecimal money) {
this.getJdbcTemplate().update("update account set money = money + ? where name = ?", money, to);
}
}
- 修改
applicationContext.xml
将 Bean 交给 Spring 进行管理
<bean id="aoccuntDao" class="top.it6666.dao.impl.AoccuntDaoImpl"/>
- 在 Dao 中注入数据源
- 在 Dao 当中注入 JDBC模板,要保证 Dao 继承了
JdbcDaoSupport
- 继承之后,就有了
Datasource
的 Set
方法了,就可以进行注入了
- 修改
applicationContext.xml
Dao 中注入 JDBC模板
...
<bean id="aoccuntDao" class="top.it6666.dao.impl.AoccuntDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
...
- 创建
AccountService
接口,定义业务方法规范
/**
* @author: BNTang
**/
public interface AccountService {
void transferMoney(String from, String to, BigDecimal money);
}
- 在创建
AccountServiceImpl
实现类,实现 AccountService
接口,并实现当中的方法
/**
* @author: BNTang
**/
public class AccountServiceImpl implements AccountService {
private AoccuntDao aoccuntDao;
public void setAoccuntDao(AoccuntDao aoccuntDao) {
this.aoccuntDao = aoccuntDao;
}
@Override
public void transferMoney(String from, String to, BigDecimal money) {
aoccuntDao.OutMoney(from, money);
aoccuntDao.Inmoney(to,money);
}
}
- 配置 Service 交给 Spring 并注入 Dao
...
<bean id="accountService" class="top.it6666.service.impl.AccountServiceImpl">
<property name="aoccuntDao" ref="aoccuntDao"/>
</bean>
...
/**
* @author: BNTang
**/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
@Resource
private AccountService accountService;
@Test
public void transferMoney() {
accountService.transferMoney("BNTang", "JonathanTang", BigDecimal.valueOf(1000));
}
}
添加事务
编程式事务
- 需要手动编写代码
- 配置平台事务管理器,修改
applicationContext.xml
...
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
...
- Spring 提供了事务管理的模板类
- 配置事务管理模板类,修改
applicationContext.xml
...
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
...
- 在业务层注入事务管理模板
- 修改
AccountServiceImpl
添加一个属性,待会用来注入用
/**
* @author: BNTang
**/
public class AccountServiceImpl implements AccountService {
private AoccuntDao aoccuntDao;
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
...
}
- 修改
applicationContext.xml
...
<bean id="accountService" class="top.it6666.service.impl.AccountServiceImpl">
<property name="aoccuntDao" ref="aoccuntDao"/>
<property name="transactionTemplate" ref="transactionTemplate"/>
</bean>
...
- 编写事务管理相关的代码,修改
AccountServiceImpl
:
/**
* @author: BNTang
**/
public class AccountServiceImpl implements AccountService {
private AoccuntDao aoccuntDao;
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public void setAoccuntDao(AoccuntDao aoccuntDao) {
this.aoccuntDao = aoccuntDao;
}
@Override
public void transferMoney(String from, String to, BigDecimal money) {
this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
aoccuntDao.OutMoney(from, money);
int a = 1 / 0;
aoccuntDao.Inmoney(to,money);
}
});
}
}
- 直接运行测试代码即可验证
- 你可以先在有异常的情况下运行一次
- 然后去掉异常运行一次即可验证
声明式事务
- 配置事务管理器
- 修改
applicationContext.xml
...
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
...
- 开启支持使用注解的方式来进行管理事务
- 修改
applicationContext.xml
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
<tx:annotation-driven transaction-manager="transactionManager"/>
/**
* @author: BNTang
**/
@Transactional
public class AccountServiceImpl implements AccountService {
private AoccuntDao aoccuntDao;
public void setAoccuntDao(AoccuntDao aoccuntDao) {
this.aoccuntDao = aoccuntDao;
}
@Override
public void transferMoney(String from, String to, BigDecimal money) {
aoccuntDao.OutMoney(from, money);
int a = 1 / 0;
aoccuntDao.Inmoney(to, money);
}
}
- 测试类代码同上
- 你可以先在有异常的情况下运行一次
- 然后去掉异常运行一次即可验证