mybatis源码解析之 mybatis如何完成自动帮我们完成事务的开启,提交与回滚

先看一部分代码 这里面有一些值得注意的地方
public void insertUser(User user) {

    try {
        //加载主配置文件
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        //创建sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //创建sqlSession
        sqlSession = sqlSessionFactory.openSession(); //默认提交 在创建中使用的是false;
        //插入 默认没有提交
        //疑问? 为什么需要提交 没有显示开启事务 得到sessions时从environment中获取事务管理,开启事务
        //io流为什么不需要关 在SqlSessionFactoryBuilder().build(inputStream)中会调用io流的关闭函数
        sqlSession.insert("insertUser", user);
        //增insert,删delete, 底层都是调用的update函数
        //没有这一句为什么数据库中表没有
        sqlSession.commit(); //提交以后事务不会回滚, 提交后会回滚,通过ditry 提交后为false
        //未提交为true;
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (sqlSession != null) {
            sqlSession.close();
        }
    }


}

首先进入new SqlSessionFactoryBuilder().build(inputStream);看看究竟是怎样调用的

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    SqlSessionFactory var5;
    try {
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        var5 = this.build(parser.parse());
    } catch (Exception var14) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
    } finally {
        ErrorContext.instance().reset();

        try {
            inputStream.close();
        } catch (IOException var13) {
            ;
        }

    }

    return var5;
}

首先建立了一个XMLConfigBuilder 型的xml解析器,在通过this.build(parser.parse())得到 SqlSessionFactory(默认是

返回DefaultSqlSessionFactory),并注意在使用完后该方法中已近帮我们关闭了inputStream,因此我们在使用过程中不需要关闭inputStream

接着看我们的openSession函数

public SqlSession openSession() {
    return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null; 

    DefaultSqlSession var8;
    try {
        Environment environment = this.configuration.getEnvironment();  
        TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        Executor executor = this.configuration.newExecutor(tx, execType);
        var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
    } catch (Exception var12) {
        this.closeTransaction(tx);
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
    } finally {
        ErrorContext.instance().reset();
    }

    return var8;
}

到这里我们明白了

configuration存放着我们所有的mybatis.xml 中的配置是对配置文件的一个封装,这里 通过配置文件取得环境 也就是<environment></environment>的信息,然后通过此方法this.getTransactionFactoryFromEnvironment(environme)取得我们的

事务工厂,并得到一个事务, 然后获取一个执行器,最后最终初始化了sqlSession并返回在这里 通过上面两个方法我们知道这里的autoCommit(从名字中我们也可以猜出)是false

接着我们继续往下看DefaultSqlSession的insert方法

public int insert(String statement, Object parameter) {
    return this.update(statement, parameter);
}

//这里可以看出是调用了update方法

public int update(String statement, Object parameter) {
    int var4;
    try {
        this.dirty = true;
        MappedStatement ms = this.configuration.getMappedStatement(statement);
        var4 = this.executor.update(ms, this.wrapCollection(parameter));
    } catch (Exception var8) {
        throw ExceptionFactory.wrapException("Error updating database.  Cause: " + var8, var8);
    } finally {
        ErrorContext.instance().reset();
    }

    return var4;
}

//这里通过配置类获取我们所写的sql语句也就是 id为"insertUser" 对应得语句 ,然后通过我们的执行器就行执行,但这里需要注意 this.dirty = true; 这个是用于标志内存中数据是否跟数据库中是否一致,设为true表示不一致因为即将要修改。

接着我们看一下sqlSession.commit()函数

public void commit() {
    this.commit(false);
}

继续

public void commit(boolean force) {
    try {
        this.executor.commit(this.isCommitOrRollbackRequired(force)); 
        this.dirty = false; 
    } catch (Exception var6) {
        throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + var6, var6);
    } finally {
        ErrorContext.instance().reset();
    }

}
 this.executor.commit(this.isCommitOrRollbackRequired(force)); 注意这一步 我们看实现 并且在这里注意一下,执行完提交后

会执行这句 this.dirty = false;  意思是提交完成,数据库状态中的记录与内存中数据一致

private boolean isCommitOrRollbackRequired(boolean force) {
    return !this.autoCommit && this.dirty || force;
}

 这里我们注意 force闯入的是false,而 this.dirty在上面我们知道被设置为了true,所以最终由this.autoCommit决定,当我们

直接使用不带参数的openSession默认.autoCommit是为false故这里为返回的为真

继续看commit函数
public void commit(boolean required) throws SQLException {
    if (this.closed) {
        throw new ExecutorException("Cannot commit, transaction is already closed");
    } else {
        this.clearLocalCache();
        this.flushStatements();
        if (required) {
            this.transaction.commit();
        }

    }
}

这里的required是为真,故事务提交即sqlSession.commit()提交或导致事务的提交

在这里我们知道了sqlSession.commit()提交会进行事务的提交

接下来我们关注一下sqSession.close() 来明白为什么我们不需要担心失败会滚的问题了

public void close() {
    try {
        this.executor.close(this.isCommitOrRollbackRequired(false));
        this.closeCursors();
        this.dirty = false;
    } finally {
        ErrorContext.instance().reset();
    }


}

看一下这个方法

private boolean isCommitOrRollbackRequired(boolean force) { return !this.autoCommit && this.dirty || force; }

这里 !this.autoCommit是true, force为false,所以值由dirty决定,在前面commit中,若提交成功,dirty或被重新设置为false

则返回的是false, 若提交不成功,则返回的是true,则返回的是true

我们看executor的close方法
public void close(boolean forceRollback) {
    try {
        try {
            this.rollback(forceRollback);
        } finally {
            if (this.transaction != null) {
                this.transaction.close();
            }

        }
    } catch (SQLException var11) {
        log.warn("Unexpected exception on closing transaction.  Cause: " + var11);
    } finally {
        this.transaction = null;
        this.deferredLoads = null;
        this.localCache = null;
        this.localOutputParameterCache = null;
        this.closed = true;
    }

}
再看rollbanck方法 我们知道
public void rollback(boolean required) throws SQLException {
    if (!this.closed) {
        try {
            this.clearLocalCache();
            this.flushStatements(true);
        } finally {
            if (required) {
                this.transaction.rollback();
            }

        }
    }

}

在这里我们终于知道了 若是 提交成功 required就是false 就不会回滚, 若提交失败。required就是true就会回滚事务。 同时也应当注意 当我们使用sqlSessionFactory.openSession() 在 做一些跟新操作的时候别忘了使用sqlsession.commit()哦! 不然所有的都会回滚 。

猜你喜欢

转载自blog.csdn.net/qq_32459653/article/details/81228191