2016-07-23 7 views
0

Ich möchte einem Rezept eine Kategorie zuweisen. Wenn ich dem Rezept eine zweite Kategorie mit dem gleichen Namen zuteile, führt es eine andere Einfügung in die Datenbank durch, die abbricht (Abbruch aufgrund von Constraint-Verletzung (UNIQUE Constraint fehlgeschlagen: category.name) - das ist in Ordnung). Ich möchte diesen Eintrag wiederverwenden und an das Rezept anhängen. Gibt es eine JPA-Möglichkeit, dies "automatisch" zu tun oder muss ich damit umgehen? Sollte ich in der setCategory-Methode nach einer gleichnamigen Kategorie suchen und diese verwenden, wenn vorhanden? Gibt es ein Muster?JPA Eindeutige Einträge

@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; 
    } 
} 

Beispiel:

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

Ausführung dieses zweimal in der einzigartigen constraint ergibt fehlgeschlagen: category.name. Das ist in Ordnung, da ich nicht mehrere Kategorien mit demselben Namen möchte. Die Datenbank hat dies durchgesetzt, aber ich suche nach der Lösung, dies auch auf der Java-Sprache zu erzwingen. Die 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

Was Sie brauchen, ist eine Sammlung von Kategorien und ordnen Sie sie mit Hibernate Collection Mapping-Regeln. Siehe [dies] (https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/collections.html). Mit anderen Worten, einige Bits müssen von Ihnen selbst erledigt werden. – ha9u63ar

+0

und "die zweite Kategorie mit dem gleichen Namen" kommt von wo? von Grund auf neu erstellt? (In welchem ​​Fall ist ein anderes Objekt), abgerufen von find()/query()? In diesem Fall repräsentiert, was bereits in der Datenbank ist. Es sei denn, Sie definieren Ihre Ausdauer, die niemand kennen kann. FWIW Eine JPA "Unique Constraint" wird nur in der DB erzwungen ... in DDL –

Antwort

0

Hallo das Verhalten, das Sie beschreiben, sind ein Ergebnis des Mappings

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

Wenn wir diese Zuordnung übersetzen ist eine einfache Sprache. Sie sagen:

Jedes Mal, wenn ich versuche, ein Rezept zu speichern, sollte die entsprechende Kategorie auch beibehalten werden.

Das Problem hier kommt von der Tatsache, dass Sie bereits eine vorhandene Kategorie haben, so dass die @ Cascade.PERSIST hier nicht geeignet ist.

Die Semantik ist hier das Gegenteil. Ein Empfänger erstellt nur dann eine Kategorie, wenn eine Kategorie noch nicht existiert. Dies bedeutet, dass die Erstellung der Kategorie eher eine Ausnahme als eine allgemeine Regel darstellt.

Dies bedeutet, dass Sie hier zwei Optionen haben.

Option 1 ist Cascade.PERSIST zu entfernen.

Option 2 ist es durch Cascade.MERGE zu ersetzen.

Option 3 ist in die andere Richtung gehen. Anstatt die @ ManyToOne-Beziehung in Rezept zu Kategorie mit Cascade.PERSIST zu kommentieren, um die @ OneToMany-Beziehung von der Kategorie zu Rezept zu kommentieren.

Der Vorteil eines solchen Ansatzes ist sehr einfach. Sie möchten beim Hinzufügen eines neuen Rezepts nicht immer eine neue Kategorie erstellen. Es ist 100% die ganze Zeit, die Sie eine neue Kategorie hinzufügen möchten, die Sie auch alle angefügten Rezepten hinzufügen möchten.

Auch werde ich Ihnen empfehlen Bidirektionale Beziehungen über unidirektionale Einsen und lesen Sie diesen Artikel über merge zu begünstigen und beharren JPA EntityManager: Why use persist() over merge()?

0

Das Problem wird eine neue Kategorie mit der folgenden Aussage erstellen:

Category category = new Category(); 

Da diese Instanz der Category Entität neu ist und dementsprechend keine persistente Identität aufweist, versucht der Persistenzanbieter, einen neuen Datensatz in der Datenbank zu erstellen. Da das Feld name der Tabelle categoryunique ist, erhalten Sie die Ausnahme für die Constraint-Verletzung von der Datenbank.

Die Lösung holt zuerst die Kategorie und weist sie recipe zu. Also, was Sie tun müssen, ist die folgende:

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

Diese geholt Instanz ist eine verwaltete Einheit und der Persistenz-Provider nicht zu retten versuchen. Dann weisen diese Instanz mit dem recipe wie Sie bereits getan:

recipe.setCategory(category); 

In diesem Fall wird die Kaskadierung einfach ignorieren Speichern der category Beispiel, wenn recipe gespeichert wird. Dies ist in der JPA 2.0-Spezifikation in Abschnitt angegeben 3.2.2 Persistierende eine Entity Instanz wie folgt:

Wenn X eine bereits existierende verwaltete Einheit ist, wird durch den Betrieb anhalten ignoriert.