一、前言
[此篇很多是我自己的理解和总结,如果有错误的地方欢迎批评指正。部门后端人员务必全部阅读完此篇,尤其是红色字体部分!]
1.1 事务的概念
事务是数据库管理系统执行过程中的一个逻辑单位,一个操作序列。就数据库事务而言它的存在包含有以下两个目的:
- 为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
- 当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。
事务有四大特性(ACID):
- 原子性(Atomicity):事务是数据库的逻辑工作单位,而且是必须是原子工作单位,对于其持久化的操作,要么全部执行,要么全部不执行。比如A向B转账100元,就一定要保证原子性(要么同时成功,要么同时失败)。
- 一致性(Consistency):在事务开始以前,被操作的数据的完整性处于一致性的状态,事务结束后,被操作的数据的完整性也必须处于一致性状态。比如A向B转账,不可能A扣了钱,B却没收到(这就是非一致性)。
- 隔离性(Isolation):一个事务的执行不能被其他事务所影响。比如A和C同时向B转账,那B同一时间只能和一个人交易(同时只能有一个交易在执行)。
- 持久性(Durability):一个事务一旦提交,事物的操作便永久性的保存在DB中。即使此时再执行回滚操作也不能撤消所做的更改。A向B转账,成功以后B的账户就存入了转账数额,在不做其他的操作前提下数据是永久性的。
如果某个系统事务是的原子性和一致性不能保证,就会遇到主从不一(主更新或生成了,子没更新或生成)、库存为负数、金额为负数等。一个复杂的业务系统,一旦事务失控,那这个系统必然是不可靠的。
1.2 Hibernate事务
*Spring Data JPA它的底层是用Hibernate实现的,Spring Data JPA事务即为Hibernate事务。Hibernate是对JDBC的轻量级对象封装,Hibernate本身是不具备事务处理功能的,Hibernate的事务实际上是底层的JDBC事务的封装。先贴一段代码:
public synchronized void receiveMessage(String _message) {
if(sessionFactory==null){
sessionFactory=(SessionFactory) SpringBeanUtil.getBeanFromSpringByBeanName("sessionFactory");
}
session = sessionFactory.openSession();
te = session.getTransaction();
te.begin();
try {
//保存数据
.
.
fqsArrXmlHome.persist();
te.commit();
} catch (RuntimeException re) {
if(te != null){
if(session!=null){
if(session.isOpen()){
te.rollback();
}
}
}
re.printStackTrace();
throw re;
}finally {
if(session!=null){
if(session.isOpen()){
session.close();
}
}
}
}
这是我们很久以前项目中的一段代码,写法和JDBC或者数据库的存储过程套路是差不多的,基本流程就是开会话,执行,如果有异常就回滚,最后关闭会话。这种写法有两大弊端:一、代码量大,而且还是重复的代码,二、如果忘记关闭会话,随着连接数的增多,可能会造成连接池溢出,系统假死等情况。那我们有没有什么更简洁的方法或工具去管理Hibernate的事务呢?答案就是Spring。
二、Spring管理Hibernate事务
Spring以非侵入式方式对Hibernate事务进行管理。声明式事务管理使业务代码不受污染,只要加上注解就可以获得完全的事务支持。
疑问1:我们前几篇中,好像没有看到在哪里声明了事务,但是确能将数据持久化到数据库中去,是怎么实现的?
答:上图是JpaRepository接口实现类,我们可以看到在方法上加了@Transactional注解,加了此注解就开启了声明式事务,也就是可以让Spring去帮我们管理Hibernate的事务了,所以在使用data jpa操作数据库时,即使我们没有在Service层上开启事务也能保存数据。
疑问2:以前我们搭建(SSH)项目,需要在配置文件中配置事务管理器TransactionManager后@Transactional注解生效,但是我们的Spring Boot项目好像直接使用就可以了,是怎么实现的呢?
答:Spring Boot就是简化配置的,在引用 spring-boot-starter-data-jpa,使用data-jpa作为数据库访问技术的时候,就自动帮我们配置好了,引入starter-data-jpa依赖后直接使用@Transactional即可。
下面我们进行代码实现: