代码间接放在Github仓库【https://github.com/Damaer/Myb... 】,可间接运行,就不占篇幅了。

[TOC]

1.为什么咱们应用SQLSessionFactoryBuilder的时候不须要本人敞开流?

咱们看咱们的代码:

public class StudentDaoImpl implements IStudentDao {    private SqlSession sqlSession;    public void insertStu(Student student) {        try {            InputStream inputStream;            inputStream = Resources.getResourceAsStream("mybatis.xml");            SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);            sqlSession=sqlSessionFactory.openSession();            sqlSession.insert("insertStudent",student);            sqlSession.commit();        } catch (IOException e) {            e.printStackTrace();        }finally {            if(sqlSession!=null){                sqlSession.close();            }        }    }}

当咱们应用inputStream = Resources.getResourceAsStream("mybatis.xml");的时候,咱们并须要去敞开inputstream,咱们能够查看源码,首先看到SqlSessionFactoryBuilder().build()这个办法:

    // 将inputstream传递进去,调用了另一个分装的build()办法    public SqlSessionFactory build(InputStream inputStream) {        return this.build((InputStream)inputStream, (String)null, (Properties)null);    }

跟进去,咱们再来看另一个build办法,外面有一个finally模块,无论怎么样都会执行close办法,所以这就是为什么咱们在应用的时候为什么不必敞开inputstream的起因:因为这个流是在finally代码块中被敞开了。

    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;    }

2. Sqlsession是如何创立的?

语句外面执行代码:应用SQLSessionFactory去关上一个session,这里的session咱们能够初步了解为一个sql的会话,相似咱们想要发信息给他人,必定须要关上一个和他人的会话。

sqlSession=sqlSessionFactory.openSession();

咱们须要查看源码,咱们发现opensession是sqlSessionFactory的一个接口办法,sqlSessionFactory是一个接口。

public interface SqlSessionFactory {    // 在这里只贴出了一个办法,其余的就不贴了    SqlSession openSession();    }

idea选中该办法,ctrl + alt +B,咱们能够发现有DefaultSqlSessionFactory,和SqlSessionManager这两个类实现了SqlSessionFactory这个接口

那么咱们须要跟进去DefaultSqlSessionFactory这个类的openSesseion办法,在外面调用了一个封装好的办法:openSessionFromDataSource()

    public SqlSession openSession() {        return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);    }

当然在DefaultSqlSessionFactory这个类外面还有一个办法,参数是autoCommit,也就是能够指定是否主动提交:

    public SqlSession openSession(boolean autoCommit) {        return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, autoCommit);    }

咱们再跟进去源码,咱们会发现有一个参数是autoCommit,也就是主动提交,咱们能够看到上一步传值是false,也就是不会主动提交,通过configuration(主配置)获取environment(运行环境),而后通过environment(环境)开启和获取一个事务工厂,通过事务工厂获取事务对象Transaction,通过事务对象创立一个执行器executor,Executor是一个接口,实现类有比方SimpleExecutor,BatchExecutor,ReuseExecutor,所以咱们上面代码里的execType,是指定它的类型,生成指定类型的Executor,把援用给接口对象,有了执行器之后就能够return一个DefaultSqlSession对象了。

    private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {        Transaction tx = null;        DefaultSqlSession var8;        try {            // configuration是主配置文件            Environment environment = this.configuration.getEnvironment();            // 获取事务工厂,事务管理器能够使jdbc之类的            TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);            // 获取事务对象Transaction            tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);            // 通过事务对象创立一个执行器executor            Executor executor = this.configuration.newExecutor(tx, execType);            // DefaultSqlSession是SqlSession实现类,创立一个DefaultSqlSession并返回            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;    }

咱们跟 var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);这句代码,咱们这是初始化函数赋值于各个成员变量,咱们发现外面有一个dirty成员,这是干什么用的呢?从名字上来讲咱们了解是脏的,这里既然设置为false,那就是不脏的意思。那到底什么是脏呢?脏是指内存外面的数据与数据库外面的数据存在不统一的问题,如果统一就是不脏的
前面会解释这个dirty的作用之处,到这里一个SqlSession就创立实现了。

    public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {        this.configuration = configuration;        this.executor = executor;        this.dirty = false;        this.autoCommit = autoCommit;    }

3.增删改是怎么执行的

咱们应用到这句代码:

sqlSession.insert("insertStudent",student);

咱们发现同样是接口办法,下面咱们晓得SqlSession其实是DefaultSqlSession所实现的接口,那么咱们跟进去DefaultSqlSession的insert()办法,咱们发现其实inset办法底层也是实现了update这个办法,同样的delete办法在底层也是调用了update这个办法,增,删,改实质上都是改

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

那么咱们当初跟进去update办法中,dirty变成ture,表明行将改数据,所以数据库数据与内存中数据不统一了,statement是咱们穿过来的id,这样就能够通过id拿到statement的对象,而后就通过执行器执行批改的操作:

    public int update(String statement, Object parameter) {        int var4;        try {            // dirty变成ture,表明数据和数据库数据不统一,须要更新            this.dirty = true;            // 通过statement的id把statement从配置中拿到映射关系            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;    }

4.SqlSession.commit()为什么能够提交事务(transaction)?

首先,咱们应用到的源码,同样抉择DefaultSqlSession这个接口的办法,咱们发现commit外面调用了另一个commit办法,传进去一个false的值:

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

咱们跟进去,发现下面传进去的false是变量force,外面调用了一个isCommitOrRollbackRequired(force)办法,执行的后果返回给commit办法当参数。

public void commit(boolean force) {    try {        this.executor.commit(this.isCommitOrRollbackRequired(force));        // 提交之后dirty置为false,因为数据库与内存的数据统一了。        this.dirty = false;    } catch (Exception var6) {        throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + var6, var6);    } finally {        ErrorContext.instance().reset();    }}

咱们跟进去isCommitOrRollbackRequired(force)这个办法,这个办法从命名上是须要提交还是回滚的意思。在后面咱们晓得autoCommit是false,那么取反之后就是true,对于dirty咱们晓得后面咱们执行过insert()办法,insert的底层调用了update办法,将dirty置为true,示意行将批改数据,那咱们晓得!this.autoCommit && this.dirty的值就是true,那么就短路了,所以整个表达式的值就是true。

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

返回上一层的,咱们晓得this.isCommitOrRollbackRequired(force)的返回值是true。

this.executor.commit(this.isCommitOrRollbackRequired(force));

跟进去commit办法,这个commit办法是一个接口办法,实现接口的有BaseExecutor,还有CachingExecutor,咱们抉择BaseExecutor这个接口实现类:

// required是truepublic void commit(boolean required) throws SQLException {    // 如果曾经 敞开,那么就没有方法提交,抛出异样    if (this.closed) {        throw new ExecutorException("Cannot commit, transaction is already closed");    } else {        this.clearLocalCache();        this.flushStatements();        // 如果required是true,那么就提交事务        if (required) {            this.transaction.commit();        }    }}

5.为什么sqlsession敞开就不须要回滚了?

如果咱们在下面曾经提交过了,那么dirty的值就为false。咱们应用的是sqlSession.close();,跟进去源码,同样是接口,咱们跟DefaoultSqlsession的办法,同样调用了isCommitOrRollbackRequired()这个办法:

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

咱们跟进去isCommitOrRollbackRequired(false)这个办法,咱们晓得force传进来的值是false,autoCommit是false(只有咱们应用无参的sqlSessionFactory.openSession();),取反之后!autoCommit是true,然而dirty曾经是false,所以!this.autoCommit && this.dirty的值是false,那么force也是false,所以整一个表达式就是false:

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

咱们返回上一层,executor.close()办法,参数是false:

this.executor.close(this.isCommitOrRollbackRequired(false));

跟进去close()办法,forceRollback的值是false,咱们发现有一个this.rollback(forceRollback)

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;        }    }

咱们跟进去rollback()这个办法,咱们能够发现required是fasle,所以 this.transaction.rollback();是不会执行的,这个因为咱们在后面做了提交了,所以是不必回滚的:

     public void rollback(boolean required) throws SQLException {        if (!this.closed) {            try {                this.clearLocalCache();                this.flushStatements(true);            } finally {                if (required) {                    this.transaction.rollback();                }            }        }    }

如果咱们当初执行完insert()办法,然而没有应用commit(),那么当初的dirty就是true,也就是数据库数据与内存的数据不统一。咱们再执行close()办法的时候,dirty是true,!this.autoCommit是true,那么整个表达式就是true。

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

返回上一层,close的参数就会变成true

this.executor.close(this.isCommitOrRollbackRequired(false));

close()办法外面调用了 this.rollback(forceRollback);,参数为true,咱们跟进去,能够看到的确执行了回滚:

     public void rollback(boolean required) throws SQLException {        if (!this.closed) {            try {                this.clearLocalCache();                this.flushStatements(true);            } finally {                if (required) {                    this.transaction.rollback();                }            }        }    }

所以只有咱们执行了提交(commit),那么敞开的时候就不会执行回滚,只有没有提交事务,就会产生回滚,所以外面的dirty是很重要的。

【作者简介】
秦怀,公众号【秦怀杂货店】作者,技术之路不在一时,山高水长,纵使迟缓,驰而不息。这个世界心愿所有都很快,更快,然而我心愿本人能走好每一步,写好每一篇文章,期待和你们一起交换。

此文章仅代表本人(本菜鸟)学习积攒记录,或者学习笔记,如有侵权,请分割作者核实删除。人无完人,文章也一样,文笔稚嫩,在下不才,勿喷,如果有谬误之处,还望指出,感激不尽~