日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

MyBatis 事务管理解析和有关事务的几种特殊场景表现

發(fā)布時間:2025/3/20 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MyBatis 事务管理解析和有关事务的几种特殊场景表现 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

點擊上方?好好學(xué)java?,選擇?星標(biāo)?公眾號

重磅資訊、干貨,第一時間送達 今日推薦:干掉 Navicat:這個 IDEA 的兄弟真香!個人原創(chuàng)100W+訪問量博客:點擊前往,查看更多

作者 | 祖大俊

來源 |?my.oschina.net/zudajun/blog/666764

1.說到數(shù)據(jù)庫事務(wù),人們腦海里自然不自然的就會浮現(xiàn)出事務(wù)的四大特性、四大隔離級別、七大傳播特性。

四大還好說,問題是七大傳播特性是哪兒來的?是Spring在當(dāng)前線程內(nèi),處理多個數(shù)據(jù)庫操作方法事務(wù)時所做的一種事務(wù)應(yīng)用策略。

事務(wù)本身并不存在什么傳播特性,不要混淆事務(wù)本身和Spring的事務(wù)應(yīng)用策略。(當(dāng)然,找工作面試時,還是可以巧妙的描述傳播特性的)

2.一說到事務(wù),人們可能又會想起create、begin、commit、rollback、close、suspend。

可實際上,只有commit、rollback是實際存在的,剩下的create、begin、close、suspend都是虛幻的,是業(yè)務(wù)層或數(shù)據(jù)庫底層應(yīng)用語意,而非JDBC事務(wù)的真實命令。

create(事務(wù)創(chuàng)建):不存在。

begin(事務(wù)開始):姑且認(rèn)為存在于DB的命令行中,比如Mysql的start transaction命令,以及其他數(shù)據(jù)庫中的begin transaction命令。JDBC中不存在。

close(事務(wù)關(guān)閉):不存在。應(yīng)用程序接口中的close()方法,是為了把connection放回數(shù)據(jù)庫連接池中,供下一次使用,與事務(wù)毫無關(guān)系。

suspend(事務(wù)掛起):不存在。

附上我歷時三個月總結(jié)的?Java 面試 + Java 后端技術(shù)學(xué)習(xí)指南,筆者這幾年及春招的總結(jié),github 1.1k star,拿去不謝!

下載方式

1.?首先掃描下方二維碼

2.?后臺回復(fù)「Java面試」即可獲取

Spring中事務(wù)掛起的含義是,需要新事務(wù)時,將現(xiàn)有的connection1保存起來(它還有尚未提交的事務(wù)),然后創(chuàng)建connection2,connection2提交、回滾、關(guān)閉完畢后,再把connection1取出來,完成提交、回滾、關(guān)閉等動作,保存connection1的動作稱之為事務(wù)掛起。

在JDBC中,是根本不存在事務(wù)掛起的說法的,也不存在這樣的接口方法。

因此,記住事務(wù)的三個真實存在的方法,不要被各種事務(wù)狀態(tài)名詞所迷惑,它們分別是:conn.setAutoCommit()、conn.commit()、conn.rollback()。

conn.close()含義為關(guān)閉一個數(shù)據(jù)庫連接,這已經(jīng)不再是事務(wù)方法了。

1. Mybaits中的事務(wù)接口Transaction

public?interface?Transaction?{Connection getConnection()?throws?SQLException;void?commit()?throws?SQLException;void?rollback()?throws?SQLException;void?close()?throws?SQLException; }

有了文章開頭的分析,當(dāng)你再次看到close()方法時,千萬別再認(rèn)為是關(guān)閉一個事務(wù)了,而是關(guān)閉一個conn連接,或者是把conn連接放回連接池內(nèi)。

事務(wù)類層次結(jié)構(gòu)圖:

JdbcTransaction:單獨使用Mybatis時,默認(rèn)的事務(wù)管理實現(xiàn)類,就和它的名字一樣,它就是我們常說的JDBC事務(wù)的極簡封裝,和編程使用mysql-connector-java-5.1.38-bin.jar事務(wù)驅(qū)動沒啥差別。其極簡封裝,僅是讓connection支持連接池而已。

ManagedTransaction:含義為托管事務(wù),空殼事務(wù)管理器,皮包公司。僅是提醒用戶,在其它環(huán)境中應(yīng)用時,把事務(wù)托管給其它框架,比如托管給Spring,讓Spring去管理事務(wù)。

org.apache.ibatis.transaction.jdbc.JdbcTransaction.java部分源碼。

@Overridepublic?void?close()?throws?SQLException {if?(connection != null) {resetAutoCommit();if?(log.isDebugEnabled()) {log.debug("Closing JDBC Connection ["?+ connection + "]");}connection.close();}}

面對上面這段代碼,我們不禁好奇,connection.close()之前,居然調(diào)用了一個resetAutoCommit(),含義為重置autoCommit屬性值。

connection.close()含義為銷毀conn,既然要銷毀conn,為何還多此一舉的調(diào)用一個resetAutoCommit()呢?消失之前多喝口水,真的沒有必要。

其實,原因是這樣的,connection.close()不意味著真的要銷毀conn,而是要把conn放回連接池,供下一次使用,既然還要使用,自然就需要重置AutoCommit屬性了。

通過生成connection代理類,來實現(xiàn)重回連接池的功能。如果connection是普通的Connection實例,那么代碼也是沒有問題的,雙重支持。

2. 事務(wù)工廠TransactionFactory

顧名思義,一個生產(chǎn)JdbcTransaction實例,一個生產(chǎn)ManagedTransaction實例。兩個毫無實際意義的工廠類,除了new之外,沒有其他代碼。

<transactionmanager type="JDBC"?/>

mybatis-config.xml配置文件內(nèi),可配置事務(wù)管理類型。

3. Transaction的用法

無論是SqlSession,還是Executor,它們的事務(wù)方法,最終都指向了Transaction的事務(wù)方法,即都是由Transaction來完成事務(wù)提交、回滾的。

配一個簡單的時序圖。

代碼樣例:

public?static?void?main(String[] args) {SqlSession sqlSession = MybatisSqlSessionFactory.openSession();try?{StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);Student student = new?Student();student.setName("yy");student.setEmail("email@email.com");student.setDob(new?Date());student.setPhone(new?PhoneNumber("123-2568-8947"));studentMapper.insertStudent(student);sqlSession.commit();} catch?(Exception e) {sqlSession.rollback();} finally?{sqlSession.close();}}

注:Executor在執(zhí)行insertStudent(student)方法時,與事務(wù)的提交、回滾、關(guān)閉毫無瓜葛(方法內(nèi)部不會提交、回滾事務(wù)),需要像上面的代碼一樣,手動顯示調(diào)用commit()、rollback()、close()等方法。

因此,后續(xù)在分析到類似insert()、update()等方法內(nèi)部時,需要忘記事務(wù)的存在,不要試圖在insert()等方法內(nèi)部尋找有關(guān)事務(wù)的任何方法。

4. 你可能關(guān)心的有關(guān)事務(wù)的幾種特殊場景表現(xiàn)(重要)

1. 一個conn生命周期內(nèi),可以存在無數(shù)多個事務(wù)。

// 執(zhí)行了connection.setAutoCommit(false),并返回SqlSession sqlSession = MybatisSqlSessionFactory.openSession();try?{StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);Student student = new?Student();student.setName("yy");student.setEmail("email@email.com");student.setDob(new?Date());student.setPhone(new?PhoneNumber("123-2568-8947"));studentMapper.insertStudent(student);// 提交sqlSession.commit();studentMapper.insertStudent(student);// 多次提交sqlSession.commit();} catch?(Exception?e) {// 回滾,只能回滾當(dāng)前未提交的事務(wù)sqlSession.rollback();} finally?{sqlSession.close();}

對于JDBC來說,autoCommit=false時,是自動開啟事務(wù)的,執(zhí)行commit()后,該事務(wù)結(jié)束。

以上代碼正常情況下,開啟了2個事務(wù),向數(shù)據(jù)庫插入了2條數(shù)據(jù)。

JDBC中不存在Hibernate中的session的概念,在JDBC中,insert了幾次,數(shù)據(jù)庫就會有幾條記錄,切勿混淆。而rollback(),只能回滾當(dāng)前未提交的事務(wù)。

2. autoCommit=false,沒有執(zhí)行commit(),僅執(zhí)行close(),會發(fā)生什么?

try?{studentMapper.insertStudent(student); } finally?{sqlSession.close(); }

就像上面這樣的代碼,沒有commit(),固執(zhí)的程序員總是好奇這樣的特例。

insert后,close之前,如果數(shù)據(jù)庫的事務(wù)隔離級別是read uncommitted,那么,我們可以在數(shù)據(jù)庫中查詢到該條記錄。

接著執(zhí)行sqlSession.close()時,經(jīng)過SqlSession的判斷,決定執(zhí)行rollback()操作,于是,事務(wù)回滾,數(shù)據(jù)庫記錄消失。

下面,我們看看org.apache.ibatis.session.defaults.DefaultSqlSession.java中的close()方法源碼。

@Overridepublic?void?close()?{try?{executor.close(isCommitOrRollbackRequired(false));dirty = false;} finally?{ErrorContext.instance().reset();}}

事務(wù)是否回滾,依靠isCommitOrRollbackRequired(false)方法來判斷。

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

在上面的條件判斷中,!autoCommit=true(取反當(dāng)然是true了),force=false,最終是否回滾事務(wù),只有dirty參數(shù)了,dirty含義為是否是臟數(shù)據(jù)。

@Overridepublic?int insert(String?statement, Object?parameter) {return?update(statement, parameter);}@Overridepublic?int update(String?statement, Object?parameter) {try?{dirty = true;MappedStatement ms = configuration.getMappedStatement(statement);return?executor.update(ms, wrapCollection(parameter));} catch?(Exception e) {throw?ExceptionFactory.wrapException("Error updating database. Cause: "?+ e, e);} finally?{ErrorContext.instance().reset();}}

源碼很明確,只要執(zhí)行update操作,就設(shè)置dirty=true。insert、delete最終也是執(zhí)行update操作。

只有在執(zhí)行完commit()、rollback()、close()等方法后,才會再次設(shè)置dirty=false。

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

因此,得出結(jié)論:autoCommit=false,但是沒有手動commit,在sqlSession.close()時,Mybatis會將事務(wù)進行rollback()操作,然后才執(zhí)行conn.close()關(guān)閉連接,當(dāng)然數(shù)據(jù)最終也就沒能持久化到數(shù)據(jù)庫中了。

3. autoCommit=false,沒有commit,也沒有close,會發(fā)生什么?

studentMapper.insertStudent(student);

干脆,就這一句話,即不commit,也不close。

結(jié)論:insert后,jvm結(jié)束前,如果事務(wù)隔離級別是read uncommitted,我們可以查到該條記錄。jvm結(jié)束后,事務(wù)被rollback(),記錄消失。通過斷點debug方式,你可以看到效果。

這說明JDBC驅(qū)動實現(xiàn),已經(jīng)Kao慮到這樣的特例情況,底層已經(jīng)有相應(yīng)的處理機制了。這也超出了我們的探究范圍。

但是,一萬個屌絲程序員會對你說:Don't do it like this. Go right way。

警告:請按正確的try-catch-finally編程方式處理事務(wù),若不從,本人概不負(fù)責(zé)后果。

注:無參的openSession()方法,會自動設(shè)置autoCommit=false。

總結(jié):Mybatis的JdbcTransaction,和純粹的Jdbc事務(wù),幾乎沒有差別,它僅是擴展支持了連接池的connection。

另外,需要明確,無論你是否手動處理了事務(wù),只要是對數(shù)據(jù)庫進行任何update操作(update、delete、insert),都一定是在事務(wù)中進行的,這是數(shù)據(jù)庫的設(shè)計規(guī)范之一。

最后,再附上我歷時三個月總結(jié)的?Java 面試 + Java 后端技術(shù)學(xué)習(xí)指南,筆者這幾年及春招的總結(jié),github 1.1k star,拿去不謝!

下載方式

1.?首先掃描下方二維碼

2.?后臺回復(fù)「Java面試」即可獲取

總結(jié)

以上是生活随笔為你收集整理的MyBatis 事务管理解析和有关事务的几种特殊场景表现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。