解決Could not commit JPA transaction RollbackException: Transaction marked as rollbackOnly
Could not commit JPA transaction RollbackException: Transaction marked as rollbackOnly:出現該錯誤的原因是使用@Transactional事務托管注解的方法或類事務已經被标記為值復原,且不能再設定為不會滾。
首先了解下@Transactional注解:
@Transactional注解
@Transactional屬性
屬性 | 類型 | 描述 |
---|---|---|
value | String | 可選的限定描述符,指定使用的事務管理器 |
propagation | enum: Propagation | 可選的事務傳播行為設定 |
isolation | enum: Isolation | 可選的事務隔離級别設定 |
readOnly | boolean | 讀寫或隻讀事務,預設讀寫 |
timeout | int (in seconds granularity) | 事務逾時時間設定 |
rollbackFor | Class對象數組,必須繼承自Throwable | 導緻事務復原的異常類數組 |
rollbackFor | ClassName 類名數組,必須繼承自Throwable | 導緻事務復原的異常類名字數組 |
noRollbackFor | Class對象數組,必須繼承自Throwable | 不會導緻事務復原的異常類數組 |
noRollbackFor | ClassName 類名數組,必須繼承自Throwable | 不會導緻事務復原的異常類名字數組 |
用法
@Transactional 可以作用于接口、接口方法、類以及類方法上。當作用于類上時,該類的所有 public 方法将都具有該類型的事務屬性,同時,我們也可以在方法級别使用該标注來覆寫類級别的定義。
雖然 @Transactional 注解可以作用于接口、接口方法、類以及類方法上,但是 Spring 建議不要在接口或者接口方法上使用該注解,因為這隻有在使用基于接口的代理時它才會生效。另外, @Transactional 注解應該隻被應用到 public 方法上,這是由 Spring AOP 的本質決定的。如果你在 protected、private 或者預設可見性的方法上使用 @Transactional 注解,這将被忽略,也不會抛出任何異常。
預設情況下,隻有來自外部的方法調用才會被AOP代理捕獲,也就是,類内部方法調用本類内部的其他方法并不會引起事務行為,即使被調用方法使用@Transactional注解進行修飾。
@Transactional(readOnly = true)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// do something
}
// these settings have precedence for this method
//方法上注解屬性會覆寫類注解上的相同屬性
@Transactional(readOnly = false, propagation=
Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
// do something
}
}
那麼我們的問題就是如果使用@Transactional注解,将事務交給Spring處理的話,因為@Transactional預設設定為復原,無論如何設定rollbackFor、noRollbackFor都會将事務復原。但是代碼執行的時候,似乎SQL語句沒有執行復原就結束了。是以接抛出該異常。
解決方法是從entityManager中得到EntityManagerFactory再建立一個新的EntityManager,然後開始執行事務:
@PersistenceContext
private EntityManager entityManager;
/**
* 擷取 entityManager
* @return the entityManager
*/
public EntityManager getEntityManager()
{
return entityManager;
}
/**
* 設定 entityManager
* @param entityManager the entityManager to set
*/
public void setEntityManager(EntityManager entityManager)
{
this.entityManager = entityManager;
}
private int execute(String sql,EntityManager em)
{
EntityManager em = getEntityManager().getEntityManagerFactory().createEntityManager();
em.getTransaction().begin();
.......
em.getTransaction().commit();
return ;
}
同時還能解決:javax.persistence.TransactionRequiredException: Executing an update/delete query異常,因為此處建立了一個事務處理。而該異常是沒有事務處理Spring抛出來的異常,一舉兩得!