2012-09-19 110 views
2

我無法對下列項目正確搞清楚如何建立JPA的持久性(使用的EclipseLink和交易型=「RESOURCE_LOCAL」)唯一的新實體:如何級聯堅持

@Entity 
public class User { 
    // snip various members 

    @ManyToMany 
    private List<Company> companies; 

    public void setCompanies(List<Company> companies) { 
      this.companies = companies; 
    } 
} 

@Entity 
public class Company { 
    // snip various members 
} 

我「M試圖做的是建立了企業名單級聯,這樣,如果一個新的公司,以前沒有堅持是在列表中,它會自動與用戶一起堅持:

User newUser = new User(); 

Company newCompany = new Company(); 
List<Company> companies = new ArrayList<Company>(); 
companies.add(newCompany); 

newUser.setCompanies(companies); 

entityManager.persist(newUser); 

通過設置cascadeType.PERSIST @ManyToMany,這工作得很好。但是,如果公司的名單中包含一個公司,是previsouly堅持,我得到一個MySQLIntegrityConstraintViolationException,因爲它試圖堅持(INSERT)一個新的公司具有相同主鍵:

User newUser = new User(); 

Company oldCompany = companyDAO.find(oldCompanyId); 
List<Company> companies = new ArrayList<Company>(); 
companies.add(oldCompany); 

newUser.setCompanies(companies); 

entityManager.persist(newUser); 

那麼如何來建立這樣新的公司會自動持續存在,但現有的公司只是簡單地添加到用戶 - 公司映射中?

+0

你有沒有試過CascadeType.MERGE – RNJ

+0

試過MERGE是的,但是然後新的公司不會自動持續。 – Rolf

回答

7

在hibernate中考慮級聯的最好方法是如果您在父級上調用方法X,那麼它會調用每個子級上的方法X.所以是的,如果你打電話堅持用戶,那麼它會調用堅持每個孩子,不管他們是否被堅持。

這種情況並非理想的級聯處理。 Cascade Persist適用於所有孩子都是由父母創建的情況(例如,如果某個用途具有「技能」列表),並且更多用於一對多。

我個人不會在這種情況下使用級聯。在不需要的情況下使用級聯可能會減慢應用程序的速度。

如果您覺得必須使用級聯,則可以使用級聯合並。合併將在持久化實體時保持不變。然而,合併有一些非常奇怪的副作用,這可能是爲什麼你沒有注意到它的工作原理。考慮下面的例子:

x = new Foo(); 
y = new Foo(); 

em.persist(x); 
Foo z = em.merge(y); 

//x is associated with the persistence context 
//y is NOT associated with the persistence context 
//z is associated with the persistence context 
+0

感謝您的見解。也許我會完全跳過級聯。但實際上,我使用CascadeType.MERGE的問題在於,在使用新公司保留新用戶時,它不起作用。這導致InvalidDataAccessApiUsageException「通過未標記級聯PERSIST的關係找到新對象」。 – Rolf

+0

有趣的是,我想我錯了。 – Pace

+0

所以我最終放棄了級聯,而是修改了負責保存用戶實體的DAO,以便在持久化或合併用戶之前,迭代用戶公司,並首先持續/合併這些用戶實體。我最初沒有意識到的是級聯對於更新用戶和公司之間的關係不是必需的 - 這是作爲更新用戶的一部分自動處理的,因爲關係僅僅是用戶的屬性。 – Rolf

4

你的問題是你正在破壞你的持久性上下文。受管理的對象應該只引用其他管理對象。所以讓你的新對象引用一個現有的分離對象是錯誤的。

你需要做的是做一個find()來獲得現有分離對象的託管版本,並讓你的新對象引用它,然後調用persist。

你也可以使用merge()而不是persist,它應該解析你的對象的引用。請注意,合併不會管理分離的對象,它會返回管理的分離對象的副本。

+0

「做一個find()來獲得現有分離對象的託管版本,並讓你的新對象引用它,然後調用對它堅持」 - 但是不是我正在做什麼? companyDAO.find(oldCompanyId)調用entityManager.find()並返回添加到用戶公司列表中的結果,然後用戶使用entityManager.persist(newUser)持久化; – Rolf

+1

@Rolf在這種情況下的問題可能是您正在使用不同的實體管理器。舊公司由dao的實體經理獲得,然後再用另一個持久這個新實體。持續時間的老公司眼中的舊公司基本上是分開的。 –