Spring框架深入(三)--事务
一、事务的概念
1、事务是什么
(1)、作为单个逻辑工作单元执行的一系列操作(一组SQL会成为一个事务),是并发控制的单位,要么全部成功,要么全部失败
(2)、如银行转账(需要两个update)/网上商城购物
2、事务的特征
(1)、原子性:所有的操作会被看成一个逻辑单元,要么全部成功,要么全部失败
(2)、一致性:事务在完成时,必须使所有的数据都保持一致状态,保证数据的完整性
(3)、隔离性:与并发有关,事务之间的相互影响—隔离级别
(4)、持久性:事务结束后,结果是可以固化的
二、事务隔离
1、事务隔离用于处理事务之间的并发问题
2、事务的隔离级别
(1)、未授权读取
(2)、授权读取
(3)、可重复读取
(4)、序列化:隔离级别最高的
3、事务隔离的实现:
(1)、悲观锁:基于数据库的锁,不能操作数据
a、悲观锁是读取的时候为后面的更新加锁,之后再来的读操作都会等待。这种是数据库锁。
b、悲观锁是数据库实现,他阻止一切数据库操作。
c、在实际生产环境里边,如果并发量不大且不允许脏读,可以使用悲观锁解决并发问题;
(2)、乐观锁:不同的事务可以看到同一对象的不同历史版本
a、乐观锁是一种思想,是基于版本号机制的,具体实现是,表中有一个版本字段,第一次读的时候,获取到这个字段。处理完业务逻辑开始更新的时候,需要再次查看该字段的值是否和第一次的一样。如果一样更新,反之拒绝。之所以叫乐观,因为这个模式没有从数据库加锁。
b、乐观锁适用于多读的应用类型,这样可以提高吞吐量;
c、在实际生产环境中,如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以我们就要选择使用乐观锁。
三、B/S中的事务
1、一个请求对应一个业务,一个业务其实就应该是一个事务
2、一个请求对应一个事务
3、一个事务----MyBatis中的事务与sqlSession相关
4、一个请求对应着启动一个线程,一个线程对应一个事务
(1)、当前请求线程所执行的所有操作都是属于同一个事务的,使用的是同一个sqlSession
(2)、dao的所有操作应该是基于同一个sqlSession,这些操作才构成一个事务;
5、一个线程对应着同一个sqlSession:
如何让一个线程中得到的sqlSession对象是同一个呢?使用ThreadLocal
6、ThreadLocal
(1)、当前线程变量:key/value
(2)、将sqlSession放入线程上下文空间,线程会执行请求要做的所有方法(很多个dao操作),每次的dao操作所使用的sqlSession都从当前线程上下文取得;
(3)、使用原理/步骤:
a、每个dao在执行的时候,会使用getSqlSession来获得会话
b、判断当前线程中是否有session,如果有的话,就用当前线程的session。如果没有的话,就会创建session放入当前线程,同时返回session;
c、继续执行下一个dao操作的时候,因为是属于同一个请求线程的,所以可以从当前线程里拿到同一个session,从而形成事务的概念。
/* * 当前线程上下文空间 * 将session放入当前线程变量中 * 如果当前线程有该对象,就直接拿来用 * 如果没有,才去新建对象 * */ public class SessionFactoryUtil { private static ThreadLocal threadLocal = new ThreadLocal(); public static SqlSession getSqlSession() { SqlSession session = (SqlSession) threadLocal.get(); if(session!=null) { return session; } else{ session=new SqlSession(); threadLocal.set(session); return session; } } }
public void run() { //这两个dao操作时属于同一个事务的,也就是dao操作所使用的sqlSession是同一个 userDao.buy(); productDao.updatePruduct(); }
7、B/S中要实现事务,需要将sqlSession放入ThreadLocal(当前线程上下文)中
通过servletFilter实现请求到达的时候,创建session放入ThreadLocal
四、Spring中的事务
1、事务其实是一个切面的存在,只需要在Spring中配置AOP就可以实现事务了
2、AOP
(1)、核心业务:自己编写
(2)、切面:事务这个切面Spring已经提供了实现:不需要自己编写
a、Spring已经提供了实现事务的通知,配置为bean
b、事务管理平台:确定事务切面用在哪个平台
c、事务策略定义:事务的属性(隔离级别、传播性)
d、事务状态
3、在Spring中实现事务:通过配置AOP实现
(1)、将Spring提供的通知类配置到核心业务线
(2)、基于注解进行配置
(3)、基于AOP的配置文件进行配置
4、PlatformTransactionManager
确定要做的事务是基于哪个平台(JDBC、Hibernate、MyBatis、JPA)
五、Spring+MyBatis事务管理
1、Spring+MyBatis集成后,默认每个操作都是产生一个新的SqlSession,不构成事务概念,每个操作就是一个独立的事务
2、事务都是基于service层
3、Spring中事务AOP的配置
(1)、首先,我们这儿有一个Author类(实现类get,set方法),以及AuthorMapper接口
public class Author { private int id; private String username; private String password; private String email; private String bio; }
public interface AuthorMapper { public Author findById(int id); public void insertAuthor(Author aut); }
接口中有两个方法,分别是查找所有和插入
如果我们就这样运行,他就会创建两个SqlSession分别处理两个方法;
(3)、在核心配置文件中进行事务AOP的配置
<!-- AOP 事务配置 核心业务:service——已完成 切面bean:Spring提供支持类,进行配置 建立切入点aop关联,进行配置 --> <!-- 配置事务管理器:切面的一部分 --> <bean id="transManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置事务策略状态:切面的一部分 事务通知的配置 --> <tx:advice id="txAdvice" transaction-manager="transManager"> <tx:attributes> <!-- 对应的方法与事务的使用 REQUIRED:在事务中执行,如果事务不存在,则会重新创建一个 SUPPORTS:使用当前的环境执行,如果当前存在事务,则会使用这个事务,如果当前没有这个事务,则不使用事务 --> <tx:method name="add*" propagation="REQUIRED"/> <tx:method name="insert*" propagation="REQUIRED"/> <tx:method name="del*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="find*" propagation="SUPPORTS" read-only="true"/> <tx:method name="get*" propagation="SUPPORTS" read-only="true"/> <tx:method name="select*" propagation="SUPPORTS" read-only="true"/> </tx:attributes> </tx:advice> <!-- 核心业务与事务关联起来 --> <aop:config> <!-- 切入点 --> <aop:pointcut expression="execution(* service.*.*.*(..))" id="transPointCut"/> <!-- 关联操作 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="transPointCut"/> </aop:config>
如果是对于hibernate而言,到这儿就已经可以实现事务了。但是,对于MyBatis而言,还需要继续进行配置,有两种配置方法;
a、基于注解实现事务
<!-- 配置注解驱动,标注@Transactional的类和方法都具有事务性 --> <tx:annotation-driven transaction-manager="transManager"/>
只需要在信心配置文件中配置注解驱动,并且在实现类上加上@Transactional就可以了;
@Transactional @Override public void doFindAndInsert() { Author aut=new Author(); aut.setBio("test"); aut.setEmail("test"); aut.setPassword("test"); aut.setUsername("test"); authorMapper.insertAuthor(aut); //报错的时候,回滚 int i=5/0; Author author = authorMapper.findById(1); System.out.println(author); return null; }
运行结果:
b、基于事务管理模板实现事务
配置一个支持事务的模板bean
<!-- 基于事务模板 用于支持事务的模板bean --> <bean id="txTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <constructor-arg type="org.springframework.transaction.PlatformTransactionManager" ref="transManager"></constructor-arg> </bean>
实现类:
public void doFindAndInsert() { txTemplate.execute(new TransactionCallback<Integer>() { @Override public Integer doInTransaction(TransactionStatus arg0) { // TODO Auto-generated method stub Author aut=new Author(); aut.setBio("test"); aut.setEmail("test"); aut.setPassword("test"); aut.setUsername("test"); authorMapper.insertAuthor(aut); //报错的时候,回滚 int i=5/0; Author author = authorMapper.findById(1); System.out.println(author); return null; } }); }
运行结果:
PS:因作者能力有限,如有误还请谅解;