java 休眠@OneToOne @NotNull

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2725111/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 22:32:29  来源:igfitidea点击:

Hibernate @OneToOne @NotNull

javahibernateormjpabean-validation

提问by Marty Pitt

Is it valid to declare @OneToOneand @NotNullon both sides of a relationship, such as:

在关系的双方声明@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;
     }
 }

I can't find anything that says this is invalid, but it seems that during persistence at least one side of the relationship must be violated. (Eg., if writing changeEntry first, changeEntryDetails will be null temporarily).

我找不到任何说这是无效的,但似乎在坚持期间,至少必须违反关系的一侧。(例如,如果先写入changeEntry,则changeEntryDetails 将暂时为空)。

When trying this, I see an exception thrown not-null property references a null or transient value.

尝试此操作时,我看到抛出异常not-null property references a null or transient value

I'd like to avoid relaxing the constraint if possible, because both sides mustbe present.

如果可能,我想避免放松约束,因为双方都必须在场。

回答by Pascal Thivent

Is it valid to declare @OneToOneand @NotNullon both sides of a relationship (...) I can't find anything that says this is invalid, but it seems that during persistence at least one side of the relationship must be violated. (e.g. if writing changeEntryfirst, changeEntryDetailswill be null temporarily).

在关系的双方声明@OneToOne和声明是否有效@NotNull(...)我找不到任何说明这是无效的,但似乎在持久性期间至少必须违反关系的一方。(例如,如果changeEntry先写入,changeEntryDetails将暂时为空)。

It is valid and everything works fine with properly mapped entities. You need to declare one side of your bi-directional association as the "owning" side (this "control" the order of inserts). One possible working solution:

它是有效的,并且在正确映射实体的情况下一切正常。您需要将双向关联的一侧声明为“拥有”侧(此“控制”插入顺序)。一种可能的工作解决方案:

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

And for the other entity (note the mappedByattribute set on the non-owning side of the association):

对于另一个实体(注意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
}

With these entities, the following test (for demonstration purposes) passes:

使用这些实体,以下测试(用于演示目的)通过:

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

回答by Geoff

It should be persisting the transient value because of your cascade type.

由于您的级联类型,它应该保持瞬态值。

If you are actually trying to persist the first element before you've set the other transient element, then you'd expect this error.

如果您实际上是在设置另一个瞬态元素之前尝试保留第一个元素,那么您会期望出现此错误。

The constraint you've specified only specifies that the value cannot be null in the database, rather than in the data model, clearly when you construct a new instance of the object, the reference will be null. While the reference is null you cannot persist the entity.

您指定的约束仅指定数据库中的值不能为空,而不是数据模型中的值,显然当您构造对象的新实例时,引用将为空。当引用为空时,您无法保留实体。

回答by unR

If you got here having the same problem with openJPAand Pascals solution is still not working for you, you might want to set the openJPA property openjpa.InverseManagerto truein your persistence.xml

如果您来到这里有同样的问题的OpenJPA和帕斯卡的解决方案仍然没有为你工作,你可能想OpenJPA的属性设置openjpa.InverseManager,以true在您的persistence.xml