Mybatis源码解析之核心类分析
Mybatis源码解析之初始化分析
Mybatis源码解析之执行流程解析
Mybatis源码解析之数据库连接和连接池
一个事务内的所有操作,要么全部完成,要么全部没做,不可能存在中间状态。如果一个事务在执行过程中出现异常,会将已经执行完成的操作回滚,使得所有操作保持在全部没做的状态
事务执行前和执行后的数据库的完整性约束没有变化,执行前和执行后都处于一致性状态。
比如,A向B转账,转账就是一个事务,不管转账多少次,A和B的账户总额一致。
事务和事务之间互相隔离,互不影响。
事务完成后对数据库中数据的改变是永久的。
脏读是指事务A读取到事务B未提交的数据。
不可重复读是指同一事物内的两次相同的查询操作读取到了不同的数据,比如事务A两次查询操作之间,事务B对数据进行了修改并提交。
幻读是指事务A提交了一次更新操作,此时事务B新插入了一条数据,之后事务A查看数据会发现有一条数据没有更新,仿佛出现了幻觉。
不可重复读和幻读都是一个事务A读取了事务B提交之后的数据,而脏读是指事务A读取了事务B没有提交的数据。
不可重复读和幻读有些相似,容易出现混淆。不可重复读侧重于另一事务的修改操作,幻读侧重于另一事务的增加和删除操作。不可重复读的避免需要行锁,幻读需要表锁。
事务transaction通过配置文件的transaction节点进行配置,和数据源datasource节点一样都是environment节点的子节点,如:
参考自http://www.mybatis.org/mybatis-3/zh/configuration.html#environments
在 MyBatis 中有两种类型的事务管理器(也就是 type=”[JDBC|MANAGED]”):
JDBC – 这个配置就是直接使用了 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
MANAGED – 这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置为 false 来阻止它默认的关闭行为。例如:
同样的,根据前面几篇博客可以知道,配置文件的解析在XMLConfigBuilder#parse()方法中进行,而关于transaction节点的解析则委托给了XMLConfigBuilder#transactionManagerElement(XNode)方法处理。
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
根据type属性找到对象的TransactionFactory类型,调用无参构造器生成TransactionFactory对象。
TransactionIsolationLeve就是Mybatis的事务隔离级别,Mybatis一共有5中,NONE代表不支持事务,其它4种与数据库事务隔离级别一一对应。
public enum TransactionIsolationLevel {
NONE(Connection.TRANSACTION_NONE),
READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED),
READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),
REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),
SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE);
private final int level;
private TransactionIsolationLevel(int level) {
this.level = level;
}
public int getLevel() {
return level;
}
}
JdbcTransaction就是由jdbc处理事务,有connection对象完成事务的commit、rollback和close。
@Override
public void commit() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Committing JDBC Connection [" + connection + "]");
}
connection.commit();
}
}
@Override
public void rollback() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Rolling back JDBC Connection [" + connection + "]");
}
connection.rollback();
}
}
@Override
public void close() throws SQLException {
if (connection != null) {
resetAutoCommit();
if (log.isDebugEnabled()) {
log.debug("Closing JDBC Connection [" + connection + "]");
}
connection.close();
}
}
protected void resetAutoCommit() {
try {
if (!connection.getAutoCommit()) {
// MyBatis does not call commit/rollback on a connection if just selects were performed.
// Some databases start transactions with select statements
// and they mandate a commit/rollback before closing the connection.
// A workaround is setting the autocommit to true before closing the connection.
// Sybase throws an exception here.
if (log.isDebugEnabled()) {
log.debug("Resetting autocommit to true on JDBC Connection [" + connection + "]");
}
connection.setAutoCommit(true);
}
} catch (SQLException e) {
if (log.isDebugEnabled()) {
log.debug("Error resetting autocommit to true "
+ "before closing the connection. Cause: " + e);
}
}
}
可以看到,close方法中会将connection设置成自动提交。
ManagedTransaction让容器来管理事务Transaction的整个生命周期,使用ManagedTransaction的commit和rollback功能不会对事务有任何的影响,它什么都不会做,它将事务管理的权利移交给了容器来实现。
@Override
public void commit() throws SQLException {
// Does nothing
}
@Override
public void rollback() throws SQLException {
// Does nothing
}
@Override
public void close() throws SQLException {
if (this.closeConnection && this.connection != null) {
if (log.isDebugEnabled()) {
log.debug("Closing JDBC Connection [" + this.connection + "]");
}
this.connection.close();
}
}