开发工具与关键技术: Java
作者:肖广斌
撰写时间:2021年6月6日
事务是什么呐?
事务(Transaction),一般是指要做的或所做的事情。事务是访问数据库的一个操作序列,数据库应用系统通过事务集来完成对数据库的存取。事务的正确执行使得数据库从一种状态转换成另一种状态。
为什么要用事务?
事务是为解决数据安全操作提出的,事务控制实际上就是控制数据的安全访问。
举一个简单点的例子:就是支付宝转账业务,A账号要给B账号转200块,A账号少200,B账号要加200,假如转账过程中网络出现错误,A账号少了200块,而B账号没有加上200块,那这个业务就是操作失败,那就得控制它,总不能B账号没有收到A账号转的帐,A账号的钱还少了吧。A账号转帐业务撤销。这才能保证业务的正确性。将A账号资金减少和B账号资金增加放到同一个事务里,要么全部执行成功,要么全部撤销,这样就保证了数据的安全性。简单的说就是转账要么一起成功,要么一起失败。
事务的4个特性(ACID):
1、 原子性(atomicity):表示事务执行过程中的任何失败都将导致事务所做的任何修改失效,就是要么全部执行,要么全部不执行。
2、 一致性(consistency):表示当事务执行失败时,所有被该事务影响的数据都应该恢复到事务执行前的状态。
3、 隔离性(isolation):一个事务的执行不能被其他事务所影响。
4、 持久性(durability):表示已提交的数据在事务执行失败时,数据的状态都应该正确。
Java事务的类型有三种:JDBC事务、JTA(Java Transaction API)事务、容器事务。
事务并发处理可能引起的问题:
1、脏读(dirty read):一个事务读取了另一个事务尚未提交的数据。
2、不可重复读(non-repeatable read):一个事务的操作导致另一个事务前后两次读取到不同的数据。
3、幻读(phantom read):一个事务的操作导致另一个事务前后两次查询的结果数据量不同。
例子1:A、B事务并发执行时,A事务修改后,B事务读取到了A事务未提交的数据,此时A数据已经rollback回滚,则B事务读取到了A事务无效的“脏”数据。
例子2:当A事务读取数据后,B事务修改了A事务读取到的数据,这时A事务再去读取该数据,发现两次数据不一样。
例子3:当A事务查询数据后,B事务新增或删除了一条满足A事务查询的数据,此时A事务再去查询数据,发现查询到之前不存在的数据记录,或者之前的数据记录不存在了。
事务的七种传播行为:
1、PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是默认的事务传播行为。
2、PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
3、PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
4、PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,延缓当前事务。
5、PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务暂停。
6、PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
7、PROPAGATION_NESTED:如果没有,就新建一个事务;如果有,就在当前事务中嵌套其他事务。
事务的隔离级别:
1、ISOLATION_DEFAULT:这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。
2、ISOLATION_READ_UNCOMMITTED(读取未提交内容):这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
3、ISOLATION_READ_COMMITTED(读取提交内容):保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻读。
4、ISOLATION_REPEATABLE_READ(可重读):这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免不可重复读。
5、ISOLATION_SERIALIZABLE(可串行化):这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻读。
这里用到的是JDBC事务,在项目中我们使用事务之前,需要配置一些必需的配置文件,这是在spring-mybatis.xml里配置的,还有一种使用注解事务的配置,看个人所需,这里就不配置了
<!-- 事务管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 使用annotation定义事务 -->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
在控制器的方法中开启事务
@ResponseBody
@RequestMapping(value = "/addStudents", produces = "application/json; charset=utf-8")
public JsonReturn addStudents(Students students,HttpServletRequest request)throws Exception{
JsonReturn jsonReturn = new JsonReturn();
AccountantRecord accountantRecord = new AccountantRecord();
//获取Spring容器的对象
WebApplicationContext contextLoader = ContextLoader.getCurrentWebApplicationContext();
//设置属性的默认属性
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
//设置事务的传播行为,此处是设置为开启一个新事务
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
//设置事务的隔离级别,此处是读已经提交
definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
//从spring容器对象中获取DataSourceTransactionManager,这个根据配置文件中配置的id名(transactionManager)
DataSourceTransactionManager transactionManager = (DataSourceTransactionManager) contextLoader.getBean("transactionManager");
//获取事务状态对象
TransactionStatus transactionStatus = (TransactionStatus) transactionManager.getTransaction(definition);
try {
//要执行的方法
int intS = StudentsMapper.addStudents(students);
if (intS>0){
StudentsMapper.addAccountantRecord(accountantRecord);//新增第二张表
}
//提交事务
transactionManager.commit(transactionStatus);
}catch (Exception e){
//回滚事务
transactionManager.rollback(transactionStatus);
jsonReturn.setMsg("操作失败");
}
return jsonReturn;
}
如果开启事务之后,同时新增多表发生异常,就会回滚事务,如下图:
以上为个人理解+结合资料所整理,如有错误,欢迎指正!!!