代碼直接放在Github倉庫【https://github.com/Damaer/Mybatis-Learning 】,可直接運行,就不佔篇幅了。java
咱們看咱們的代碼:git
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()
這個方法:github
// 將inputstream傳遞進去,調用了另外一個分裝的build()方法 public SqlSessionFactory build(InputStream inputStream) { return this.build((InputStream)inputStream, (String)null, (Properties)null); }
跟進去,咱們再來看另外一個build方法,裏面有一個finally模塊,不管怎麼樣都會執行close方法,因此這就是爲何咱們在使用的時候爲何不用關閉inputstream的緣由:由於這個流是在finally代碼塊中被關閉了。sql
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; }
語句裏面執行代碼:使用SQLSessionFactory
去打開一個session
,這裏的session
咱們能夠初步理解爲一個sql
的會話,相似咱們想要發信息給別人,確定須要打開一個和別人的會話。數據庫
sqlSession=sqlSessionFactory.openSession();
咱們須要查看源碼,咱們發現opensession是sqlSessionFactory的一個接口方法,sqlSessionFactory是一個接口。session
public interface SqlSessionFactory { // 在這裏只貼出了一個方法,其餘的就不貼了 SqlSession openSession(); }
idea選中該方法,ctrl + alt +B
,咱們能夠發現有DefaultSqlSessionFactory,和SqlSessionManager這兩個類實現了SqlSessionFactory這個接口
那麼咱們須要跟進去DefaultSqlSessionFactory這個類的openSesseion方法,在裏面調用了一個封裝好的方法:openSessionFromDataSource()mybatis
public SqlSession openSession() { return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false); }
固然在DefaultSqlSessionFactory
這個類裏面還有一個方法,參數是autoCommit,也就是能夠指定是否自動提交:app
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對象了。ide
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; }
咱們使用到這句代碼:
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; }
首先,咱們使用到的源碼,一樣選擇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是true public 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(); } } }
假如咱們在上面已經提交過了,那麼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是很重要的。
【做者簡介】:
秦懷,公衆號【秦懷雜貨店】做者,技術之路不在一時,山高水長,縱使緩慢,馳而不息。這個世界但願一切都很快,更快,可是我但願本身能走好每一步,寫好每一篇文章,期待和大家一塊兒交流。
此文章僅表明本身(本菜鳥)學習積累記錄,或者學習筆記,若有侵權,請聯繫做者覈實刪除。人無完人,文章也同樣,文筆稚嫩,在下不才,勿噴,若是有錯誤之處,還望指出,感激涕零~