使用hibernate与mysql时数据不能插入的原因及解决办法
1.背景?????
??????之前從沒用過hibernate,因此在網上搜了一下hibernate快速入門方面的信息,最后我按照《Myeclipse?Hibernate?快速入門?中文版》(CSDN,百度文庫都有)一文開始了我的hibernate之旅(為項目做技術準備)。
??????下面是在學習使用時用到的三個開發工具(Myeclipse?,?Mysql?,?SQL-Front)及其版本說明:
??????Myeclipse版本為6.5(沒用最新的Myeclipse8.5,這是由于之前組里的項目都是在Myeclipse6.5中開發的,并且本項目是與其它人合作完成的,為了防止可能由于開發環境的不一致而引起的問題,我們統一使用Myeclipse6.5,所以我就在Myeclipse6.5環境下學習使用hibernate了)。
??????Mysql使用的是5.1版本。Mysql安裝完后,需要手動進行配置,其中有一項是“please?select?the?database?usage”,我在這里選擇的是“Muitifunctional?Database”(如下圖所示)。這里其實選則的是使用何種類型的數據庫(InnoDB還是MyISAM),如果選第三個,就不能用InnoDB類型的數據庫了,這個在每一個選項的說明中可以看到。
??????
??????為了方便mysql的使用,又安裝了圖形化界面的SQL-Front,版本是5.1。
?
2.遇到的問題
??????我用SQL-Front在數據庫中建了一簡單的表用于學習,等同的SQL語句如下:
“author”表有兩個字段,一個是主鍵“Id”,一個是“name”。需要說明的是SQL-Front在建表的時候默認數據庫類型為InnoDB。
??????針對“author”表,按照《Myeclipse?Hibernate?快速入門?中文版》一文的說明操作完成后,寫了一段簡單的測試代碼:
即插如一條name字段為“author”的數據,但是發現執行完后數據根本就沒有插入到數據庫中。后來在網上搜了一下,有人給出了解決辦法,即使用事務來解決,修改后的代碼如下:
數據插入的問題是解決了,但是發現插入數據后,Id已經在插入前自動增加了(不是從1開始了),也就是說之前的測試雖然數據看似沒有插入數據庫,但是實際效果卻跟插入了數據庫一樣(要不然Id不會自動增加),這又是為什么?在網上搜了一下,結合自己的理解,我分析了一下原因。
?
3.原因
??????上述問題的原因的本質我認為在于使用的數據庫類型。
???????我使用的數據庫類型是“InnoDB”,這是一個支持事務的數據庫類型,這種數據庫你無論什么操作,最后如果你不“commit”的話,等于啥也沒干(這其中的道理在網上搜一下對數據庫中事務的簡單介紹應該不難理解)。雖然通過save方法可以將sql語句發送到數據庫讓其執行(備注:并不是所有的save方法都會將sql語句發送到數據庫,當主鍵生成策略為native的時候會發送到數據庫,比如“author”的主鍵Id,其生成策略就為native,不過可以通過dao.getSession().flush()強行將sql語句發送到數據庫。這些我也只是知道一點皮毛,有時間還需要深入了解),數據庫也確實執行了(分配了Id,并且將Id自動加1),但是這個執行的結果只是臨時的,如果不“commit”的話,隨著會話session的結束(即上述代碼中dao.getSession().close()語句),這個臨時的執行結果也就沒了,直接的體現就是數據沒有最終插入數據庫。
???????通過SQL-Front可以很好的觀察到這一過程(針對未引入事務的那段代碼),首先在dao.getSession().Close()處設置斷點,然后用SQL-Front打開這個表,并設置SQL-Front與mysql的會話隔離級別為(Read?Uncommitted,默認的級別為Repeatable?Read,這個網上也有很多介紹的,也不多說了),然后執行測試代碼,執行到斷點處后,也即剛剛執行完dao.save(author)而還沒有關閉本次會話,通過SQL-Front會發現,這個數據插入到了表中(當然是“臨時”插入數據,如果會話隔離級別不是Read?Uncommitted的話是看不到這條插入的數據的),繼續執行dao.getSession().close()后,通過SQL-Front刷新“author”表中的數據,會發現剛剛插入的那條數據又消失了。
???????不過這其中有一個比較特殊的地方,那就是Id這個自段了。這個字段是“author”表的主鍵,并且它的值是數據庫自己產生的,在插入數據的時候不需要指定這個字段的值。這個字段的特殊之處在于你在插入數據的時候,無論最后是否“commit”,這個字段的值都會自動加1供下一次插入數據時使用,不會說由于本次會話沒有“commit”,而在會話結束時自動減1恢復到原來的值。
???????這一點我覺得應該是出于并發的考慮(沒有查閱相關資料,只是提出我自己的猜測)。假設有三個會話A和B,都要向“author”表中插入數據(假設表中沒有數據,Id從1開始),A首先調用了save方法,數據庫為A插入的數據分配的Id是1,然后“author”表將Id自動加1并保存,在A未“commit”之前,B也調用了save方法,自然數據庫為B插入的數據分配的Id是2(不會是1,如果是1,A和B將要提交的數據主鍵Id值就沖突了,會造成A和B誰后“commit”數據誰失敗,數據庫就無法并發了),“author”表將Id字段再次加1后保存(此時為下一組數據使用的Id值為3),假設B首先“commit”了數據,即“author”表中有了Id字段值為2的數據,而A最終沒有“commit”數據,如果此時“author”表的字段自動減1,可以看到,下一次數據插入分配的Id字段值就會是2,和現有的數據發成了主鍵沖突。因此,對于“author”表中Id這個字段,無論某個插入操作是否最終“commit”,只要調用了save方法,Id字段就會自加1。這也能解釋之前遇到的那個問題,即數據沒有插入,Id字段卻自動增加了。
?
4.解決方法
?????總共有兩種解決方法:
?????第一種:?可以考慮使用“MyISAM”類型的數據庫,這種類型的數據庫不支持事務,因此在調用save方法的時候(注意,如果主鍵生成策略不是native的,必須在save后調用dao.getSession().flush()方法,即強行將sql語句發送到數據庫,否則一樣沒有插入數據),數據就已經最終插入到數據庫里了(注意,這是最終結果,不是臨時結果,這和使用“InnoDB”類型數據庫時“commit”的效果是一樣的)。當然了,直接使用事務機制(就像上面那段修改后的代碼一樣)也是可以的(“MyISAM”類型的數據庫雖然不支持事務,但是并不代表不能用hibernate里的事務機制,這兩個概念還不太一樣。當主鍵生成策略不是native的時候,使用事務機制還省的調用dao.getSession().flush()方法了)。mysql支持“MyISAM”類型的數據庫,可以在SQL-Front中直接將“InnoDB”類型的數據庫轉為“MyISAM”類型的數據庫(建議這么做的時候小心,因為看到網上有人說這么做可能會產生問題,不過我轉換的時候倒是沒碰到什么問題,也可能是我的“author”表簡單的緣故),也可在建表的時候直接指定類型為“MyISAM”。關于“MyISAM”類型的數據庫的更多信息(比如相對于“InnoDB”類型數據庫有什么優缺點)網上也有很多介紹,就不在這里羅嗦了。
?????第二種:還是使用“InnoDB”類型的數據庫。這時可以通過兩種途徑解決:一是像上面那個修改后的代碼一樣,加入事務機制,這是最保險的(也推薦使用);第二個途徑就是在hibernate的配置文件中,加入自動提交的屬性,如下圖所示:
這個屬性的作用是,一旦調用了save方法(和第一種解決方法一樣,如果主鍵生成策略不是native的,必須在save后調用dao.getSession().flush()方法),hibernate會自動幫你“commit”,在代碼里不需要自己寫關于事務的那些代碼(例如commit調用)。這么做的缺點網上也有很多說明,也不在這里多說了。
?
5.總結
??????由于我水平實在有限(大菜鳥一個啦),表達能力也不是很好,所以說了這么多也可能還沒說明白,沒說透徹。不過我覺的對大家有用的一個結論就是,無論什么操作,都放在事務里提交,這樣是最省事,也是最保險的。
?
?
整整浪費了我一天時間啊???我操
轉載于:https://www.cnblogs.com/toSeeMyDream/p/8867707.html
總結
以上是生活随笔為你收集整理的使用hibernate与mysql时数据不能插入的原因及解决办法的全部內容,希望文章能夠幫你解決所遇到的問題。