Domanda

E 'valido per dichiarare @OneToOne e @NotNull su entrambi i lati di una relazione, come ad esempio:

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

Non riesco a trovare nulla che dice che questo è valido, ma sembra che durante la persistenza di almeno un lato del rapporto deve essere violata. (Eg., Se la scrittura changeEntry prima, changeEntryDetails sarà nullo temporaneamente).

Quando si tenta questo, vedo una not-null property references a null or transient value gettato un'eccezione.

Vorrei evitare di rilassare il vincolo, se possibile, perché entrambe le parti deve essere presenti.

È stato utile?

Soluzione

  

E 'valido per dichiarare @OneToOne e @NotNull su entrambi i lati di una relazione (...) non riesco a trovare nulla che dice che questo è valido, ma sembra che durante la persistenza di almeno un lato del rapporto deve essere violata . (Ad esempio, se crei changeEntry primo, changeEntryDetails sarà nullo temporaneamente).

E 'valida e tutto funziona bene con le entità correttamente mappati. È necessario dichiarare un lato della vostra associazione bidirezionale come il "possedere" lato (questo "controllo" l'ordine di inserti). Una soluzione di lavoro possibile:

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

E per l'altra entità (notare il mappedBy set di attributi sul lato non-possesso dell'associazione):

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

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

    // constructor, getters and setters
}

Con queste entità, il seguente test (a scopo dimostrativo) passa:

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

Altri suggerimenti

Si persiste il valore transitorio a causa del tipo a cascata.

Se si sta in realtà cercando di persistere il primo elemento prima di aver impostato l'altro elemento transitorio, quindi ci si aspetta questo errore.

Il vincolo che hai specificato specifica solo che il valore non può essere nullo nel database, piuttosto che nel modello di dati, chiaramente quando si costruisce una nuova istanza di un oggetto, il riferimento sarà nullo. Mentre il riferimento è nullo, non si possono persistere l'entità.

Se è arrivato qui avendo lo stesso problema con la OpenJPA e Pascal soluzione ancora non funziona per voi, si potrebbe desiderare di impostare la proprietà openjpa.InverseManager OpenJPA al true nel vostro persistence.xml

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top