15

@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ます。

両側が存在する必要があるため、可能であれば制約を緩和することは避けたいと思います。

4

3 に答える 3

20

宣言することは有効で@OneToOneあり@NotNull、関係の両側で(...)これが無効であると言うものは何も見つかりませんが、永続化中に関係の少なくとも一方の側に違反する必要があるようです。changeEntry(たとえば、最初に書き込む場合、changeEntryDetails一時的に null になります)。

それは有効であり、適切にマッピングされたエンティティですべてが正常に機能します。双方向関連の片側を「所有」側として宣言する必要があります (これは、挿入の順序を「制御」します)。考えられる実用的な解決策の 1 つ:

@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());
    }
}
于 2010-04-28T01:11:06.790 に答える
0

openJPA で同じ問題が発生し、Pascalsソリューションがまだ機能しない場合は、persistence.xml でopenJPA プロパティopenjpa.InverseManagerをに設定することをお勧めします。true

于 2011-06-15T12:38:57.680 に答える
0

カスケード型のため、一時的な値を永続化する必要があります。

他の一時的な要素を設定する前に、実際に最初の要素を永続化しようとしている場合は、このエラーが予想されます。

指定した制約は、データ モデルではなくデータベースで値を null にすることはできないことのみを指定しています。オブジェクトの新しいインスタンスを作成すると、参照は null になります。参照が null の間は、エンティティを永続化できません。

于 2010-04-27T21:05:55.503 に答える