2016-07-23 7 views
0

レシピにカテゴリを割り当てたいと思います。同じ名前の2番目のカテゴリをレシピに割り当てると、データベースに別のレコードが挿入されます(制約違反のために中断します(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; 
    } 
} 

例:UNIQUE制約では、この二回の結果を実行する

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); 

に失敗しました:category.nameを。私は同じ名前の複数のカテゴリを必要としないので、これは問題ありません。データベースがこれを強制しましたが、私はこれを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

「同じ名前の2番目のカテゴリ」はどこから来ますか?スクラッチから作成? (その場合は別のオブジェクトです)、find()/ query()で取得しますか?この場合は、すでにデータベース内にあるものを表します。あなたが永続性を定義しない限り、誰も知ることができません。 FWIW JPAの「一意制約」はDDL内のDBのみに適用されます –

答えて

0

こんにちはあなたが記述している行動は、マッピング

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

の結果です。あなたは言っている:私は、対応するカテゴリ 同様に永続化する必要があるレシピを保存しよう

毎回。

ここで問題となるのは、既存のカテゴリが既に存在しているためです。@ Cascade.PERSISTは適切ではありません。

ここでのセマンティクスは反対です。レシピは、カテゴリがまだ存在しない場合にのみカテゴリを作成します。これは、カテゴリの作成が一般的なルールよりも例外であることを意味します。

ここには2つのオプションがあります。

オプション1は、Cascade.PERSISTを削除します。

オプション2は、それをCascade.MERGEに置き換えることです。

オプション3は別の方法です。カテゴリーからレシピへの@OneToMany関係に注釈を付けるために、Cascade.PERSISTを使用してレシピ内の@ManyToOne関係にカテゴリーに注釈を付ける代わりに、

このようなアプローチの利点は非常に簡単です。新しいレシピを追加するときに常に新しいカテゴリを作成するとは限りませんが。新しいカテゴリーを追加したい場合は、100%すべてのレシピを追加する必要があります。

また、私は問題では、次の文を使用して新しいカテゴリを作成しているある一方向のものより双方向の関係を優先することをお勧めいたしますと、マージについては、この記事を読んで、JPA EntityManager: Why use persist() over merge()?

0

を持続します:

Category category = new Category(); 

Categoryエンティティのこのインスタンスは新規であり、したがって永続的なIDを持たないため、永続性プロバイダはデータベースに新しいレコードを作成しようとします。 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が保存されるとき

recipe.setCategory(category); 

は、この場合、カスケードはちょうどcategoryインスタンスを保存無視します。これは次のようにエンティティインスタンスを永続化部3.2.2でJPA 2.0仕様に記載されている:

Xは既存の管理されたエンティティである場合、それは持続操作によって無視されます。

関連する問題