一、概述
1.1 什么是事务
事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)
1.2 事务的特性
- 原子性(Atomicity)
一个事务中的所有操作不可分割,要么全部成功,要么全部失败
- 一致性(Consistency)
一个事务执行完成前后,要保证事务前后业务逻辑的一致性
- 隔离性(Isolation)
事务的执行不能受其他事务干扰,彼此隔离
- 持久性(Durability)
事务的提交或者回滚都需要持久化到存储介质中
1.3 spring事务
spring对事务提供了两种实现方式:声明式事务和编程式事务管理;spring事务基于AOP实现
1.4 spring事务的隔离级别
- 并发事务产生的问题
问题 | 描述 | 备注 |
脏读 | 一个事务对数据进行更新操作,另一个事务对数据进行读取,前者进行了回滚,后者读取到的数据便是脏数据,称之为脏读 | |
不可重复读 | 一个事务对数据进行多次重复读取,另一个事务在读取期间对数据进行了更新操作,前者读取到的数据前后不一致,称之为不可重复读 | |
幻读 | 一个事务对数据进行多次读取操作,期间另一个事务进行了插入数据的操作,前者读取数据前后不一致,称之为幻读 |
- 事务的隔离级别及解决的问题
隔离级别 | 含义 | 脏读 | 不可重复读 | 幻读 |
READ UNCOMMITTED | 读未提交 | |||
READ COMMITTED | 读已提交 | ✅ | ||
REPEATABLE READ | 可重复读 | ✅ | ✅ | |
SERIALIZABLE | 序列化 | ✅ | ✅ | ✅ |
1.5 spring事务的传播特性
传播行为 | 含义 | 备注 |
PROPAGATION_REQUIRED | 表示当前方法必须运行在事务中,如果当前没有事务,就新建一个事务 | |
PROPAGATION_SUPPORTS | 如果当前有事务,就在事务中执行,如果当前没有事务,就以非事务方式执行 | |
PROPAGATION_MANDATORY | 表示当前方法必须在事务中运行,如果当前没有事务,就抛出异常 | |
PROPAGATION_REQUIRED_NEW | 表示当前方法必须运行在自己的事务中,如果当前存在事务,把当前事务挂起 | |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 | |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常 | |
PROPAGATION_NESTED | 嵌套事务 |
二、xml配置实现
2.1 步骤
- 配置事务管理器
- 配置通知
- 配置切入点表达式
- 织入通知
- 配置事务属性
2.2 示例
beans.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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置service -->
<bean id="userService" class="com.lizza.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<!-- 配置dao,由于dao继承了JdbcDaoSupport,所以需要初始化dataSource -->
<bean id="userDao" class="com.lizza.dao.impl.UserDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置数据源,使用spring提供的dataSource:DriverManagerDataSource -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- spring基于xml声明式事务的配置 -->
<!--
1. 配置事务管理器
2. 配置通知
3. 配置切入点表达式
4. 织入通知
5. 配置事务属性
-->
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 配置事务属性
name: 方法名
isolation: 隔离级别, 默认为DEFAULT, 为数据库的隔离级别
propagation: 事务的传播行为, 默认为REQUIRED, 表示一定会有事务; 涉及数据编辑操作选择REQUIRED, 涉及数据查询操作选择SUPPORTS
read-only: 是否只读事务, 默认值为false, 表示读写
timeout: 设置事务的超时时间, 默认值为-1, 表示永不超时
rollback-for: 指定一个异常, 发生该异常时, 事务进行回滚, 其他异常不回滚; 默认值为都需要回滚
no-rollback-for: 指定一个异常, 发生改一次, 事务不回滚, 其他异常回滚; 默认值为都需要回滚
-->
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="transfer*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 配置切入点表达式 -->
<aop:pointcut id="txPoint" expression="execution(* com.lizza.service.impl.*.*(..))"/>
<!-- 织入通知 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint" ></aop:advisor>
</aop:config>
</beans>
三、注解配置实现
3.1 关键注解
-
@EnableTransactionManagement
:开启spring事务管理 -
@Transactional
:指定需要运行在事务中的类或者方法
3.2 步骤
- 配置JdbcConfig
@Configuration
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean("jdbcTemplate")
public JdbcTemplate createJdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean("dataSource")
public DataSource createDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
- 配置TransactionConfig
@Configuration
public class TransactionConfig {
/**
* 配置事务管理器
* @author: lizza@vizen.cn
* @date: 2020/4/6 10:54 下午
* @param dataSource
* @return org.springframework.transaction.PlatformTransactionManager
*/
@Bean("transactionManager")
public PlatformTransactionManager createPlatformTransactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
- 配置SpringConfig
@Configuration
@ComponentScan("com.lizza")
@Import({JdbcConfig.class, TransactionConfig.class})
@EnableTransactionManagement
@PropertySource("classpath:jdbc.properties")
public class SpringConfig {
}
- 业务层配置
@Service("userService")
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public int addOne(User user) {
return userDao.addOne(user);
}
@Override
public int deleteOne(int id) {
return userDao.deleteOne(id);
}
@Override
public int updateOne(User user) {
return userDao.updateOne(user);
}
@Override
public User findOne(int id) {
return userDao.findOne(id);
}
@Override
public List<User> findAll() {
return userDao.findAll();
}
@Override
public int transferMoney(int from_id, int to_id, double money) {
// 1. 获取付款账户和收款账户
User from_user = userDao.findOne(from_id);
User to_user = userDao.findOne(to_id);
// 2. 付款账户付款,收款账户收款
from_user.setMoney(from_user.getMoney() - money);
to_user.setMoney(to_user.getMoney() + money);
// 3. 更新账户信息
userDao.updateOne(from_user);
int i = 100/0;
userDao.updateOne(to_user);
return 0;
}
}