spring的事务管理介绍

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qiyongkang520/article/details/79356228

一、目前的Transaction的误区

有写操作就开启事务
数据连接泄露
数据库连接超时

二、传统的Java事务管理

大家脑海里事务是不是一个一组明确的sql,在最开始来个begin,在最后面来个commit。
就好像一堆sql,统一交给db去执行一样。

实际情况不是这样的。
Java的JDBC驱动是这么干的:
Connection conn = ConnectionFacotry.getConnection(); #创建一个tcp/ip到db的长连接,db要生成一个回话对象,专门接受请求。
conn.setAutoCommit(false); #告诉db,执行sql的结果(生成临时记录)放到缓冲区里,别提交。必要的话给db会给临时记录映射的物理记录上锁。
PreparedStatement ps = conn.getPreparedStatement(sql); #sql语句封装,不解释。
ps.executeUpdate(); #把update sql发给了db,db执行了,产生了临时数据,但并没有刷到物理数据上去。
conn.executeSql(selectSql) ; #用这个conn对象执行select语句的话,可以查到刚刚update的临时数据;
#但如果另一个conn对象试图执行同样语句,查不到的。事务缓存是一个conn对象独立一份,不共享。
conn.commit(); #把commit指令发给db,db会将临时数据“覆盖”到物理数据上,并放开物理数据的锁。
conn.close(); #把close指令发给db,db会清掉回话对象和事务缓存;同时java本地对象也打上了关闭标志。
这个过程中,java代码必须使用同一个conn对象,既保证一直访问同一个db端的回话对象,因为只有这样,才能正确对接db端的事务缓存(这个事务缓存在db的内存里, 而不是放在jdbc客户端)。
规范代码中,会用try catch 包裹他们。仅仅是为了发生异常的时候,为的是能及时通知db关闭或回滚回话对象,免得db傻等浪费资源而已。
一般db服务端会设定一个超时,jdbc也会设定一个超时,只要超过这个时间两方不交互就主动断开连接,避免异常情况下的db资源泄漏。

三、Java事务管理的特性

首先要管理Conn对象

conn对象是java和db两端创建的,只有对应关系唯一,才能正确访问Db端的事务缓冲区。
Java端的conn对象一般是由Datasource创建的,需要靠应用程序自己维护conn对象与线程之间的关系。
一个开启了事务的Conn对象,在commit之前,只能让一个线程使用,不能给其他线程使用(Datasouce一般支持这种独占标记)。
一个没有开启事务的Conn对象,可以共享给多个线程使用。
Conn对象除了超时以外,不会调用close方法。

其次才是事务处理本身

conn.setAutoCommit(false);的时机,相当于事务开启。
被设计到一个事务的处理,只要在conn开启事务到提交事务之间,共享同一个conn对象处理sql,就能实现事务嵌套。
conn.commit();的时机

四、Java事务管理的陷阱

连接池泄露

根据特性,一个线程专用一个conn。
当线程处理时间超长,conn就会长时间不释放。
这样的线程增长较多时,Datasouce能占用的最大conn很快就会到达。
新的线程到达时,只能等待Datasouce获得新的conn。
服务器假死,一般得重启服务才能解决。

连接池浪费

非事务性请求也被当做事务请求处理。由于jdbc无法预知将要处理的请求,如果开启了事务,即便处理的是select,也会独占Conn对象。
非事务性请求,本身无需独占Conn对象,但由于被放在事务请求的调用链里,加长了事务性请求的占用Conn时间。

五、Spring处理事务的特性与陷阱

Spring完整的实现了特性,也完整带过来了陷阱。
我们需要了解spring的原理,依靠spring简化我们编程的同事,设计绕开陷阱。

六、Spring Mybatis联合事务定义

Mybatis的sqlConnectionFactory可以识别被TransactionManager管理起来的Datasouce,并做了一些封装。
TransactionManager根据Annotation或者xml配置的语法,为Service、Manager、Dao的方法配置线程安全的Tm对象,这些对象被称作“切点”。
Mybatis的Mapper在执行sql之前,会查看当前线程的TransactionManager对象(以下简称Tm对象),选择性的复用同一个线程内的Conn对象。
如果Tm状态是Required,则优先共享Tm提供的Conn对象(这个对象已被当前线程独占),出了异常则会在共享Tm上标记回滚,没有则新独占一个Conn对象。
如果Tm状态是RequiredNew,则立即新独占一个Conn对象,无视Tm提供的Conn对象。
如果Tm状态是NotSupporded,则立即获取一个自由的Conn对象(非独占性的),无视Tm提供的Conn对象,。
如果Tm状态是Never,则校验Tm是否获取的是自由Conn,如果是独占Conn的就抛异常(强制无事务)。
Tm的状态可以从“切点”开始,从Service一直传递到Dao,直到撞上另一个“切点”才会产生上述判断。
Tm在“切点”创建的事务Conn,会在该切点方法return后执行commit或者rollback,如果调用链上层还有切点,则只是标记或者无视(依照Tm状态而定)。

七、Spring Mybatis事务应用与优化

我们要做的,只是为Service或Manager的方法搭配合适的Tm对象。
在Required和NotSupporded里选一个吧,其他几种很难用到。
NotSupporded使用非独占Conn,性能和陷阱是最少的。
Required的Tm,除非业务需要,否则我们要保证这个方法尽快结束。
碰上大批量处理,则应提前拆分,外层用一个NotSupporded的方法控制分片,内层再循环调用Required(或RequiredNew)批量提交,缩小一次提交的内容,避免Conn因超时而提前断开或Rollback。
尽可能不使用Required方法处理一个大型的调用链,因为在这个调用执行过程中,外层方法始终要独占Conn,(试想这个调用链内部有个方法,会访问一个网络不稳定的url…..)
如果理解了上述描述,可能会觉得,使用正则式匹配方法名的Tm管理,有些粗放。@Transactional才是更好的选择。

猜你喜欢

转载自blog.csdn.net/qiyongkang520/article/details/79356228
今日推荐