Будьте в спящем режиме @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 будет пустым временно).

При попытке этого я вижу выданное исключение not-null property references a null or transient value.

Я хотел бы постараться не ослаблять ограничение, если это возможно, потому что должны присутствовать обе стороны.

13
задан Pascal Thivent 28 April 2010 в 07:09
поделиться

2 ответа

Допустимо ли объявлять @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());
    }
}
18
ответ дан 1 December 2019 в 23:31
поделиться

Оно должно сохранять временное значение из-за вашего каскадного типа.

Если вы на самом деле пытаетесь сохранить первый элемент до того, как зададите другой переходный элемент, вы ожидаете этой ошибки.

Указанное вами ограничение только указывает, что значение не может быть нулевым в базе данных, а не в модели данных, очевидно, когда вы создаете новый экземпляр объекта, ссылка будет нулевой. Пока ссылка пуста, вы не можете сохранить сущность.

0
ответ дан 1 December 2019 в 23:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: