Mybatis事务
概述
事务管理器(transactionManager)
在 MyBatis 中有两种类型的事务管理器(也就是 type=”[JDBC|MANAGED]”):
JDBC – 这个配置就是直接使用了 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
MANAGED – 这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置为 false 来阻止它默认的关闭行为。例如:
<transactionManager type="MANAGED"> <property name="closeConnection" value="false"/> </transactionManager>
提示如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器, 因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
这两种事务管理器类型都不需要任何属性。它们不过是类型别名,换句话说,你可以使用 TransactionFactory 接口的实现类的完全限定名或类型别名代替它们。
public interface TransactionFactory {
void setProperties(Properties props);
Transaction newTransaction(Connection conn);
Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}任何在 XML 中配置的属性在实例化之后将会被传递给 setProperties() 方法。你也需要创建一个 Transaction 接口的实现类,这个接口也很简单:
public interface Transaction {
Connection getConnection() throws SQLException;
void commit() throws SQLException;
void rollback() throws SQLException;
void close() throws SQLException;
Integer getTimeout() throws SQLException;
}使用这两个接口,你可以完全自定义 MyBatis 对事务的处理。
MyBatis事务遇到的问题
如果开启MyBatis事务管理,则需要手动进行事务提交,否则事务会回滚到原状态;
如果在具体操作执行完后不通过sqlSession.commit()方法提交事务,事务在sqlSession关闭时会自动回滚到原状态;只有执行了commit()事务提交方法才会真正完成操作;
如果不执行sqlSession.commit()操作,直接执行sqlSession.close(),则会在close()中进行事务回滚;
如果不执行sqlSession.commit()操作也不手动关闭sqlSession,在程序结束时关闭数据库连接时会进行事务回滚;
事务管理入口
在XML配置文件中定义事务工厂类型,JDBC或者MANAGED分别对应JdbcTransactionFactory.class和ManagedTransactionFactory.class;
如果type=”JDBC”则使用JdbcTransactionFactory事务工厂则MyBatis独立管理事务,直接使用JDK提供的JDBC来管理事务的各个环节:提交、回滚、关闭等操作;
如果type=”MANAGED”则使用ManagedTransactionFactory事务工厂则MyBatis不在ORM层管理事务而是将事务管理托付给其他框架,如Spring
事务对UPDATE操作的影响
事务的提交
在sqlSession中执行了UPDATE操作,需要执行sqlSession.commit()方法提交事务,不然在连接关闭时候会自动回滚;
exector.commit()事务提交方法归根到底是调用了transaction.commit()事务的提交方法;这里的transaction就是根据配置对应的JdbcTransaction或者ManagedTransaction;
如果是JdbcTransaction的commit()方法,通过调用connection.commit()方法通过数据库连接实现事务提交;
如果是ManagedTransaction的commit()方法,则为空方法不进行任何操作;
事务的回滚
sqlSession执行close()关闭操作时,如果close()操作之前进行了UPDATE操作未进行commit()事务提交则会进行事务回滚然后再关闭会话;如果update后执行了commit则直接关闭会话;
在DefaultSqlSession类中如果执行了UPDATE操作则会将标志位dirty赋值为true
在事务提交时会将dirty赋值为false;
在关闭会话时会判断dirty是否为true,如果为true则需要进行事务回滚操作,否则直接关闭会话
判断dirty标志是否为true
如果dirty为true则判定forceRollback为true,执行回滚操作;
事务对SELECT的影响
事务对select操作的影响主要体现在对缓存的影响上,主要包括一级缓存和二级缓存
一级缓存
因为一级缓存是Session级别的,事务的提交回滚对MyBatis的一级缓存没有影响;一级缓存放在BaseExector中的PerpectualCache类型的localCache中;
二级缓存
在Mapper.xml文件解析时会根据文件中的标签或者创建Cache实例,并将该实例放入每一个MappedStatement中,在MappedStatement执行select操作时候会获取该cache;
查询二级缓存
根据Statement所在的Mapper的cache缓存对象和根据statement生成的cacheKey,从正式缓存中取缓存数据
根据Statement所在的Mapper的cache缓存对象在TransactionManager中定位对应的TransactionCache,TransactionCache中保存这正式缓存delegate和临时未提交缓存entiryToAddOnCommit;
获取缓存时直接从正式缓存delegate中查询
存储二级缓存
如果从二级缓存中未命中缓存,则需要从数据库中查取,再将查询结果放入二级缓存中;查询结果首先放入二级缓存临时缓存中,只有执行了commit()事务提交才正式转移到正式缓存中;也就是说只有执行了commit()方法的缓存才被下次查询使用,不然仍会执行数据库查询任务并覆盖上次的临时缓存;
根据Statement所在的Mapper的cache缓存对象在TransactionManager中定位对应的TransactionCache
将数据放入TransactionCache中的临时缓存entiryToAddOnCommit中
提交二级缓存
执行事务提交时候会调用TransactionCacheManager内的commit()方法提交缓存,每一个cachingExecutor对应一个TransactionCacheManager,也就是一个SqlSeesion对应一个TransactionCacheManager,因此sqlSession.commit()是提交了当前会话的所以二级缓存的临时缓存;每个sqlSession对每一个Mapper的cache都有一个临时缓存,多个sqlSession共享一个Mapper的cache的正式缓存;
将临时缓存entiryToAddOnCommit中的数据转移到正式缓存delegate中
回滚二级缓存
如果之前未执行UPDATE操作,dirty标志为false,在关闭会话之前没有进行事务提交则进行数据库回滚和缓存提交;
如果执行了UPDATE操作,dirty标志为true,在关闭会话之前没有进行事务提交则进行数据库回滚和缓存回滚;
可以调用session.close(true)关闭会话时强行回滚缓存;
总结
MyBatis可以通过XML配置是否独立处理事务,可以选择不单独处理事务,将事务托管给其他上层框架如spring等;
如果MyBatis选择处理事务则事务会对数据库操作产生影响
对UPDATE操作的影响主要表现在UPDATE操作后如果没有进行事务提交则会子啊会话关闭时进行数据库回滚;
对SELECT操作的影响主要表现在二级缓存上,执行SELECT操作后如果未进行事务提交则缓存会被放在临时缓存中,后续的SELECT操作无法使用该缓存,直到进行commit()事务提交,临时缓存中的数据才会被转移到正式缓存中;
Last updated
Was this helpful?