2010-04-27 17 views
12

Ist es gültig @OneToOne und @NotNull auf beiden Seiten einer Beziehung zu erklären, wie:Hibernate @OneToOne @NotNull

class ChangeEntry 
{ 
    @OneToOne(cascade=CascadeType.ALL) 
    @NotNull 
    ChangeEntryDetails changeEntryDetails; 

    public void addDetails(ChangeEntryDetails details) { 
     this.changeEntryDetails = details; 
     details.setChangeEntry(this); 
    } 
} 

class ChangeEntryDetails 
{ 
    @OneToOne(cascase=CascadeType.ALL) 
    @NotNull 
    ChangeEntry changeEntry; 

    public void setChangeEntry(ChangeEntry changeEntry) 
    { 
      this.changeEntry = changeEntry; 
    } 
} 

Ich kann nichts finden, das sagt dieser ungültig ist, aber es scheint, dass während Persistenz mindestens einer Seite der Beziehung muss verletzt werden. (ZB wenn ChangeEntry zuerst geschrieben wird, wird ChangeEntryDetails vorübergehend null).

Wenn ich dies versuche, sehe ich eine Ausnahme geworfen not-null property references a null or transient value.

Ich möchte vermeiden, die Beschränkung wenn möglich zu entspannen, da beide Seiten müssen vorhanden sein.

+0

Scheint wie ein problematisches Datenmodell für mich. – Yishai

+0

Cascade auf beiden Seiten ist ein wenig seltsam, aber es sollte nicht wirklich problematisch sein. Könnten Sie das näher ausführen? – Geoff

+0

Die Kaskade war wirklich ein Schrotflintenansatz, um zu versuchen, die transiente Eigenschaft zu erhalten. Es muss nicht auf dem Datensatz Details sein –

Antwort

14

Ist es gültig @OneToOne und @NotNull auf beiden Seiten eine Beziehung zu erklären (...) Ich kann nichts finden, das sagt diese ungültig ist, aber es scheint, dass muss verletzt werden. (Wenn z. B. changeEntry zuerst geschrieben wird, wird changeEntryDetails vorübergehend ungültig).

Es ist gültig und alles funktioniert einwandfrei mit ordnungsgemäß zugeordneten Entitäten. Sie müssen eine Seite Ihrer bidirektionalen Verknüpfung als die "besitzende" Seite deklarieren (diese "Kontrolle" die Reihenfolge der Einsätze). Eine mögliche Arbeitslösung:

@Entity 
@NamedQueries({ @NamedQuery(name = ChangeEntry.FIND_ALL_CHANGEENTRIES, query = "SELECT c FROM ChangeEntry c") }) 
public class ChangeEntry implements Serializable { 
    public final static String FIND_ALL_CHANGEENTRIES = "findAllChangeEntries"; 

    @Id 
    @GeneratedValue 
    private Long id; 

    @OneToOne(optional = false, cascade = CascadeType.ALL) 
    @JoinColumn(name = "DETAILS_ID", unique = true, nullable = false) 
    @NotNull 
    private ChangeEntryDetails changeEntryDetails; 

    public void addDetails(ChangeEntryDetails details) { 
     this.changeEntryDetails = details; 
     details.setChangeEntry(this); 
    } 

    // constructor, getters and setters 
} 

Und für die andere Einheit (man beachte das mappedBy Attribut auf der nicht-besitzende Seite des Vereins):

@Entity 
public class ChangeEntryDetails implements Serializable { 
    @Id 
    @GeneratedValue 
    private Long id; 

    @OneToOne(optional = false, mappedBy = "changeEntryDetails") 
    @NotNull 
    private ChangeEntry changeEntry; 

    // constructor, getters and setters 
} 

Mit diesen Entitäten, der folgende Test (für Demonstrationszwecken) übergibt:

public class ChangeEntryTest { 
    private static EntityManagerFactory emf;  
    private EntityManager em; 

    @BeforeClass 
    public static void createEntityManagerFactory() { 
     emf = Persistence.createEntityManagerFactory("TestPu"); 
    }  
    @AfterClass 
    public static void closeEntityManagerFactory() { 
     emf.close(); 
    }  
    @Before 
    public void beginTransaction() { 
     em = emf.createEntityManager(); 
     em.getTransaction().begin(); 
    }  
    @After 
    public void rollbackTransaction() { 
     if (em.getTransaction().isActive()) { 
      em.getTransaction().rollback(); 
     } 
     if (em.isOpen()) { 
      em.close(); 
     } 
    } 

    @Test 
    public void testCreateEntryWithoutDetails() { 
     try { 
      ChangeEntry entry = new ChangeEntry(); 
      em.persist(entry); 
      fail("Expected ConstraintViolationException wasn't thrown."); 
     } catch (ConstraintViolationException e) { 
      assertEquals(1, e.getConstraintViolations().size()); 
      ConstraintViolation<?> violation = e.getConstraintViolations() 
       .iterator().next(); 

      assertEquals("changeEntryDetails", violation.getPropertyPath() 
       .toString()); 
      assertEquals(NotNull.class, violation.getConstraintDescriptor() 
       .getAnnotation().annotationType()); 
     } 
    } 

    @Test 
    public void testCreateDetailsWithoutEntry() {  
     try { 
      ChangeEntryDetails details = new ChangeEntryDetails(); 
      em.persist(details); 
      fail("Expected ConstraintViolationException wasn't thrown."); 
     } catch (ConstraintViolationException e) { 
      assertEquals(1, e.getConstraintViolations().size()); 
      ConstraintViolation<?> violation = e.getConstraintViolations() 
       .iterator().next(); 

      assertEquals("changeEntry", violation.getPropertyPath() 
       .toString()); 
      assertEquals(NotNull.class, violation.getConstraintDescriptor() 
       .getAnnotation().annotationType()); 
     } 
    } 

    @Test 
    public void validEntryWithDetails() { 
     ChangeEntry entry = new ChangeEntry(); 
     ChangeEntryDetails details = new ChangeEntryDetails(); 
     entry.addDetails(details); 
     em.persist(entry); 

     Query query = em.createNamedQuery(ChangeEntry.FIND_ALL_CHANGEENTRIES); 
     assertEquals(1, query.getResultList().size()); 
    } 
} 
+1

Sie, Herr, Rock. Hervorragende Antwort - vielen Dank. Wenn ich fragen könnte - warum @NotNull und optional = false auf der @OneToOne-Deklaration deklarieren? Dienen sie verschiedenen Zwecken? –

+2

@Marty Gern geschehen, froh, dass Sie es hilfreich finden.Bezüglich der Verwendung von '@ NotNull' und' @JoinColumn (nullable = false) 'verstehe ich den [Anhang D.] (http://people.redhat.com/~ebernard/validation/#appendix-jpa) der Bean Validierungsspezifikation ist, dass das Generieren von Bean-Validierungs-DDL für Persistence Providers nicht obligatorisch ist, so dass ich JPA- und BV-APIs nur für den Fall verwende. –

0

Es sollte den transienten Wert aufgrund Ihres Kaskadentyps beibehalten.

Wenn Sie tatsächlich versuchen, das erste Element beizubehalten, bevor Sie das andere transiente Element festgelegt haben, dann erwarten Sie diesen Fehler.

Die von Ihnen angegebene Einschränkung gibt nur an, dass der Wert in der Datenbank und nicht im Datenmodell nicht null sein darf. Beim Erstellen einer neuen Instanz des Objekts wird die Referenz eindeutig null. Während die Referenz null ist, können Sie die Entität nicht beibehalten. während Ausdauer mindestens eine Seite der Beziehung

+0

Vielen Dank für die Klärung der Datenbank vs Modell Aspekt. Macht Sinn. –

0

Wenn Sie auch hier habe das gleiche Problem mit OpenJPA und Pascals Lösung funktioniert immer noch nicht für Sie, möchten Sie vielleicht die openJPA Eigenschaft openjpa.InverseManager auf true in Ihrer persistence.xml setzen

Verwandte Themen