2016-04-14 63 views
1

我想問一下這種行爲的推理,因爲看起來我並不完全理解運行到Spring的和merge()在Spring中的區別@Transactional方法/類。當@Transactional方法拋出一個異常時,Hibernate merge()的行爲

我有這應該回滾DB操作,但它不會(全班被註釋爲@Transactional)下面的代碼:

@Override 
public MyBean assignNewFoo(Integer id, Integer idNewFoo) { 

    MyBean bean = myBeanRepository.findOne(id); 
    bean = myBeanRepository.save(bean); 

    bean.setNewFoo(
      fooManagement.findById(idNewFoo) 
      ); 
    if (true) throw new RuntimeException(); 
    return bean; 
} 

下面的代碼不會回滾按預期的例外是拋出:

@Override 
public MyBean assignNewFoo(Integer id, Integer idNewFoo) { 

    MyBean bean = myBeanRepository.findOne(id); 
    myBeanRepository.save(bean); 

    bean.setNewFoo(
      fooManagement.findById(idNewFoo) 
      ); 
    if (true) throw new RuntimeException(); 
    return bean; 
} 

的保存()方法來自類org.springframework.data.jpa.repository.support.SimpleJpaRepository,所以它的代碼是:

@Transactional 
public <S extends T> S save(S entity) { 

    if (entityInformation.isNew(entity)) { 
     em.persist(entity); 
     return entity; 
    } else { 
     return em.merge(entity); 
    } 
} 

該實體是一個現有的實體,所以我明白它正在做一個merge()。按照JPA specification

find方法(只要它會被調用在鎖定或與 LockModeType.NONE調用)並且是 在事務上下文中調用不需要的getReference方法。 如果有 事務範圍的持久化上下文的實體管理器在使用時,所產生的 實體將被分離;如果使用具有擴展 持久性上下文的實體管理器,則會對其進行管理。


合併操作允許狀態從分離 實體傳播到由實體管理器管理的持久性實體。是應用到實體X合併操作的 語義 如下:

  • 如果X是一個分離的實體,X的狀態被複制到預先存在的管理實體實例的相同的X」身份或X的新管理副本X'被創建。
  • 如果X是新實體實例,則創建新的託管實體實例X',並將X的狀態複製到實例X'中的新託管實體中。
  • 如果X是一個除去實體實例,一個IllegalArgumentException將由合併操作拋出(或事務提交將 失敗)。
  • 如果X是一個被管實體,它將被合併操作忽略,但是,合併操作被級聯到由X關聯的實體 如果這些關係已被註釋爲 級聯元素值cascade = MERGE或級聯=所有註釋。
  • 用於Y通過從X具有級聯元件值級聯關係中引用的所有實體= MERGE或級聯= ALL,Y合併 遞歸爲Y」。對於由X引用的所有這樣的Y,X'被設置爲 引用Y'。 (請注意,如果管理X,則X與 X'是同一個對象。)
  • 如果X是與X'合併的實體,並且引用了另一個實體Y,其中cascade = MERGE或cascade = ALL未指定,則從X'導航同一關聯產生對 的引用管理對象Y」具有相同持久化標識爲Y.
  1. 如果merge(返回副本)的假想管理實體,爲什麼存儲在數據庫中的變化,當我使用分離的一個? (除非有例外,這是我想要的行爲)

  2. 爲什麼無論如何改變提交,如果我修改新的託管實體但拋出異常?

編輯按照要求通過@艾倫乾草:

package org.customer.somefoos.service.impl; 

import java.util.ArrayList; 
import java.util.Date; 
import java.util.HashSet; 
import java.util.LinkedHashSet; 
import java.util.List; 
import java.util.Map; 
import java.util.Set; 

import javax.annotation.Resource; 

import org.springframework.stereotype.Service; 
import org.springframework.transaction.annotation.Transactional; 

import org.customer.somefoos.entity.MyBean; 
import org.customer.somefoos.repository.MyBeanRepository; 
import org.customer.somefoos.service.MyBeanManagement; 
import org.customer.somefoos.service.FooManagement; 

@Service 
@Transactional 
public class MyBeanManagementImpl implements MyBeanManagement { 

    @Resource 
    private MyBeanRepository myBeanRepository; 

    @Resource 
    private FooManagement fooManagement; 


    @Override 
    public List<MyBean> findAll() { 
     return myBeanRepository.findAll(); 
    } 

    @Override 
    public MyBean findById(Integer id) { 
     return myBeanRepository.findOne(id); 
    } 

    @Override 
    public void delete(Integer id) { 
     myBeanRepository.delete(id); 
    } 

    @Override 
    public MyBean save(MyBean myBean) { 
     return myBeanRepository.save(myBean); 
    } 

    @Override 
    public MyBean assignNewFoo(Integer id, Integer idNewFoo) { 

     MyBean bean = myBeanRepository.findOne(id); 
     myBeanRepository.save(bean); 

     bean.setNewFoo(
       fooManagement.findById(idNewFoo) 
       ); 
     if (true) throw new RuntimeException(); 
     return bean; 
    } 

} 
+0

你可以發佈整個類的代碼嗎? –

+0

完成。你認爲別的地方有什麼問題嗎,@AlanHay? – jplatasv

回答

0
  1. 看來你誤會合並語義和容器管理事務的行爲。您的assignNewFoo方法是事務性的,您的「bean」實例是從存儲庫加載的。因此,在事務結束之前(或者直到您手動從持久化上下文中刪除),纔會繼續管理「bean」實例。這意味着myBeanRepository.save(bean);調用不做任何事情,因爲「bean」已經是JPA管理的實體。 myBeanRepository.save(bean) == bean只要保存是在同一個事務中執行'findOne'已經發出。合併用於將對實體的非託管實例所做的更改應用於託管實體。此代碼說明正在使用合併案例:

    MyBean bean = repo.findOne(id); 
    MyBean anotherInstance = new MyBean(); 
    anotherInstance.setId(id); 
    anotherInstance.setNewFoo("100"); 
    MyBean managed = repo.save(anotherInstance); 
    // And now we take a look: 
    managed == bean; // => true 
    anotherInstance == managed; // => false 
    bean.getNewFoo(); // => "100" 
    // An anotherInstance is still detached while save() call has 
    // returned us a managed instance ('bean') 
    

    根據JPA規範條目,您引用:它在此處不適用。它說關於非事務性搜索,但是您的搜索在由assignNewFoo調用啓動的事務中執行。

  2. 從上面所寫的所有東西:提供的兩個代碼示例來演示不回滾行爲實際上是相同的。還有你可能面對你抱怨這個問題的一些原因:

    • 你調用從@Transactional方法assignNewFoo和你在這個外@Transactional方法交易[執行程序檢查。由於您的傳播級別爲'REQUIRED'且RuntimeException未在assignNewFoo調用中捕獲,因此該事務將被標記爲回滾一次assignNewFoo調用完成,但實際回滾將在完成事務傳播的方法後執行。
    • 如果你100%肯定你做的一切正確,這可能是Spring/Provider/DBMS的問題。我無法在最新的Spring Boot + Hibernate 4 + HSQLDB上重現此錯誤,因此可能需要檢查是否超出選項。
+0

感謝您的回覆,羅馬。它非常有用。我進一步調試並發現'PersistenceContextType'實際上是Extended的,而不是我認爲的Transaction,所以這是適用於'findOne'的文本:「[...]如果使用具有擴展持久性上下文的實體管理器,他們將被管理「,就像你所說的那樣。大約2。我一直無法重現這一點,所以我什麼都不能說。 – jplatasv

相關問題