1、什么是事务
事务是数据库操作最基本的单元,逻辑上一组操作,要么都成功,如果有一个失败,则所有操作都失败
经典场景:银行转账
2、事物的四个特性(ACID)
原子性、一致性、隔离性、持久性
3、引出事务操作
(1)数据库建表account,并插入数据
pom依赖
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.7.RELEASE</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.20</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>4.3.0.RELEASE</version> </dependency> </dependencies>核心配置文件application.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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 开启组件扫描--> <!-- 2、扫描上层--> <context:component-scan base-package="com.lu"></context:component-scan> <!-- 数据库连接池。 destroy-method="close"作用是当数据库连接不使用的时候,就把该连接重新放到数据池中,方便下次使用调用. xml中:&要用 & 转义--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/spring-crud?userSSL=true& useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC" /> <property name="username" value="root" /> <property name="password" value="root" /> </bean> <!--JdbcTemplate对象--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!--注入dataSourceDefault--> <property name="dataSource" ref="dataSource"></property> </bean> </beans>(2)创建service,dao完成对象创建和注入关系
UserDao类
public interface UserDao { void addMoney(); void reduceMoney(); }UserDaoImpl类
@Repository public class UserDaoImpl implements UserDao{ @Autowired private JdbcTemplate jdbcTemplate; public void addMoney() { String sql = "update account set money = money-? where username=?"; jdbcTemplate.batchUpdate(sql,"100","lucy"); } public void reduceMoney() { String sql = "update account set money = money+? where username=?"; jdbcTemplate.batchUpdate(sql,"100","tom"); } }UserService类
@Service public class UserService { //注入dao @Autowired private UserDao userDao; //转账方法 public void accountMoney(){ //lucy少100 userDao.reduceMoney(); //tom多100 userDao.addMoney(); } }测试类TestAccount
public class TestAccount { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); UserService userService = context.getBean("userService",UserService.class); userService.accountMoney(); } }3、正常执行上边程序,是没问题的。但是,如果在步骤中间,有异常,就可能导致一个成功、一个失败的问题,进而导致钱数错误。
//转账方法 public void accountMoney(){ //lucy少100 userDao.reduceMoney(); //模拟异常 int i= 10/0; //tom多100 userDao.addMoney(); }4、如何解决上边的问题?
使用事务进行解决
事务操作的过程
1、事务添加到JavaEE三层架构里面的Service层(业务逻辑层)
2、spring中有两种事务管理方式
编程式事务管理(如上图,4步)
声明式事务管理(常用)
3、声明式事务管理
基于注解方式(常用)基于xml配置文件方式4、在spring进行声明式事务管理,底层使用AOP
5、spring事务管理API
(1)提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类
@Transactional,这个注解可以添加类上面,也可以添加方法上面。
添加到类上面,这个类里面所有方法都添加事务。
添加到方法上面,只是为这个方法添加事务。
在service类上面添加注解@Transactional,在这个注解里面可以配置事务相关参数。
使用方法:就是在Transactional注解后边的括号内添加propagation = xxx 例如: @Transactional(propagation = Propagation.REQUIRED)
事务传播行为一般是:多事务方法之间的调用,这个过程中事务时如何进行管理的
图片来自尚硅谷王泽老师B站视频
事务的传播行为指方法之间事务的传播,比如方法A调用了方法B: 有7种,required 、required_new常用:
REQUIRED Spring默认的传播机制,如果A有事务就使用当前事务, 如果A没有事务, 就创建一个新事务。REQUIRES_NEW 不管A有没有事务都创建一个新事务。NEVER 如果A有事务就抛异常, 如果A没有事务, 就以非事务执行。使用方法:就是在Transactional注解后边的括号内添加,如下。如果有多种,逗号隔开。 @Transactional(isolation = Isolation.REPEATABLE_READ)
并发下事务会产生的问题
举个例子,事务A和事务B操纵的是同一个资源,事务A有若干个子事务,事务B也有若干个子事务,事务A和事务B在高并发的情况下,会出现各种各样的问题。
脏读、不可重复读、幻读
(1)脏读(Dirty read) 脏读发生在一个事务读取了被另一个事务改写但尚未提交的数据时。
(2)不可重复读(Nonrepeatable read) 不可重复读发生在一个事务执行相同的查询两次或两次以上,但每次查询结果都不相同时。这通常是由于另一个并发事务在两次查询之间更新了数据。
(不可重复读重点在修改)
(3)幻读(Phantom reads) 幻读和不可重复读相似。当一个事务(T1)读取几行记录后,另一个并发事务(T2)插入或者删除了一些记录时,幻读就发生了。在后来的查询中,第一个事务(T1)就会发现一些原来没有或少了的额外记录。 (幻读重点在新增或删除) 参考的blog:https://www.cnblogs.com/mseddl/p/11577846.html
解决:通过设置事务隔离级别,解决读的问题
隔离级别含义Read Uncommitted读未提交:可能导致脏读、幻读或不可重复读。Read Committed读已提交:可防止脏读,但幻读和不可重复读仍可能发生。Repeated Read可重复读:可防止脏读和不可重复读,但幻读仍可能发生。Serializable串行化:完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。练习代码: 链接:https://pan.baidu.com/s/1jOYuR-sfeT6DFhLWXqJOpg 提取码:vx8x