在关系的两边声明@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
。
如果可能的话,我想避免放松约束,因为双方必须出现。
答案 0 :(得分:18)
在关系的两边声明
@OneToOne
和@NotNull
是否有效(...)我找不到任何说这是无效的东西,但似乎在持久性期间至少必须违反关系的一方。 (例如,如果先写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
属性):
@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());
}
}
答案 1 :(得分:0)
由于您的级联类型,它应该保持瞬态值。
如果您在设置其他瞬态元素之前实际上试图保留第一个元素,那么您会发现此错误。
您指定的约束仅指定数据库中的值不能为null,而不是在数据模型中,当您构造对象的新实例时,该引用将为null。虽然引用为null,但您无法持久保存该实体。
答案 2 :(得分:0)
如果您遇到与 openJPA 相同的问题并且Pascals解决方案仍然无效,您可能需要将openJPA属性openjpa.InverseManager
设置为true
你的persistence.xml