数据库事务的四大特性和隔离级别
- 数据库事务的四大特性:原子性、隔离性、一致性和永久性。
1-1)、原子性是指已经不能再进行细分,对于数据库的操作要么全部成功,要么全部失败;
1-2)、隔离性是指两个事务之间相互不影响,彼此相互隔离;
1-3)、一致性是指数据操作过后是从一个一致性的状态到另一个一致性的状态;
1-4)、永久性指对数据的改变是永久的;
例如:账户A有5000元,分别转给B、C账户2000和1000,原子性就是每次要么转账成功,要么转账失败,不会出现其他情况;隔离性是A给B转账不会影响A给C转账;一致性是指A给B转账后,A剩余3000,B有2000,给C转账后,A剩余2000,C有1000,A、B和C三种总和还是5000;永久性是转账完成A就剩2000,B有2000,C有1000,如果没有其来回操作,账户的钱信息就保存在数据里;
- 数据库事务的隔离级别:读未提交、读已提交、可重复读和可串行化;
2-1)、读未提交是指事务A可以读取事务B没有提交的修改,如果事务B最终没有提交且回滚,则事务A就产生了脏读;
2-2)、读已提交是指事务A只能读取事务B已提交的数据修改,则事务A就不会出现脏读,但是事务A内读取的数据有可能发生变化,导致两次读到的数据不一致,所谓的不可重复读;
2-3)、可重复读是指事务A内,复制一份数据的镜像,在当前事务A内所有的读取都从镜像里读取,实现了数据的可以重复读;同样事务B读取有事务B的镜像;
2-4)、前三种事务隔离级别分别解决了脏读、不可重复读的问题,但是对于幻读和虚读没有做到卡控,因此出现了可串行化,不论是读事务还是写事务,所有的事务必须串行化处理。
- Spring事务
3-1)、Spring使用容器管理bean对象,在bean对象初始化时,spring容器会判断当前类创建普通对象、JDK代理对象还是CGLIB代理对象,没有实现接口的类如果需要代理对象进行方法加强,则生成CGLIB代理对象,实现接口的类则生成JDK代理对象:
3-2)、同一个service类方法相互调用事务问题
A)、实例A是代理对象,A的B方法没有事务,在B中通过直接调用C方法,虽然方法C上包含了事务注解,但是事务不生效如:
B)、既然这样,B方法就加事务,事务是不是就生效了,经测试,B生效了,但是C的事务只能承接B的事务(C上加了注解没用,不加事务也存在,如果我B上事务是REQUIRED,C上事务是REQUIRES_NEW,这时是不生效的);
原因,查了很多文档,有下列解释:
由于Spring AOP采用了动态代理实现AOP,在Spring容器中的bean(也就是目标对象)会被代理对象代替,代理对象里加入了我们需要的增强逻辑,当调用代理对象的方法时,目标对象的方法就会被拦截。而上文中问题出现的症结也就是在这里,通过调用代理对象的action方法,在其内部会经过切面增强,然后方法被发射到目标对象,在目标对象上执行原有逻辑。
如何让事务生效,解决方法如下:
方法一、既然不生效是因为不是有代理对象调用,我们直接方法C封装在另一个service类中即可解决,代码不在实现;
但是我们在实际场景中,不想因为一个简单的C方法创建一个新的类型且方法中事务类型就一种,
方法二(只能解决相同事务)可以如下:
但在实际的业务场景中,B方法都是复杂的业务逻辑校验和查询库信息,我们不想在调用B方法时就启动事务,也不想通过创建新的类型,怎么办呢,我们注意到原理里说B调C方法时内部调用,且没有加强入手,我们只要调用C方法时使用加强代理对象就行。
方法三、如下:
当然,该方法还需要增加额外的配置,将bean对象暴露给当前线程;
否则AopContext获取不到代理类;写到这里突然有个问题,以前都是从applicationContext中获取bean(bean对象在spring中已经是代理对象了)。
方法四:
三、四方法类似都行。