web-dev-qa-db-fra.com

Pourquoi ai-je une violation de clé primaire pour une propriété @OneToMany?

J'ai une entité:

@Entity
public class Student {

    @GeneratedValue(strategy = GenerationType.AUTO)
    @Id
    private long id;

    @OneToMany
    private Set<Course> courses;
}

Lorsque j'essaie de conserver la première entité de ce type, cela fonctionne correctement, mais lorsque j'essaie de sauvegarder un nouvel étudiant avec le même cours que l'entité déjà stockée, il échoue. Voici l'erreur:

insert into student_courses (student, courses) values (?, ?) [23505-172]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause

org.h2.jdbc.JdbcSQLException: Unique index or primary key violation: "UK_1DBE7B943AC44E1B841DA2688EB_INDEX_5 ON PUBLIC.STUDENT_COURSES(COURSES)"; SQL statement:
insert into student_courses (student, courses) values (?, ?) [23505-172]
    at org.h2.message.DbException.getJdbcSQLException(DbException.Java:329)
    at org.h2.message.DbException.get(DbException.Java:169)
    at org.h2.message.DbException.get(DbException.Java:146)
    at org.h2.index.BaseIndex.getDuplicateKeyException(BaseIndex.Java:83)
    at org.h2.index.TreeIndex.add(TreeIndex.Java:65)
    at org.h2.table.RegularTable.addRow(RegularTable.Java:124)
    at org.h2.command.dml.Insert.insertRows(Insert.Java:126)
    at org.h2.command.dml.Insert.update(Insert.Java:86)
    at org.h2.command.CommandContainer.update(CommandContainer.Java:79)
    at org.h2.command.Command.executeUpdate(Command.Java:235)
    at org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.Java:154)
    at org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.Java:140)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.Java:133)
    at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.Java:58)
    at org.hibernate.persister.collection.AbstractCollectionPersister.recreate(AbstractCollectionPersister.Java:1256)
    at org.hibernate.action.internal.CollectionRecreateAction.execute(CollectionRecreateAction.Java:58)
    at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.Java:364)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.Java:356)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.Java:281)
    at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.Java:328)
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.Java:52)
    at org.hibernate.internal.SessionImpl.flush(SessionImpl.Java:1234)
    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.Java:404)
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.Java:101)
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.Java:175)
    at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.Java:75)
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.Java:515)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.Java:757)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.Java:726)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.Java:478)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.Java:272)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.Java:95)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:179)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.Java:158)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:179)
    at org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.Java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:179)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.Java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.Java:207)
    at com.Sun.proxy.$Proxy45.save(Unknown Source)
    at students.StudentController.createRandomShiur(ShiurimController.Java:66)
    at shiurim.StudentController.getStudents(StudentController.Java:26)
    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.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.Java:220)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.Java:133)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.Java:104)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.Java:746)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.Java:687)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.Java:83)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.Java:925)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.Java:856)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.Java:946)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.Java:837)
    at javax.servlet.http.HttpServlet.service(HttpServlet.Java:621)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.Java:822)
    at javax.servlet.http.HttpServlet.service(HttpServlet.Java:728)
    at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:305)
    at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:210)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.Java:77)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.Java:108)
    at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:243)
    at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:210)
    at org.Apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.Java:222)
    at org.Apache.catalina.core.StandardContextValve.invoke(StandardContextValve.Java:123)
    at org.Apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.Java:502)
    at org.Apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.Java:680)
    at org.Apache.catalina.core.StandardHostValve.invoke(StandardHostValve.Java:171)
    at org.Apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.Java:99)
    at org.Apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.Java:118)
    at org.Apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.Java:408)
    at org.Apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.Java:1023)
    at org.Apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.Java:589)
    at org.Apache.Tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.Java:1686)
    at Java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.Java:895)
    at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:918)
    at Java.lang.Thread.run(Thread.Java:695)

J'utilise Spring Data JPA avec Hibernate et H2 comme base de données.

L'application a créé ses propres tables de base de données:

HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(false);
hibernateJpaVendorAdapter.setGenerateDdl(true);
hibernateJpaVendorAdapter.setDatabase(Database.H2);

Alors, pourquoi y aurait-il une contrainte unique?

Mettre à jour:

Je vois que Hibernate a ajouté la contrainte:

Hibernate: alter table student_courses add constraint UK_a8c48f36df374f1293c46699c83 unique (courses)

Comment puis-je dire à Hibernate de ne pas créer cela?

Merci!

13
Adam

Étant donné que la violation se produit dans la table STUDENT_COURSES, il semble que vous tentiez de conserver deux fois la même relation. Vous avez mappé une Set, qui indique à Hibernate que ces relations ne doivent pas se produire plus d'une fois. Recherchez dans le DDL généré des index uniques sur COURSE_ID, STUDENT_ID).

La raison peut être une faille dans la logique de votre programme (par exemple, la modification de champs pertinents pour equals sur Courses après leur ajout à l'ensemble ou un equals défectueux dans votre entité Course.

Vous devez décider vous-même (votre client dans le monde réel, puisqu'il s'agit d'une décision commerciale), si un Student peut participer à un Course plusieurs fois (par exemple, il a échoué la première fois).

11
atamanroman

Ce n'est pas le problème avec Sequence/JPA /hibern.Selon votre journal, l'erreur est survenue sur student_courses 

insert into student_courses 

UK_1DBE7B943AC44E1B841DA2688EB_INDEX_5

Il semble que vous ayez une contrainte unique dans la table student_course avec des cours. supprimez cette contrainte unquie/vous devrez peut-être ajouter un composite d'étudiant et de cours pour la contrainte unquie et pas seulement les cours.

0
Mani

Vous devez utiliser GenerationType.TABLE au lieu de GenerationType.AUTO. De cette façon, jpa utilise une table de séquence pour l'attribution d'identifiant et vous n'aurez peut-être jamais besoin de générer de valeur de séquence ou d'incrémentation automatique ni de déclencheurs réduisant la portabilité.

0
bNd