ConstraintViolationException lors de l'enregistrement d'une association
Bonjour,
ça fait quelques jours que j'essaie d'implémenter une relation manyToMany porteuse d'information. Au final, j'ai utilisé la solution proposée par ce tuto : un POJO par entité (Membre, Questionnaire et ListeQuestionnaire dans mon cas) plus une entité pour la clé primaire composite (ListeQuestionnaireId).
J'ai un dao pour chacune des 3 entités. Aucun problème pour Membre et pour Questionnaire. Par contre les tests du dao de ListeQuestionnaire plantent :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("TestDao-context.xml")
@DirtiesContext
public class TestListeQuestionnaireDao {
@Autowired
private ListeQuestionnaireDao listeQuestionnaireDao;
@Autowired
private ServiceMembre serviceMembre;
@Autowired
private QuestionnaireDao questionnaireDao;
private ListeQuestionnaire listeQuestionnaire1, listeQ2;
private Membre benji, lola;
@Before
@Transactional
public void startTransaction() throws ParseException{
benji = new Membre();
benji.setIdMembre("ben.joris");
benji = serviceMembre.saveOne(benji);
Questionnaire q1 = new Questionnaire();
q1.setNumero("Q1");
q1.setTitre("Nutrition");
q1.setSexeDestinataire('b');
q1 = questionnaireDao.saveOne(q1);
System.out.println("--------- TEST : enregistrement listeQuestionnaire1...");
listeQuestionnaire1 = new ListeQuestionnaire();
listeQuestionnaire1.setaRepondu(false);
listeQuestionnaire1.setMembre(benji);
listeQuestionnaire1.setQuestionnaire(q1);
listeQuestionnaire1 = listeQuestionnaireDao.saveOne(listeQuestionnaire1);
System.out.println("--------- TEST : fin startTransaction...");
}
@Test
@Transactional
@Rollback
public void testFindAll() {
System.out.println("--------- TEST : testFindAll...");
List<ListeQuestionnaire> questionnaireAttendus = new ArrayList<ListeQuestionnaire>();
questionnaireAttendus.add(listeQuestionnaire1);
List<ListeQuestionnaire> questionnairesObtenus = listeQuestionnaireDao.findAll();
Assert.assertEquals(questionnaireAttendus, questionnairesObtenus);
}
@Test @Transactional
@Rollback
public void testCountAll() {
System.out.println("--------- TEST : testCountAll...");
Assert.assertEquals(1, listeQuestionnaireDao.countAll());
}
@Test @Transactional
@Rollback
public void testLoad() {
ListeQuestionnaire questionnaire = listeQuestionnaireDao.getOne(listeQuestionnaire1.getId());
Assert.assertEquals(listeQuestionnaire1, questionnaire);
}
@Test @Transactional
@Rollback
public void testDelete() {
listeQuestionnaireDao.deleteOne(listeQuestionnaire1);
listeQuestionnaire1 = listeQuestionnaireDao.getOne(listeQuestionnaire1.getId());
Assert.assertNull(listeQuestionnaire1);
} |
enfin, les tests findAll et countAll plantent, les deux autres passent.
pour les deux qui plantent, j'obtiens l'erreur :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1235)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1168)
at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:250)
at fr.statlife.protoE4N.data.dao.jpa.ListeQuestionnaireDaoImpl.findAll(ListeQuestionnaireDaoImpl.java:32)
at fr.statlife.protoE4N.data.dao.jpa.TestListeQuestionnaireDao.testFindAll(TestListeQuestionnaireDao.java:88)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:262)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:178)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:64)
at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:1175)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1251)
at org.hibernate.impl.QueryImpl.list(QueryImpl.java:102)
at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:241)
... 31 more
Caused by: java.sql.BatchUpdateException: Cannot add or update a child row: a foreign key constraint fails (`protoe4n`.`listequestionnaire`, CONSTRAINT `FK8C6EC97C87F51B2E` FOREIGN KEY (`idMembre`) REFERENCES `Membre` (`idMembre`))
at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:2024)
at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:1449)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
... 39 more
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`protoe4n`.`listequestionnaire`, CONSTRAINT `FK8C6EC97C87F51B2E` FOREIGN KEY (`idMembre`) REFERENCES `Membre` (`idMembre`))
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
at com.mysql.jdbc.Util.getInstance(Util.java:386)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1039)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3597)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3529)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1990)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2151)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2625)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2119)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2415)
at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:1976)
... 42 more |
sachant qu'en console j'ai les traces suivantes avant l'erreur :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| Hibernate:
select
membre0_.idMembre as idMembre1_1_,
membre0_.codeIdentification as codeIden2_1_1_,
membre0_.dateNaissance as dateNais3_1_1_,
membre0_.infosPerso_idInfosPerso as infosPerso6_1_1_,
membre0_.password as password1_1_,
membre0_.sexe as sexe1_1_,
infosperso1_.idInfosPerso as idInfosP1_2_0_,
infosperso1_.codePostal as codePostal2_0_,
infosperso1_.email as email2_0_,
infosperso1_.poids as poids2_0_,
infosperso1_.taille as taille2_0_,
infosperso1_.ville as ville2_0_
from
Membre membre0_
left outer join
InfosPerso infosperso1_
on membre0_.infosPerso_idInfosPerso=infosperso1_.idInfosPerso
where
membre0_.idMembre=?
Hibernate:
insert
into
Questionnaire
(numero, sexeDestinataire, titre)
values
(?, ?, ?)
--------- TEST : enregistrement listeQuestionnaire1...
Hibernate:
select
listequest0_.idMembre as idMembre4_0_,
listequest0_.idQuestionnaire as idQuesti2_4_0_,
listequest0_.aRepondu as aRepondu4_0_
from
ListeQuestionnaire listequest0_
where
listequest0_.idMembre=?
and listequest0_.idQuestionnaire=?
--------- TEST : fin startTransaction...
--------- TEST : testFindAll...
Hibernate:
insert
into
ListeQuestionnaire
(aRepondu, idMembre, idQuestionnaire)
values
(?, ?, ?)
WARN - JDBCExceptionReporter - SQL Error: 1452, SQLState: 23000
ERROR - JDBCExceptionReporter - Cannot add or update a child row: a foreign key constraint fails (`protoe4n`.`listequestionnaire`, CONSTRAINT `FK8C6EC97C87F51B2E` FOREIGN KEY (`idMembre`) REFERENCES `Membre` (`idMembre`))
ERROR - tractFlushingEventListener - Could not synchronize database state with session |
Ce que ça me dis, c'est que l'enregistrement de ListeQuestionnaire n'est pas
fait tant qu'un autoFlush appelé par ma méthode findAll n'est pas appelé. Et aussi biensur qu'une contrainte de clé étrangère échoue.
Par contre, pour les deux tests qui plantent pas, il tente pas de faire d'insert de ListeQuestionnaire, y'a juste la requête select qui est appelée :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| Hibernate:
select
membre0_.idMembre as idMembre1_1_,
membre0_.codeIdentification as codeIden2_1_1_,
membre0_.dateNaissance as dateNais3_1_1_,
membre0_.infosPerso_idInfosPerso as infosPerso6_1_1_,
membre0_.password as password1_1_,
membre0_.sexe as sexe1_1_,
infosperso1_.idInfosPerso as idInfosP1_2_0_,
infosperso1_.codePostal as codePostal2_0_,
infosperso1_.email as email2_0_,
infosperso1_.poids as poids2_0_,
infosperso1_.taille as taille2_0_,
infosperso1_.ville as ville2_0_
from
Membre membre0_
left outer join
InfosPerso infosperso1_
on membre0_.infosPerso_idInfosPerso=infosperso1_.idInfosPerso
where
membre0_.idMembre=?
Hibernate:
insert
into
Questionnaire
(numero, sexeDestinataire, titre)
values
(?, ?, ?)
--------- TEST : enregistrement listeQuestionnaire1...
Hibernate:
select
listequest0_.idMembre as idMembre4_0_,
listequest0_.idQuestionnaire as idQuesti2_4_0_,
listequest0_.aRepondu as aRepondu4_0_
from
ListeQuestionnaire listequest0_
where
listequest0_.idMembre=?
and listequest0_.idQuestionnaire=?
--------- TEST : fin startTransaction... |
Donc le getOne et le deleteOne fonctionnent, mais j'ai pas l'impression qu'ils interagissent avec la base de données, mais plutôt que ça interagit juste avec le contexte de persistance... Enfin, je vois ça de mes yeux de débutante sur le sujet, donc je me trompe peut être :aie:
Je suis débutante avec spring JPA et hibernate, mais j'ai suivi pas mal de tutos sur le sujet. Cela dit, je manque quand même d'expérience sur le sujet et j'arrive pas à comprendre d'où vient le problème d'échec de la clé étrangère, ni pourquoi il sauvegarde pas mon Objet ListeQuestionnaire au moment où j'appelle la méthode saveOne du Dao...
voici le code du dao :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| public abstract class AbstractDaoJPAImpl<T extends DomainObject> implements Dao<T> {
private Class<T> domainClass;
@PersistenceContext(type=PersistenceContextType.EXTENDED)
protected EntityManager em;
public AbstractDaoJPAImpl(Class<T> domainClass) {
this.domainClass = domainClass;
}
public void deleteOne(T object)
{
em.remove(object);
}
public T getOne(Serializable id)
{
return (T) em.find(domainClass, id);
}
public T saveOne(T object)
{
return em.merge(object);
}
/*
* Getters and setters
*/
public EntityManager getEm() {
return em;
}
public void setEm(EntityManager em) {
this.em = em;
}
} |
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Repository
public class ListeQuestionnaireDaoImpl extends AbstractDaoJPAImpl<ListeQuestionnaire> implements
ListeQuestionnaireDao {
public ListeQuestionnaireDaoImpl(){
super(ListeQuestionnaire.class);
}
public List<ListeQuestionnaire> findAll() {
TypedQuery<ListeQuestionnaire> query = em.createQuery("select listeQ from ListeQuestionnaire listeQ ",
ListeQuestionnaire.class);
return query.getResultList();
}
public int countAll() {
TypedQuery<Long> query = em.createQuery("select count(listeQ) from ListeQuestionnaire listeQ", Long.class);
return (query.getSingleResult()).intValue();
}
} |
un peu d'aide ou des conseils pour combler mes zones d'ombres seraient les bienvenus :mrgreen: