2016-07-23 49 views
0

我想將一個類別分配給配方。如果我將另一個具有相同名稱的類別分配給配方,則會向數據庫插入另一箇中止(由於違反約束而中止(UNIQUE約束失敗:category.name) - 實際上這很好)。我想重複使用此條目並將其附加到配方中。是否有JPA方式「自動」執行此操作,還是必須處理此操作?我應該在setCategory方法中搜索一個具有相同名稱的類別,並使用這個類別(如果存在)?有沒有圖案?JPA唯一條目

@Entity 
public class Recipe { 
    private Integer id; 
    private Category category; 

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column(name = "id") 
    public Integer getId() { 
     return id; 
    } 

    public void setId(Integer id) { 
     this.id = id; 
    } 

    @ManyToOne(cascade = CascadeType.PERSIST) 
    @JoinColumn(name = "category_id", referencedColumnName = "id") 
    public Category getCategory() { 
     return category; 
    } 

    public void setCategory(Category category) { 
     this.category = category; 
    } 

} 

@Entity 
public class Category { 
    private Integer id; 
    private String name; 
    private List<Recipe> recipes; 

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column(name = "id") 
    public Integer getId() { 
     return id; 
    } 

    public void setId(Integer id) { 
     this.id = id; 
    } 

    @Basic 
    @Column(name = "name") 
    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    @OneToMany(mappedBy = "category", cascade = {CascadeType.ALL}) 
    public List<Recipe> getRecipes() { 
     return recipes; 
    } 

    public void setRecipes(List<Recipe> recipes) { 
     this.recipes = recipes; 
    } 
} 

例子:

Category category = new Category(); 
category.setName("Test Category"); 
cookbookDAO.add(cookbook); 

Recipe recipe = new Recipe(); 
recipe.setTitle("Test Recipe"); 
recipe.setCategory(category); 
recipeDAO.add(recipe); 

執行此兩次導致UNIQUE約束失敗:category.name。這很好,因爲我不想要多個具有相同名稱的類別。數據庫強制執行此操作,但我正在尋找soltuion以在Java語言級別強制執行此操作。 的DDL:

CREATE TABLE "recipe" 
(
    id INTEGER PRIMARY KEY AUTOINCREMENT, 
    category_id INTEGER, 
    FOREIGN KEY (category_id) REFERENCES category(id) 
); 
CREATE TABLE "category" 
(
    id INTEGER PRIMARY KEY AUTOINCREMENT, 
    `name` VARCHAR, 
    UNIQUE(`name`) ON CONFLICT ABORT 
); 
+0

你需要的是一個類別的集合,並使用Hibernate的集合映射規則來映射它們。見[this](https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/collections.html)。換句話說,一些位需要自己完成。 – ha9u63ar

+0

和「第二類同名」來自哪裏?從頭開始創建? (在這種情況下是不同的對象),由find()/ query()檢索?在這種情況下代表數據庫中已有的內容。除非你定義你的持久性,沒有人可以知道。 FWIW JPA「唯一約束」在DB中僅在DDL中執行 –

回答

0

你好你所描述的行爲是映射

@ManyToOne(cascade = CascadeType.PERSIST) 
    @JoinColumn(name = "category_id", referencedColumnName = "id") 
    public Category getCategory() { 
     return category; 
    } 

的結果,如果我們把這種映射是簡單的語言。您在說:

每次我嘗試保存配方時,相應的類別應該也保留 。

這裏的問題來自於您已經有一個現有類別,因此@ Cascade.PERSIST在這裏不合適。

這裏的語義是相反的。只有在類別不存在的情況下,Recipie纔會創建一個類別。這意味着類別的創建更多的是例外而不是一般規則。

這意味着你有兩個選擇。

選項1是刪除Cascade.PERSIST。

選項2是用Cascade.MERGE替換它。

選項3是走另一條路。而不是使用Cascade.PERSIST將食譜中的@ManyToOne關係註釋爲類別,以註釋從類別到食譜的@OneToMany關係。

這種方法的優點非常簡單。雖然在添加新配方時並不總是希望創建新的類別。您想要添加新類別的時候都是100%,您還需要添加所有附加的收件人。

此外,我會建議你偏向於單向者的雙向關係,並閱讀這篇文章,有關合並和堅持JPA EntityManager: Why use persist() over merge()?

0

問題是要創建一個使用下面的語句一個新的類別:

Category category = new Category(); 

因爲Category實體的這個實例是新的,並且因此沒有持久性標識,所以持久性提供者將嘗試在數據庫中創建新記錄。由於category表的name字段是唯一您從數據庫中獲取約束違規異常。

該解決方案首先獲取該類別並將其分配給recipe。所以,你必須做的是以下幾點:

String queryString = "SELECT c FROM Category c WHERE c.name = :catName"; 
TypedQuery<Category> query = em.createQuery(queryString, Category.class); 
em.setParameter("catName", "Test Category"); 
Category category = query.getSingleResult(); 

這牽強實例是一個管理實體和持久性提供者將不嘗試保存。那麼這種情況下分配給recipe,你已經做了:

recipe.setCategory(category); 

在這種情況下,當被保存recipe級聯會忽略保存category實例。這在部分3.2.2 JPA 2.0規範堅持一個實體實例如下所述:

如果X是一個預先存在的管理實體,它是由persist操作忽略。