2 Stimmen

Hibernate UniqueConstraint mehrere Spalten mit möglichen Nullwerten

Ich habe ein Problem, das das Einfügen von duplizierten Werten in die MySQL-Datenbank (mit Hibernate) verhindert. Das Problem ist: Ich habe eine Kategorie, die eine übergeordnete Kategorie hat. Es können zwei Kategorien mit demselben Namen, aber verschiedenen übergeordneten Kategorien vorhanden sein (deshalb kann der Kategoriename nicht als BusinessId verwendet werden). Wenn die Kategorie keine übergeordnete Kategorie hat (d. h. oberste Kategorien), ist der Wert von parentId NULL. Ich kann nicht verstehen, was ich falsch mache im folgenden Code, um das Duplikat in die Datenbank einzufügen.

Hier ist meine Entitätsklasse.

@Entity
@Table(name = "category", uniqueConstraints = {@UniqueConstraint(columnNames = {"name","parentId"})})
public class Category implements Serializable {
    @Id
    @Column(name="id")
    @GeneratedValue
    private long id;

    @Column(name="name")
    private String name;

    @ManyToOne(cascade={CascadeType.ALL})
    @JoinColumn(name="parentId", nullable=true)
    private Category parent;

    ...
}

Hier ist der Code, der zum Parsen neuer Kategorien verwendet wird (und ich glaube, dass hier etwas falsch ist):-|)

 for (...) {
      Category parent = null;
      String [] categories = somePath.split("\\\\");

      for (String cat: categories) {
          Category category = new Category();
          category.setName(cat);
          category.setParent(parent);
          parent = categoryDB.insertCategory(category);
      }
 }

Und hier ist die insertCategory-Funktion

public Category insertCategory (Category category) {
    Session session = sessionFactory.openSession();
    Transaction transaction = null;
    try{
        transaction = session.beginTransaction();
        category.setId((Long) session.save(category)); 
        transaction.commit();
    } catch (HibernateException e) {
        if (transaction!=null) 
            transaction.rollback();
        ...
    } finally {
        session.close(); 
    }
    return category;
}

Nach Ausführung des Codes habe ich folgende Einträge in der Datenbank erhalten:

select * from category;
+----+--------------+----------+
| id | name         | parentId |
+----+--------------+----------+
|  1 | A            |     NULL |
|  4 | A            |     NULL |
|  2 | B            |        1 |
|  3 | C            |        2 |
|  5 | B            |        4 |
|  6 | C            |        5 |
+----+--------------+----------+

Wenn ich nur nach "name" einschränken möchte, werden nur 3 Einträge in die Datenbank eingefügt, aber ich kann die Beschränkung nur nach Namen nicht durchsetzen, aufgrund der oben beschriebenen Situation.

EDIT: Eine der Möglichkeiten, die ich sah, dieses Problem zu lösen, war die Verwendung von Funktionen in CONSTRAINT-Definitionen (die im Falle von MySQL z. B. IFNULL(parentId,0) sein könnten. Es scheint, dass eine solche Möglichkeit beispielsweise in Oracle möglich ist (gemäß diesem Beitrag), jedoch nicht in MySQL.

EDIT 2: Tatsächlich habe ich in MySQL-Bug-Tracker Personen mit ähnlichem Problem gefunden. Da ich MySQL verwende, habe ich überprüft, welcher Code beim Erstellen der Tabelle generiert wird, und es war fast identisch mit dem im Bug-Bericht (der tatsächlich nach einigen Standards im Bugtracker als NICHT Fehler angesehen wurde. Wie auch immer, mein Kollege hat versucht, den äquivalenten Code auf MS SQL Server auszuführen und es hat ihn tatsächlich davon abgehalten, die Zeilen mit denselben Werten im Namensfeld und im Null-Elternteil einzufügen. Es scheint, dass es keine Möglichkeit auf Datenbankebene gibt (zumindest unter Verwendung von MySQL), um die gewünschte CONSTRAINT zu setzen, was ziemlich enttäuschend ist. Ich akzeptiere die Antwort, da es eine der möglichen Lösungen für das Problem ist (Verwendung von magischen Zahlen, wovon ich aber Abstand nehmen werde, da ich einen zusätzlichen find-Abfrage in meinem Code bevorzuge)

Jede Hilfe wird geschätzt, Danke!

1voto

albertredneck Punkte 96

Eine einfache Lösung besteht darin, parentId auf "self" zu setzen.

+----+--------------+----------+
| id | name         | parentId |
+----+--------------+----------+
|  1 | A            |        1 |
|  2 | B            |        1 |
|  3 | C            |        2 |
|  4 | C            |        3 |
+----+--------------+----------+

So können Sie niemals zwei "Hauptkategorien" haben. Machen Sie parentId nicht nullable.

Ich bin mir sicher, dass Ihr Problem etwas mit nicht überprüften Nullwerten in den Einschränkungen zu tun hat. Sie können es jedoch mit Hibernate-Checks versuchen, aber es wird sicherlich ineffizienter sein.

@org.hibernate.annotations.Check

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X