2014-11-24 13 views
9

Próbuję zachować dwie różne jednostki przy użyciu JPA1, z implementacją Hibernate. kod na to, jak pokazano poniżej:Właściwość nie-pusta odwołuje się do wartości pustej lub przejściowej dla wartości trwałej

jednostka nadrzędna klasa

@Entity 
@Table(name = "parent") 
public class Parent implements Serializable { 

    {...} 
    private Child child; 

    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "child_id", nullable = "false") 
    public Child getChild() { 
     return child; 
    } 

    public void setChild(Child child) { 
     this.child = child; 
} 

klasa jednostka Dziecko

@Entity 
@Table(name = "child") 
public class Child implements Serializable { 

    private Integer id; 

    @Id 
    @Column(name = "child_id") 
    public Integer getId() { 
     return id; 
    } 

    public void setId(Integer id) { 
     this.id = id; 
    } 
} 

przypadek testowy

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration("classpath:META-INF/application.xml") 
@Transactional 
public class ParentTest extends TestCase { 

    @PersistenceContext 
    private EntityManager entityManager; 

    @Test 
    public void testSave() { 
     Child child = new Child(); 
     child.setId(1); 

     Parent parent = new Parent(); 
     parent.setChild(child); 

     entityManager.persist(parent.getChild()); 
     entityManager.persist(parent); // throws the exception 
    } 
} 

kierownik jednostki i transakcja na aplikacji. xml

<tx:annotation-driven transaction-manager="transactionManager" /> 

<jee:jndi-lookup id="dataSource" jndi-name="java:/jdbc/myds" expected-type="javax.sql.DataSource" /> 

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
    <property name="packagesToScan" value="com.mypackage" /> 
    <property name="dataSource" ref="dataSource" /> 
    <property name="jpaVendorAdapter"› 
     <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/> 
    </property> 
    <property name="jpaProperties"> 
     <props> 
      <prop key="hibernate.dialect>org.hibernate.dialect.Oracle10gDialect</prop> 
     </props> 
    </property> 
</bean> 

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 
    <property name="entityManagerFactory" ref="entityManagerFactory" /> 
</bean> 

Podczas próby wstawienia obiektu nadrzędnego hibernacja powoduje wygenerowanie wyjątku PropertyValueException, informując, że dziecko ma wartość zerową lub jest tymczasowe, mimo że element potomny został utworzony i zachowywał się przed tą operacją. Dziwne jest to, że test kończy się niepowodzeniem, aw prawdziwej aplikacji z wcześniej wstawionym dzieckiem działa to zgodnie z oczekiwaniami.

PS: Jestem całkiem świadomy, że mogę mapować dziecko z kaskadą, ale nie jest to ideą tutaj. Chcę tylko sprawdzić, czy te dwie działają niezależnie.

+0

Jakim typem jest 'EntityManager'? Nie mogę znaleźć żadnego odniesienia do metody "insert()". –

+0

Myślę, że może to być związane z obsługą transakcji w metodzie Junit. Rodzic można wstawić tylko wtedy, gdy dziecko jest już w bazie danych (po zatwierdzeniu). To wyjaśniałoby, dlaczego nie działa tutaj na twojej "prawdziwej" aplikacji. Czy możesz udostępnić swój plik application.xml? – santedicola

+0

@PredragMaric my bad. Używam metody persist. Naprawiono w poście. – renke

Odpowiedz

2

Oprócz tego, że rodzic ma problem z FK na dziecko, przyczyną problemu jest kolejność.

Twój problem to related to flushing. Tylko dlatego, że poinstruowałeś Hibernate, aby utrzymywał obiekt, nie oznacza to, że automatycznie obsługuje twoją prośbę. Żądanie trwałości trafia do kolejki akcji tylko do zmaterializowania w czasie spłukiwania. Tak więc twoja druga uparta po prostu znajduje istotę Rodzicielską bez rzeczywistego "upartego" Dziecka.

można po prostu naprawić swój kod w następujący sposób:

Child child = new Child(); 
    child.setId(1); 

    entityManager.persist(parent.getChild()); 
    entityManager.flush(); 

    Parent parent = new Parent(); 
    parent.setChild(child); 
    entityManager.persist(parent); 
    entityManager.flush; 
+0

W tym przypadku element potomny ma ustawiony ręcznie identyfikator, dlatego nie trzeba go przepłukiwać do bazy danych, aby był dostępny i stał się trwały w kontekście. Mimo to, próbowałem opróżniać menadżera podmiotu po każdym uporczywie i wciąż nie miałem szczęścia. – renke

7

Problemem jest tutaj jesteś przechowując tabeli nadrzędnej z ustalonymi wartościami. Po przejściu do trybu persist wymaga identyfikatora tabeli podrzędnej, który musi być utrwalony, tak jak był to klucz obcy, a zatem jest to właściwość null odwołująca się do wartości pustej.

@Test 
    public void testSave() { 
     Child child = new Child(); 
     child.setId(1); 
     entityManager.persist(child); 

     Parent parent = new Parent(); 
     parent.setChild(child); 

     entityManager.persist(parent); 
    } 

Spróbuj tego, aby zapisać dziecko, a potem nadrzędnego .Else zmianę mapowania

+0

W podanym fragmencie utrzymuję dziecko również przed rodzicem, z tą różnicą, że ustawiłeś dziecko po nim. To nie robi nic innego niż to, co zrobiłem, ponieważ utrzymuję odniesienie do potomka, a odwzorowanie relacji nie jest dwukierunkowe. – renke

0

Niektóre pole jest ustawione z not-null="true" musisz podać wartość dla że przed saveing ​​do DB. Dla wszystkich pól w tabeli nadrzędnej i dla tabeli podrzędnej ustawić wszystkie NOT NULL pola z odpowiednią wartością

+0

... co dokładnie robię. – renke

0

Spróbuj dając @ManyToOne(fetch = FetchType.LAZY,cascade=PERSIST, mappedBy="parent") dla public Child getChild() i utrzymują się tylko rodzic object.Both będzie trwało.

1

Spróbuj tego:

@ManyToOne(fetch = FetchType.LAZY,optional=false) 
@JoinColun(name = "child_id", nullable = "false") 
public Child getChild() { 
    return child; 
} 
2

Pierwszą rzeczą używasz @ManyToOne w dominującej, która mówi mi, że masz więcej niż jedno z rodziców dziecka do jednego obiektu, myślę, że nie jest to przypadek.

Zgodnie z twoją strukturą klas mogę zrozumieć, że masz mapowanie OneToOne między jednostkami nadrzędnymi i podrzędnymi.

Jeśli chodzi o wyjątek, czy istnieje powód, dla którego nie używasz kaskad pomiędzy rodzicem i dzieckiem, aby automatycznie obsługiwać mapowanie w celu zapisania, aktualizacji i usunięcia? Jeśli nie myślał o niej, niż można spróbować poprzez ustawienie poniżej konfiguracji, jak określono tutaj: Example Parent/Child - Hibernate

cascade = {CascadeType.ALL} 

Choć chciałbym zaproponować, aby zmienić mapowanie z ManyToOne do OneToOne pomiędzy dziecka i rodziców.

Proszę dać mi znać.

Powiązane problemy