事务概念
事务:数据库操作最基本单元,要么都成功,如果有一个失败,就是都失败
典型场景:银行转账,A给B转账,A钱变少,B钱变多,如果A转账过程出错,则B钱不会变多,A不会变少
四大特性:ACID
原子性:就是要么都成功,有一个失败,就全部失败,不做操作
一致性:一个状态到另一个状态必须一致的,如A没给B转成,2人前都不动,或者转成A少的数目和B增加数目一致,而不能是A失败不变,B增加或A减少,B不变
隔离性:数据库并发操作,事务间不能相互影响
持久性:修改操作一旦提交,数据库数据就发生对应变化(而不是没反应)
事务环境搭建(代码搭建)
1JavaEE常见3层,web页面层,service业务逻辑层,dao数据库操作层,如上图,我们为了实现事务环境搭建,关心的是后2个层,其中Dao不考虑业务逻辑,只关心和数据库有关的操作,如转账,修改金额,而service层则是具体实现A少钱,B多钱的过程
2我们使用可视化工具给user_db下创建表t_account,设置3个字段id,username,money
3表里创建2条数据,这里我用的是MySQL workbench,记得修改要点apply应用提交,
4可以查到数据确实创建了
然后就是创建Service类,UserDao,service注入UserDao,UserDao注入JdbcTemplate对象,JdbcTemplate注入DataSource,这个我们上节课也做过类似的
5之前的xml就是创建数据库连接池和配置jdbcTemplate对象,这个xml文件不用动,我们删掉之前的测试类,service,dao先关
6UserService类,注解创建对象,并自动装配Dao对象
7 8UserDao和其实现类,注解创建对象,并自动装配jdbcTemplate对象
9我们定义方法,修改money
10实现类如上,我们使用update方法,不记得怎么用看上节课,
11Service类里定义转账方法,底层是调用dao的方法
12如上,实现了转账100,当然这是正常情况下,
13如上,比如银行操作时,发生断电了,我们使用/0实现故障,操作前将账户设为1000,1000
14结果就变成报错外,数据lucy少了100,而Mary没变,总共丢了100,肯定是不对的
这就需要使用事务来进行处理,先说下大致逻辑
如果代码编程:我们使用trycatch结构捕获异常,try里先开启事务,然后数据库操作,最后结尾没有异常就提交事务,catch捕获到异常,里面进行事务的回滚操作。但是spring5给我们提供了更方便的事务管理相关的封装
spring事务管理介绍
之前我们之前说过JavaEE的三个层,我们事务管理一般在业务逻辑层里
spring进行事务管理操作一般有2种方式:编程式和声明式(常用后者)
我开始说的try catch finally就是编程式事务操作
对于声明式事务管理,有2种实现方式,即熟悉的xml配置和注解配置(当然还是注解最常用),spring使用事务管理,其底层其实就是AOP
spring对事务有专门的接口,针对不同的框架,有专门的实现类
15如上图,接口就是PlatformTransactionManager,看结构可以看到子接口和实现类,其中,使用JdbcTemplate和Mybatis都是使用DataSourceTransactionManager,如果是使用Hibernate,则使用HibernateTransactionManager
声明式事务管理(基于注解)
步骤:1 配置文件创建事务管理器实现类对象bean,这个没啥说的,如下图
16 17我们查看源码可以看到其有DataSource属性,我们需要给其注入数据源属性,xml里数据源已经制定好了
182.导入aop和tx名称空间,因为事务是依赖aop的,如下图,相信我们已经回来
193 开启事务注解,此处还是在xml里
20使用tx空间的annotation-driven标签,指定事务管理为我们之前创建的对象
214给Service添加注解@Transactional(给类添加,则类所有方法都进行事务管理,也可以给方法添加,使指定方法事务管理),如上
22最后运行测试类,我们先把金额改回1000,1000然后运行带/0的代码,可以看到虽然报错,但是账户并没有变化
基于注解配置事务的参数
刚才我们使用了@Transactional注解,其实是可以传入参数的
23我们给注解加上括号,可以看到其可以传入很多参数,我们重点关辛苦其中红框的6个,分别是事务传播行为,事务隔离,超时,只读,回滚和不回滚。下面我们分别对6个属性逐一解释
propagation事务传播行为
事务传播官方解释:多事务方法进行调用,过程中如何进行事务管理
什么是事务呢,对数据库进行添加修改删除等操作(使事务发生变化,不算查询)就是事务
24上面一张图方便我们理解传播行为,我们定义2个方法,add和update,其中add内部调用了update,我们给add方法加了事务,而update没有事务,这是一种情况,还有一种update有事务,调用他的add没事务,另一种,还有2个都有事务,如何管理这些情况的事务就是事务传播行为
25spring对于事务传播,定义了7种行为,如上图
REQUIRED,图中解释比较官方,我们还是以add和update为例,比如我们add有事务,而update没有事务,则update操作依据与add的事务。如果add事务终止(视频上说没有事务,,觉得这不太可能),则启动一个事务执行(是spring里的默认设置)
REQUIRED_NEW,不管add是否有事务运行,都新建一个事务(之前有的事务会被挂起)
其他几个可能不是特别常用
26我们给加上propagation属性如上,idea等号右侧输入REQUIRED,自动补全
隔离级别
我们知道事务有隔离性,即多事务操作不能互相影响
多事务有3个读问题:脏读,不可重复读,虚读(也叫幻读)
脏读:一个未提交的事务读取到了另一个未提交事务的数据
27上图来说明脏读,比如2个人都访问1条记录,都开启了事务,如果没有隔离,A事务要给钱-100,B要给钱变成60000,如果B修改60000了,可能A读到60000再-100,但是B没有提交,B回滚了,A就出现问题了,注意2个都是未提交
不可重复读:一个未提交事务访问已提交修改的事务的数据
28如上图,2个人分别开启事务A,B,A获得金额5000,B也是,但是B想修改把钱变成了900,并提交了事务,而A再进行事务读取时,发现已经变化了
当然脏读是质量问题,而不可重复度是一种现象
虚读:一个未提交事务访问另一个事务已提交添加的数据
29spring配置的隔离级别属性如上,我们可以看到不同隔离级别的几种读现象出现
30添加了隔离属性的注解如上
其他事务属性
1超时 timeout,如果事务一定时间内未提交,则进行回滚,默认-1,即不超时,设置指定值单位为秒
2 readOnly,设置是否只读,默认false,设置为true,则只能查询,不能修改添加删除
3 rollBackFor回滚,设置哪些异常进行回滚,等号右侧为异常类名.class
4 noRollBackFor不回滚,设置哪些异常不回滚,赋值同上
基于xml进行声明式事务管理
虽然注解是最常用的,这里仅体现xml实现方式,分3步:1配置事务管理器,2配置通知,3配置切入点和切面。2,3为aop部分,我们之前讲过,我们增强的部分叫做通知,切入点就是把事务加载到哪个方法,而切面就是把事务加载的过程进行体现
31我们把之前的xml拷贝一份叫bean2.xml,并把<tx:annotation-driven transaction-manager="tm"></tx:annotation-driven>开启事务注解删掉,保留bean标签,因为这个标签就是创建事务管理器
32接下来配置通知,我们使用tx:advice标签,给其起个id,里面在tx:attributes标签里添加方法,我们可以给其传入方法名,也可以像下一行使用*,表示对所有user2开头的方法增强,我们还可以给标签其他参数,就可以看到我们事务的几个属性啦
33然后就是创建切入点,还是使用aop:config标签,然后内部使用aop:pointcut,expresssion传入切入点表达式
34然后是配置切面,我们学aop是使用aop:aspect,这里使用aop:advisor,pointcut_ref就是传入我们的切入点id,然后advice-ref传入我们的通知id
35为了运行测试把service里注解给注释了
36然后修改测试类注意是bean2.xml,本以为你我运行会和视频一样OK的,但是我报错了,这里提示创建bean对象异常,我仔细排查了半天终于找到了结果,要给通知添加事务管理器关联
37修改后正常,只显示/0异常,我猜测可能默认是设置id为如下图,idea提示的,而我起名tm,还真他妈。。
38 39运行结果正常
使用完全注解进行事务操作
我们之前使用注解还是用了xml文件,这次我们只通过注解实现事务操作
40我们之前的xml文件是这样的
首先完全注解开发,需要给创建配置类替代xml文件
41如上图,我们创建config包,创建配置类TxConfig,然后使用几个注解,这里算是只是回顾了@Configuration声明此类为配置类,@ComponentScan开启组件扫描,我们在com.demo.spring5下面扫描即可,@EnableTransactionManagement 开启事务管理。接下来就是给配置类里注入属性了,像数据库连接池等等
42我们xml里是使用bean标签,我们这里使用@Bean注解,然后对象设置返回数据源,类型参照xml创建对象,里面我们new这个类即可,其中几个属性通过对象的set方法,参照xml把属性设置过来,最后返回数据源,如上,接下来,参照之前的文件我们需要创建JdbcTemplate对象,还是如法炮制
43如上,其中模板对象需要注入数据源,我们可以通过上个方法的返回值创建,但是没必要,因为默认会调用方法,但是因为单例,IOC容器只创建一次,我们直接使传入数据源参数,让spring按类型自动装配即可
44创建事务管理器,如上,没什么说的,至此,我们的配置类就把xml文件完全替代了
45我们把Service里的注释取消,恢复@Transactional注解
46测试类里变化的就是需要使用注解的上下文,参数传入配置类的class
47就在我兴致冲冲的去运行,又TM报错啦,NND,提示是字符编码问题,setUrl里传入的字符串有问题,后来将&;改成&即可,因为xml里是特殊语法
48然后还没完,改完了提示事务隔离界别不支持None,即设置REPEATABLE_READ不行,,
49最后改成default,只有/0错误,,终于完事
50验证如下,实际使用中,完全注解是王道,xml不用最好
网友评论