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
也是成功的。
4. 總結
這里遇到大部分問題終其原因都是MySQL引擎使用或是沒有指定方言的原因吧,所以,沒有性能方面的要求或者是小白請使用InnoDB
引擎。
而對於我來說,這些異常手動catch算是學到了些東西,但是會遇到問題說明自己還是沒有了解它,不說了,好好學習。