一、事务简介
事务就是一组由于逻辑上紧密关联而合并成一个整体(工作单元)的多个数据库操作,这些操作要么都成功执行,要么都执行失败。
在JavaEE企业级开发的应用领域,为了保证数据的完整性和一致性,必须引入数据库事务的概念,所以事务管理是企业级应用程序开发中必不可少的技术。
二、事务的特性
1、原子性(atomicity):“原子”的本意是“不可再分”,事务的原子性表现为一个事务中涉及到的多个操作在逻辑上缺一不可。事务的原子性要求事务中的所有操作要么都执行,要么都不执行
。
2、一致性(consistency):“一致”指的是数据的一致,具体是指:所有数据都处于满足业务规则的一致性状态。一致性原则要求:一个事务中不管涉及到多少个操作,都必须保证事务执行之前数据是正确的,事务执行之后数据仍然是正确的。如果一个事务在执行的过程中,其中某一个或某几个操作失败了,则必须将其他所有操作撤销,将数据恢复到事务执行之前的状态,这就是回滚。
3、隔离性(isolation):在应用程序实际运行过程中,事务往往是并发执行的,所以很有可能有许多事务同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。隔离性原则要求多个事务在并发执行过程中不会互相干扰
。
4、持久性(durability):持久性原则要求事务执行完成后,对数据的修改永久的保存
下来,不会因各种系统错误或其他意外情况而受到影响。通常情况下,事务对数据的修改应该被写入到持久化存储器中。
三、事务处理逻辑
四、事务管理的方式
声明式事务管理最大的优点在于开发者无需通过编程的方式来管理事务,只需在配置文件中进行相关的事务规则声明,就可以将事务应用到业务逻辑中。这使得开发人员可以更加专注于核心业务逻辑代码的编写,在一定程度上减少了工作量,提高了开发效率,所以在实际开发中,通常都推荐使用声明式事务管理。
4.1、声明式事务管理(基于AOP)
Spring的声明式事务管理可以通过两种方式来实现,一种是基于XML的方式,另一种是基于Annotation的方式
。接下来将对这两种声明式事务管理方式进行介绍。
4.2、注册事务管理器
PlatformTransactionManager接口
是代表事务管理的接口,具体如何管理事务则由它的实现类来完成。该接口常见的几个实现类如下
小提示:当底层采用不同的持久层技术时,系统只需使用不同的PlatformTransactionManager实现类即可。
4.3、基于Annotation的声明式事务管理
五、实例:模拟商店售货功能
5.1、使用的数据库表信息
5.2、创建动态web工程
5.3、创建member的bean
创建dao接口ShopDao.java
dao的实现ShopDaoImpl.java;使用到两个注解@Repository和@Autowired
package com.dgut.spring.jdbc.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.dgut.spring.jdbc.exception.BalanceNotEnoughException;
import com.dgut.spring.jdbc.exception.QuantityNotEnoughException;
@Repository
public class ShopDaoImpl implements ShopDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public int getGoodsPriceById(int goodsId) {
String selectSql = "select price from tbl_goods where goods_id=?";
Integer price = jdbcTemplate.queryForObject(selectSql, Integer.class, goodsId);
return price;
}
@Override
public void decreaseGoodsQuantity(int goodsId) {
String updateSql = "update tbl_goods set quantity = quantity-1 where goods_id=?";
jdbcTemplate.update(updateSql, goodsId);
// 查询商品数量减一后的数量
String selectSql = "select quantity from tbl_goods where goods_id=?";
Integer quantity = jdbcTemplate.queryForObject(selectSql, Integer.class, goodsId);
if (quantity < 0) {
//抛出异常
throw new QuantityNotEnoughException("Quantity is Not Enough !");
}
}
@Override
public void updateBalance(int memberId, int charge) {
String updateSql = "update tbl_member set balance = balance-? where member_id=?";
jdbcTemplate.update(updateSql, charge, memberId);
// 查询member数量减一后的数量
String selectSql = "select balance from tbl_member where member_Id=?";
Integer balance = jdbcTemplate.queryForObject(selectSql, Integer.class, memberId);
if (balance < 0) {
//抛出异常
throw new BalanceNotEnoughException("Balance is Not Enough !");
}
}
}
5.4、自定义异常
5.5、创建service:使用到三个注解:@Service,@Autowired,@Transactional
事务管理一般加载service层
5.6、配置applicationContext.xml
5.7、创建测试类
六、声明式事务配置管理参数配置
使用@Transactional注解时,可以通过参数配置事务属性:
6.1、事务传播属性(propagation)属性
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。
事务传播属性可以在@Transactional注解的propagation属性中定义
传播属性 | 描述 |
---|---|
REQUIIRED | 如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行 |
REQUIIRED_NEW | 当前的方法必须启动新事务,并在它自己的事务内运行.如果有事务正在运行,应该将它挂起 |
SUPPORTS | 如果有事务在运行,当前的方法就在这个事务内运行.否则它可以不运行在事务中 |
NOT_SUPPORTE | 当前的方法不应该运行在事务中.如果有运行的事务,将它挂起 |
MANDATORY | 当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常 |
NEVER | 当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常 |
NESTED | 如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行.否则,就启动一个新的事务,并在它自己的事务内运行. |
就是一个方法调用另一个方法产生的行为
6.2、事务隔离级别(isolation)属性
假设现在有两个事务:Transaction01和Transaction02并发执行。
1、脏读:一个未提交事务读取到另一个未提交事务的数据,
①Transaction01将某条记录的AGE值从20修改为30。
②Transaction02读取了Transaction01更新后的值:30。
③Transaction01回滚,AGE值恢复到了20。
④Transaction02读取到的30就是一个无效的值。
2、不可重复读:一个未提交事务读取到另一提交事务修改数搪
①Transaction01读取了AGE值为20。
②Transaction02将AGE值修改为30。
③Transaction01再次读取AGE值为30,和第一次读取不一致。
3、幻读:一个未提交事务读取到另一提交事务添加数据。
①Transaction01读取了STUDENT表中的一部分数据。
②Transaction02向STUDENT表中插入了新的行。
③Transaction01读取了STUDENT表时,多出了一些行。
通过事务的隔离级别就能解决上面的三个问题
6.3、事务超时时间(timeout)属性
timeout:超时时间。
(1)事务需要在一定时间内进行提交,如果不提交进行回滚(2〉默认值是-1,设置时间以秒单位进行计算。
6.4、事务是否只读(readOnly)属性
readOnly:是否只读·
(1)读:查询操作,写:添加修改别除操作w
(2) readOnly默认值 false,表示可以查询,可以添加修改删除操f(3)设置readOnly值是true,设置成true之后,只能查询,
6.5、事务回滚(rollbackFor)属性
rollbackFor:回滚,
设置出现哪些异常进行事务回滚。
6.6、事务不回滚(noRollbackFor)属性
noRollbackFor:不回滚
设置出现哪些异常不进行事务回滚,
八、基于XML配置文件的声明式事务管理
1、在spring配置文件中进行配置
第一步配置事务管理器
第二步配置通知
第三步配置切入点和切面