它是有效申报@OneToOne,并@NotNull在关系的双方,如:
@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; } }
我找不到任何表明这是无效的信息,但似乎在持久性过程中,至少必须违反关系的一侧。(例如,如果首先写入changeEntry,则changeEntryDetails临时为null)。
尝试此操作时,我看到抛出了异常not-null property references a null or transient value。
not-null property references a null or transient value
我想避免在可能的情况下放宽约束,因为双方都 必须 存在。
声明@OneToOne并@NotNull在关系的两边是否合法(…)我找不到任何表明无效的内容,但是在持久性过程中,似乎至少必须违反关系的一侧。(例如,如果changeEntry先写,changeEntryDetails则暂时为null)。
changeEntry
changeEntryDetails
这是有效的,并且在正确映射的实体上一切正常。您需要将双向关联的一侧声明为“拥有”侧(此“控制”插入顺序)。一种可行的解决方案:
@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 }
对于其他实体(请注意mappedBy在关联的非所有权方设置的属性):
mappedBy
@Entity public class ChangeEntryDetails implements Serializable { @Id @GeneratedValue private Long id; @OneToOne(optional = false, mappedBy = "changeEntryDetails") @NotNull private ChangeEntry changeEntry; // constructor, getters and setters }
对于这些实体,以下测试(用于演示目的)通过:
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()); } }