J'essaie de conserver deux entités différentes à l'aide de JPA1, avec l'implémentation d'Hibernate . Le code correspondant est tel qu'illustré ci-dessous:
Classe d'entité parent
@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;
}
Classe d'entité enfant
@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;
}
}
Cas de test
@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
}
}
Gestionnaire d'entités et transaction sur application.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>
En essayant d'insérer l'objet parent, hibernate lève une PropertyValueException, indiquant que l'enfant est null ou transitoire, même si cet enfant a été créé et persistant avant cette opération. La chose étrange est que cela échoue seulement sur le test unitaire, et dans l'application réelle, avec un enfant pré-inséré, cela fonctionne comme prévu.
PS: .__ Je sais que je pourrais mapper un enfant avec une cascade persistante, mais ce n’est pas l’idée ici. Je veux juste vérifier si ces deux travaillent indépendamment.
Le problème ici est que vous persistez dans la table parent avec les valeurs définies . Quand elle persiste, elle a besoin de l'id de la table enfant, qui doit déjà être conservé car il s'agissait d'une clé étrangère et, par conséquent, une référence non nulle. une valeur nulle.
@Test
public void testSave() {
Child child = new Child();
child.setId(1);
entityManager.persist(child);
Parent parent = new Parent();
parent.setChild(child);
entityManager.persist(parent);
}
Essayez ceci pour enregistrer d’abord l’enfant, puis le parent.
La première chose que vous utilisez @ManyToOne dans Parent, qui me dit que vous rencontrez plus d'un parent pour un objet Child, je suppose que ce n'est pas le cas.
Selon votre structure de classe, je peux comprendre que vous ayez un mappage OneToOne entre des entités parent et enfant.
En ce qui concerne l'exception, y a-t-il une raison pour laquelle vous n'utilisez pas de cascades entre parents et enfants pour gérer le mappage automatiquement pour l'enregistrement, la mise à jour et la suppression? Si vous n'y avez pas pensé, essayez-le en définissant la configuration ci-dessous, comme indiqué ici: Exemple Parent/Enfant - Hibernate
cascade = {CascadeType.ALL}
Bien que je suggère de changer le mappage de ManyToOne à OneToOne entre Child et Parent.
S'il vous plaît, faites-moi savoir.
En plus d'avoir un problème de parent avec un FK à un enfant, l'ordre persistant est la cause de votre problème.
Votre problème est lié au rinçage . Ce n'est pas parce que vous avez demandé à Hibernate de conserver un objet qu'il réponde automatiquement à votre demande. La demande de persistance se rend dans une file d'attente d'actions pour être uniquement matérialisée au moment du vidage. Donc, votre deuxième persistance trouve simplement l'entité parent sans un enfant "persistant".
Vous pouvez simplement corriger votre code comme suit:
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;
Essaye ça:
@ManyToOne(fetch = FetchType.LAZY,optional=false)
@JoinColun(name = "child_id", nullable = "false")
public Child getChild() {
return child;
}
Certains champs sont définis avec not-null="true"
vous devez fournir une valeur pour cela avant de sauvegarder dans db . Pour tous les champs de la table parent et pour la table enfant, tous les champs non nuls avec la valeur appropriée
Essayez de donner @ManyToOne(fetch = FetchType.LAZY,cascade=PERSIST, mappedBy="parent")
pour public Child getChild()
et de ne conserver que l'objet parent.