2013-04-11 145 views
12

想象一下以下機型:如何在Spring JPA中保存引用現有實體的新實體?

員工:

@ManyToMany(cascade = CascadeType.ALL) 
@JoinTable(name = "employee_project", joinColumns = @JoinColumn(name = "Emp_Id"), inverseJoinColumns = @JoinColumn(name = "Proj_id")) 
private Set<Project> projects = new HashSet<Project>(); 

項目:

@ManyToMany(mappedBy = "projects") 
private Set<Employee> employees = new HashSet<Employee>(); 

現在,如果我創建一個新的員工是指現有的項目,並嘗試堅持該員工,我得到一個錯誤:

detached entity passed to persist: Project 

我創造了就業飴如下:

public void createNewEmployee(EmployeeDTO empDTO) { 

    Employee emp = new Employee(); 
    // add stuff from DTO, including projects 

    repository.saveAndFlush(emp); // FAILS 
} 

,我更新現有的像這樣:

public void updateEmployee(EmployeeDTO empDTO) { 

    Employee emp = repository.findOne(empDTO.getId()); 
    // set stuff from DTO, including projects 

    repository.saveAndFlush(emp); // WORKS! 
} 

回答

19

我猜你不恰當地擴大事務邊界與存儲庫交互。默認情況下,事務(因此會話)邊界位於存儲庫方法級別。這會導致Project實例與EntityManager分離,因此它不能包含在持久操作中。

這裏的解決方案是擴展事務邊界到客戶端:

@Component 
class YourRepositoryClient { 

    private final ProjectRepository projects; 
    private final EmployeeRepository employees; 

    // … constructor for autowiring 

    @Transactional 
    public void doSomething() { 
    Project project = projects.findOne(1L); 
    Employee employee = employees.save(new Employee(project)); 
    } 
} 

這種方法導致Project實例留一個管理實體,從而用於新鮮Employee實例被正確地處理將被執行的操作的持續。

與兩個存儲庫交互的不同之處在於,第二種情況下,您將擁有一個已分離的實例(已被持久存在,具有一個id集合),在第一個示例中,您有完全不受管理的實例沒有一個id集。 id屬性是導致存儲庫區分調用persist(…)merge(…)的原因。所以第一種方法會導致persist(…)被觸發,第二種會導致merge(…)

+0

謝謝,這是有效的,但我仍然想知道爲什麼我的更新方法沒有從他們的存儲庫中獲取項目(請參閱我更新的問題的詳細信息) – wannabeartist 2013-04-11 08:48:56

+1

在此處擴展問題並不是一個好主意,因爲它們使某種程度上的答案。最好提出新的問題。我會相應地更新我的答案。 – 2013-04-11 10:13:29

+0

那是春天的'@ Transactional'還是JPA?還是它重要? – CorayThan 2014-10-31 07:01:02

相關問題