2016-07-08 16 views
1

Zunächst einmal weiß ich, dass dies keine Lösung sein sollte, aber ich muss das Problem auf diese Weise beheben.Force Hibernate @ OneToOne Beziehung (LIMIT 1)

Ich habe eine OneToOne Beziehung, die, sehr selten, nicht respektiert wird (DB gibt zwei Zeilen statt einer). Ich muss diese Beziehung erzwingen, etwas wie ein LIMIT 1 für diese spezifische Hibernate-Abfrage.

@Entity 
@Table(name="contact", uniqueConstraints = @UniqueConstraint(columnNames="user_id")) 
public class ContactDTO implements Serializable { 

(...) 

// Force this relation 
@OneToOne(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY, optional = true) 
@JoinColumn(name = "user_id", unique = true, insertable = false, updatable = false) 
public UserDTO getBaseUser() { 
    return baseUser; 
} 

public void setBaseUser(UserDTO baseUser) { 
    this.baseUser = baseUser; 
} 

@Column(name = "user_id", unique = true) 
public Integer getUserId() { 
    return this.userId; 
} 

@Entity 
@Table(name = "base_user") 
public class UserDTO implements Serializable { 

(...) 

@OneToOne(fetch = FetchType.LAZY, mappedBy = "baseUser") 
public ContactDTO getContact() { 
    return contact; 
} 

public void setContact(ContactDTO contact) { 
    this.contact = contact; 
} 

Wenn ich zwei Reihen (maximal) erhalten, wirft Hibernate die folgende Ausnahme:

org.hibernate.HibernateException: More than one row with the given identifier was found 

Ist das möglich oder muss ich wirklich diese Beziehung in eine @OneToMany drehen ?

Hinweis: Hibernate 3.3, leider kann ich JoinColumnsOrFormula nicht verwenden.

Vielen Dank für Ihre Hilfe! Mit freundlichen Grüßen.

+0

Könnten Sie bitte die beteiligten Entitäten und Tabellen näher erläutern? –

+0

Sicher. Ich habe ein paar zusätzliche Informationen hinzugefügt, ich denke, es ist genug, um die Logik zu verstehen. –

+0

Sie können immer zur OneToMany-Beziehung wechseln, aber sie in einem privaten Feld verwenden. Stellen Sie dann Getter und Setter bereit, die nur mit dem ersten Element arbeiten. Es wird im Grunde Ihre unerwünschte Situation kapseln, ohne dass Sie anderen Code darüber wissen müssen. –

Antwort

0

Wenn Sie doppelte Datensätze erhalten, bedeutet dies höchstwahrscheinlich, dass die Beziehung nicht 1: 1 in Ihrer Datenbank ist. Sie müssen entweder Datenbankeinschränkungen hinzufügen, um diese Beziehung zu erzwingen, oder Ihre Annotation auf 1: viele ändern. Es ist erwähnenswert, dass die @ OneToOne-Annotation nichts in der Datenbank erzwingt. Sie definiert lediglich, was Hibernate von der Beziehung erwartet, weshalb beim Senden mehrerer Datensätze ein Fehler ausgegeben wird.

überprüfen Sie die Antwort auf diesen Beitrag zu sehen, wie Ihre Beziehung in Ihrer Datenbank aussehen soll:

Defining a one-to-one relationship in SQL Server

+0

Vielen Dank für Ihre Antwort Steve.Das Problem ist, dass der Client keine Datenbankeinschränkung möchte und auch nicht das Code-Problem beheben möchte. Macht Sinn? Natürlich nicht, ich weiß. Die einzige Lösung, die ich habe, ist zu garantieren, dass diese Abfrage nur eine Zeile zurückgibt, um die Ruhezustandsbeziehung zu respektieren, oder sie zu OneToMany ändern, trotz der Tatsache, dass dies nur 5 mal pro Jahr geschieht. –

+0

Von diesen beiden Ansätzen würde ich sagen, dass der OnetoMany-Ansatz eine bessere Praxis wäre. Wenn Sie die Ergebnisse der Abfrage einschränken, würden Sie gültige Daten ausschließen. – Steve

+0

Es sind doppelte Daten, also brauche ich nur eine der beiden Zeilen. Der OneToMany-Ansatz ist der, den ich vermeiden möchte, aber im letzten Fall werde ich es tun. Im Moment versuche ich das Problem mit einem View-Trigger zu lösen. Danke für deine Hilfe Steve. –

0

Um das zu erreichen, dass Sie Ansichten nutzen könnten. Angenommen, Sie haben SOME_TABLE mit zwei Spalten REAL_ID und BAD_ID. Der erste ist einzigartig, der zweite ist Ihre gebrochene, nicht eindeutige Spalte. Erstellen Sie einfach Ansicht mit zum Beispiel:

select * from SOME_TABLE where REAL_ID in (
    select min(REAL_ID) from SOME_TABLE group by BAD_ID 
) 

die im Grunde minus Aufzeichnungen Ihrer ursprünglichen Tabelle, die BAD_ID Werte duplizieren. Dann verwenden Sie im Hibernate-Mapping diese Ansicht anstelle der ursprünglichen Tabelle.

Offensichtlich ist es ein Hack, so kann ich seine Leistung nicht garantieren, aber es sollte Ihnen ermöglichen, @OneToOne Bindung zu verwenden. Der größte Nachteil ist, dass dies standardmäßig ein schreibgeschütztes Mapping ist (was wahrscheinlich durch instead oftriggers geändert werden konnte). BEARBEITEN Einige DBs ermöglichen, dass einfache Ansichten aktualisierbar sind, so dass es ohne Trigger funktionieren kann.

Sie können auch direkt aus der Abfrage Ihre Entitäten generieren.

+0

Vielen Dank für Ihre Antwort Rafal. Das ist die Lösung, an die ich denke, und würde in diesem Fall gut passen. Wie Sie gesagt haben, es ist ein Hack, da stimme ich völlig zu. Wie du schon sagtest, wäre es ein Read-Only Mapping und ich brauche es um zu lesen und zu schreiben. Also, hast du eine Idee wie ich das mit Triggern lösen kann? –

+0

Hier ist ein Artikel, der ein Beispiel für die Verwendung von 'anstelle von 'Triggern in der Ansicht zeigt, um Einfügungen und Aktualisierungen an einer realen Tabelle vorzunehmen: http://in.relation.to/2004/06/20/history-triggers-and-hibernate/ Hoffe es hilft. –

+0

In einigen DBs kann es einfacher sein. Da ich sowohl [sqlserver] (https://stackoverflow.com/questions/3866216/updatable-views-sql-server-2008) und [oracledb] gelesen habe (https://docs.oracle.com/cd/B28359_01/server .111/b28310/views001.htm # i1007148) haben aktualisierbare Ansichten - Rafał S vor 52 Minuten –

Verwandte Themen