一,事务的基本概念
1,什么是事务
(1)事务是数据库操作最基本单元,逻辑上一组操作,这一组操作要不都成功,要么若是有一个失败,则都失败
(2)典型场景:银行转账
2,事务有四个特性(ACID)
(1)原子性:要成功都成功,要失败都失败
(2)一致性:操作之前和操作之后总量不变
(3)隔离性:多事务之间不会产生影响
(4)持久性:事务提交成功以后数据将发生永久变化
二,搭建事务操作环境
环境:WEB 层;Service 层(业务操作);Dao 层(数据库操作,不写业务)。
在Dao层创建实现方法,在Service层也创建方法但是去调用Dao层的方法。
举例:zhangsan 的 money 加 1,lisi 的 money 减 1:
1,创建数据库表,添加记录
2,创建 Service,搭建 Dao,完成对象创建和注入关系
(1)Service 注入 Dao,在 Dao 注入 JdbcTemplate,在 JdbcTemplate 注入 DataSource
- 配置文件中连接池
<!--组件扫描-->
<context:component-scan base-package="com.at"></context:component-scan>
<!--数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="name" value="jdbc:mysql://localhost:3306/demo1?useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
- dao中创建 UserDao 接口,和实现 UserDao 的 UserDaoImpl
public interface UserDao {
}
@Repository
public class UserDaoImpl implements UserDao{
@Autowired
private JdbcTemplate jdbcTemplate;
}
@Service
public class UserService {
//注入dao
@Autowired
private UserDao userDao;
}
配置文件中
<!--JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入dataSource-->
<property name="dataSource" ref="dataSource"></property>
</bean>
3,在 dao 中创建两个方法:add 和 sub 的方法,在 service 中创建方法(同时add和sub)
public interface UserDao {
public void add();
public void sub();
}
@Repository
public class UserDaoImpl implements UserDao{
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void add() {
String sql = "update t_account set money=money+? where username=?";
jdbcTemplate.update(sql,1,"zhangsan");
}
@Override
public void sub() {
String sql = "update t_account set money=money-? where username=?";
jdbcTemplate.update(sql,1,"lisi");
}
}
@Service
public class UserService {
//注入dao
@Autowired
private UserDao userDao;
public void addSub() {
userDao.add();
userDao.sub();
}
}
4,上方代码正常执行没有问题,但是如果代码在执行工程中出现异常,将产生问题
如:
public void addSub() {
userDao.add();
//假设此处出现故障故障
。。。。。。
userDao.sub();
}
在 userDao.add() 后出现故障,那么 userDao.sub() 将不会执行,如此就不能实现 add 与 sub 同时执行
(1)解决方案
- 使用事务进行解决
(2)事务操作过程
public void addSub() {
try {
//1,开启事务
//2,进行业务操作
userDao.add();
//假设此处出现故障故障
。。。。。。
userDao.sub();
//3,没有发生异常,提交事务
} catch (Exception e) {
//4,出现异常,则事务回滚
}
}
三,Spring 事务管理介绍
1,事务一般添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)
2,在 Spring 进行事务管理操作
(1)有两种:编程式事务管理,声明式事务管理。(多数用声明式)
编程式:
public void addSub() {
try {
//1,开启事务
//2,进行业务操作
userDao.add();
//假设此处出现故障故障
。。。。。。
userDao.sub();
//3,没有发生异常,提交事务
} catch (Exception e) {
//4,出现异常,则事务回滚
}
}
3,声明式事务管理
(1)基于注解方式 (大多时候用基于注解方式)
(2)基于 xml 配置文件方式
4,在 Spring 进行声明式事务管理,底层使用 AOP 原理
5,Spring 事务管理 API
(1)提供了一个接口,代表事务管理器,这个接口针对不同的框架提供了不同的实现类
接口:PlatformTransactionManager
其中 JDBC 的实现类为:DataSourceTransactionManager
四,基于注解方式的声明式事务管理
1,在 Spring:配置文件配置事务管理器
<!--数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="name" value="jdbc:mysql://localhost:3306/demo1?useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
<!--创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
2,在 Spring 配置文件中,开启事务注解
(1)在 Spring 配置文件中引入名称空间tx
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.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">
(2)开启事务注解
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
3,在 Service 类上面(获取 Service 类里面方法上面)添加事务注解
(1)@Transactional,这个注解添加到类上面,也可以添加方法上面
(2)如果把这个注解添加到类上面,这个类里面的所有的方法都添加事务
(3)如果把这个注解添加到方法上面,为这个方法添加事务
@Service
@Transactional
public class UserService {
这样当出现异常,则可以自动处理,回滚
五,声明式事务管理参数配置
1,在 service 类上面添加注解 @Transactional,在这个注解里面可以配置事务相关参数
- propagation:事务传播行为
(1)多事务方法直接进行调用,这个过程中事务是如何进行管理的
(事务方法:对数据库表数据进行变化的操作,像查询就不是事务方法)
研究如:有事务行为的方法调用了无事务行为的方法时,有事务行为的方法调用了也是有事务行为的方法时,无事务行为的方法调用了有事务行为的方法时等等改如何处理。
@Service
@Transactional(propagation = Propagation.REQUIRED)
public class UserService {
- isolation:事务隔离级别
(1)事务有个特性 隔离性:多事务操作之间不会产生影响。不考虑隔离性产生很多问题。
(2)有三个读问题:脏读,不可重复读,虚(幻)读
(3)脏读:多事务之间,一个未提交事务读取到另一个未提交事务的数据
(4)不可重复读:一个未提交事务读取到另一个提交事务的修改数据
(5)虚(幻)读:一个未提交的事务读取到另一个提交事务添加数据
(6)通过设置事务隔离级别,解决上述问题
脏读 | 不可重复读 | 幻读 | |
---|---|---|---|
READ UNCOMMITTED (读未提交) | 有 | 有 | 有 |
READ COMMITTED (读已提交) | 无 | 有 | 有 |
REPEATABLE READ(可重复读) | 无 | 无 | 有 |
SERIALIZABLE(串行化) | 无 | 无 | 无 |
@Service
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ)
public class UserService {
(mysql 默认是 REPEATABLE READ(可重复读))
- timeout:超时时间
- readOnly:是否只读
- rollbackFor:回滚
- noRollbackFor:不回滚