一.事物的概念?
事物可以看做是由对数据库的若干操作组成的一个单元
事物是一系列的动作,一旦其中有一个动作出现错误,必须全部回滚,提供将事物中对数据库的所有已完成的操作全部撤销,滚回到事物开始的状态,避免出现由于数据不一致而导致接下来的一系列错误.
事物的出现是为了确保数据的完整性和一致性,确保用户的每一个操作都是可靠的,事物中的每一步操作都必须执行,只要有发生异常就回退到事物未开始的操作状态,这些事物要么都执行,要么都取消,从而保证数据满足原子性,一致性.在目前的企业开发中,事物的管理是必不可少的.
二.事物的四大特性
事物具有四大特性: 原子性,一致性,隔离性,持久性
-
原子性(Atomicity): 事物是一个原子操作,由于一系列的动作组成.事物的原子性确保动作要么全部完成,要么完全不起作用
-
一致性(Consistency): 事物在完成时,必须是所有的数据都保持一致
-
隔离性(Isolation): 并发事物执行之间无影响.在一个事物内部的操作对其他事物是不产生影响的,这需要事物隔离级别来指定隔离性
-
持久性(Durability): 一旦事物完成,数据库的改变必须是持久化的
三.Spring事物管理
Spring事物管理分为两种形式: 编程式事物和声明式事物
在进行事物管理之前我们首先要进行配置,Spring针对不同的dao框架,提供了不同的实现类.Jdbc,mybatis 事物管理实现类是 DataSourceTransactionManager.
配置事物管理器:
<!-- 配置 spring 事务管理类, 并注入数据源 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
编程式事物:
编程式事物管理在代码中很少使用,这种方式需要注入一个事务管理对象TransactionTemplate ,然后在我们代码中需要提交事务或回滚事务时自己写代码实现.此处我们不再进一步了解.
声明式事物:
声明式事务管理建立在AOP的基础上,其本质是对方法前后进行拦截,所以声明式事物管理是方法级别的.
Spring声明式事物管理有两种方式:
基于xml文件实现和基于注解方式实现,此处我们使用以基于注解方式实现
1.首先我们要开启注解事务管理:
<!-- 开启注解事务管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
2.在 service 中控制事务:
@Service
@Transactional
public class AdminService {
}
这样我们就开启了Spring事物管理,下面我们举一个简单的栗子更为深刻的理解事务管理:
我们在这里设计一个简单的转账业务进行示例
实现原理:
我们以之前创建的数据库中admin表的password作为用户的金额,设置两个方法模拟一个账户中金额增加功能,一个账户中金额减少功能.在我们开启事物管理功能后,这两个事物就应该满足事物的原子性,要么同时执行了数据库操作,要么都没有执行.我们首先调用sub()方法模拟一个账户中金额减少,在调用add()方法模拟另一个账户增加相应的金额.以运行期异常模拟实际操作过程中可能出现的异常情况,这样Spring事物管理就会识别到这个异常,为了确保安全及保证原子性,就不会向数据库提交事物,并且将已执行代码进行回滚.回到调用之前的状态.
如果我们开启事务管理,并且它有效的执行,那么当我们的sub()方法中出现了运行期异常,或者sub()方法调用之后,add()方法未调用之前出现了运行期异常,那么Spring事务管理就不会向数据库提交事物,即数据库中两个账户的金额都保持不变.这样就体现出了Spring的事务管理功能.
要是没有开启事务管理或者事务管理功能失效,那么当我们执行完sub()方法后就会向数据库提交事物,即使在add()方法未执行之前出现了异常情况,由于已经提交了事务,此账户中的金额还是会减少;而add()方法无法正常执行,因此账户中的金额没有增加,这样就出现了问题.
代码实现:
1.在AdminDao中编写两个方法add()和sub()模拟增加金额和减少金额事务
package com.ffyc.springdemo.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class AdminDao {
@Autowired
JdbcTemplate jdbcTemplate;
public void add(){
jdbcTemplate.update("update admin set password = password +300 where id = 2");
}
public void sub(){
jdbcTemplate.update("update admin set password = password -300 where id = 3");
}
}
2.在AdminService中开启事物管理,并调用增加,减少金额的方法.
package com.ffyc.springdemo.service;
import com.ffyc.springdemo.dao.AdminDao;
import com.ffyc.springdemo.model.Admin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
public class AdminService {
@Autowired
AdminDao adminDao;
public void zhuanZhang(){
adminDao.sub();
System.out.println(10/0); //此处模拟出现一个运行期异常
adminDao.add();
}
}
3.在测试类中调用:
package com.ffyc.springdemo.test;
import com.ffyc.springdemo.service.AdminService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test1 {
@Test
public void test() {
ApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
AdminService adminService = app.getBean("adminService", AdminService.class);
adminService.zhuanZhang();
}
}
4.运行结果分析:
由于sub()方法之后出现了运行时异常,Spring事务管理功能就不会向数据库提交事务,因此此时两个账户中金额应该均保持不变,经核实亦是如此,说明Spring事务管理功能正常运行.