"1. 使用過程" "2. 背景" "3. 遇到問題" "3.1 不指定Hibernate資料庫方言,預設SQL生成方式" "3.2 拋出異常Hibernate加入了@Transactional事務不會回滾" "3.3 Hibernate使用Spring Test測試加入了@Transactiona ...
1. 使用過程
在項目中我使用Spring Boot 2.0.0.RELEASE開發並且集成了Spring JPA;我這裡將Hibernate版本設置為5.1.2.Final,這裡做修改主要是因為LocalDate跟資料庫映射時有些問題才做更換,其他基本沒什麼大的問題;資料庫使用了MySQL
2. 背景
使用MySQL,所以先對MySQL做一些瞭解,這裡MySQL資料庫中有四種存儲引擎,主要介紹兩種,分別為MyISAM
和InnoDB
,它們兩個的區別如下:
InnoDB | MyISAM | |
---|---|---|
訪問速度 | 相對慢 | 快 |
事務 | 支持 | 不支持 |
外鍵 | 支持 | 不支持 |
應用場景 | 需要事務提交、回滾功能應用 | 以查詢為主的應用 |
各種引擎簡要介紹
https://blog.csdn.net/qq_27028821/article/details/52267991
3. 遇到問題
3.1 不指定Hibernate資料庫方言,預設SQL生成方式
在不指定方言的情況下預設使用了MySQL5Dialect
,這樣在列印create table
命令時會在後面指定它的資料庫引擎為MyISAM
,這樣生成的資料庫是不支持外鍵的,也是不支持事務性操作的。請註意是資料庫。
3.2 拋出異常Hibernate加入了@Transactional
事務不會回滾
現在這裡有個實體類:
@Entity
@Data
public class Test{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
}
我們保存需要保存一個Test
實例時發現拋出了一個異常資料庫居然保存成功,代碼如下:
Service:
@Resource
private TestDao testDao;
@Transactional
public void test(){
Test test = new Test();
test.setName("test");
testDao.save(test);
int a = 1 / 0;
}
這裡我們要看回去MySQL生成的方式,預設是使用了MyISAM
這是不支持事務的,如果向資料庫保存了數據,那麼事務回滾也是不管用的,所以,現在我們需要指定配置資料庫的方言,在Spring Boot中可在application.properties
配置如下:
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
如果是xml配置的可以這麼配置
<property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
這樣我們重新運行生成的表可以有外鍵生成也可以支持資料庫事務,當然數據就可以回滾了
當然了,除了以上的解決方法之外,其實還有一個不怎麼好的解決方法,那就是將Test
中的id
生成策略註解改為如下內容:
@Entity
@Data
public class Test{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
}
對於這個為什麼可以,請接著往下麵看
主鍵生成策略參考
https://blog.csdn.net/lwt976647637/article/details/53352369
3.3 Hibernate使用Spring Test測試加入了@Transactional
事務無論如何資料庫插入不成功
這裡使用上面修改成AUTO的實體類
在測試類裡面我們可以保存我們一個Test
對象,測試代碼如下:
junit.Test
@Test
@Transactional
public void test(){
Test test = new Test();
test.setName("test");
testDao.save(test);
}
上面沒有任何異常拋出,進度條也綠了,但是數據插入資料庫不成功,這時,可以看下控制台列印輸出的語句:
//....前面有很多,不寫出來了
transaction manager [org.springframework.orm.jpa.JpaTransactionManager@5e5ddfbc]; rollback [true]
這裡我大膽的猜測就是Spring Test自動幫我們把事務回滾了,我們如果要看到把數據插入資料庫的話只是需要在test方法加入註解,如
@Test
@Transactional
@Rollback(false) //設置事務不自動回滾
public void test(){
Test test = new Test();
test.setName("test");
testDao.save(test);
}
3.4 Hibernate在使用MyISAM引擎也可以回滾?
我在之前寫的會員管理系統中是沒有指定生成方言的,預設是使用了MyISAM,但是在Service中出現了異常還是可以回滾,怎麼回事?
這是因為我們在save
之後Hibernate不會立馬執行SQL的,除非是事務提交了,我在事務提交之前拋出了異常,所以在Hibernate的緩存中的SQL是不會執行成功的,因此就用假的回滾現象,這不是資料庫提供的回滾功能。
3.5 Hibernate在使用生成策略是IDENTITY不能回滾事務,AUTO可以
這是因為如果使用了IDENTITY
是資料庫維護我們的主鍵,Hibernate為了獲取id是需要向資料庫插入數據才能獲得id的值的,所以會執行SQL,在使用MyISAM引擎的情況下是不能回滾事務的
如果用了AUTO那麼不需要資料庫維護,Hibernate自己維護是不需要向資料庫要主鍵的,那麼不會立馬執行SQL就跟上一個問題一樣的結果。所以這裡的生成策略使用uuid也是可以的,只要不是資料庫維護。
3.6 使用MyISAM引擎下有緩存的情況@Controller
下使用事務回滾不成功,@Service
下成功
在Controller層使用@Transactional
事務是不會回滾的,但是Service層就可以,如果把@Controller
替換成@Component
也是成功的。
我做了下調試,發現Controller調用方法如下圖:
Service調用事務方法如下圖:
也就是代理方式不同,一種是JDK代理一種是CGLib代理,有可能是這個導致了AOP實現的事務是調用不成功的,具體可以自行搜索AOP與動態代理相關的知識(原諒我知識局限)。
4. 總結
這裡遇到大部分問題終其原因都是MySQL引擎使用或是沒有指定方言的原因吧,所以,沒有性能方面的要求或者是小白請使用InnoDB
引擎。
而對於我來說,這些異常手動catch算是學到了些東西,但是會遇到問題說明自己還是沒有瞭解它,不說了,好好學習。