web-dev-qa-db-ja.com

@OneToManyプロパティの主キー違反が発生するのはなぜですか?

私はエンティティを持っています:

@Entity
public class Student {

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

    @OneToMany
    private Set<Course> courses;
}

このタイプの最初のエンティティを永続化しようとすると正常に機能しますが、すでに保存されているエンティティと同じコースで新しい生徒を保存しようとすると失敗します。ここにエラーがあります:

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)

データベースとしてHibernateとH2を備えたSpring Data JPAを使用しています。

アプリケーションは独自のデータベーステーブルを作成しました。

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

では、なぜ固有の制約があるのでしょうか?

更新:

Hibernateが制約を追加したことがわかります。

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

Hibernateにそれを作成しないように指示するにはどうすればよいですか?

ありがとう!

15
Adam

違反はSTUDENT_COURSESテーブル、同じ関係を2度持続しようとしているようです。 Setをマップしました。これは、これらの関係が1回だけ発生する必要があることをHibernateに通知します。生成されたDDLでCOURSE_ID、STUDENT_IDの一意のインデックスを確認します。

理由は、プログラムロジックの欠陥(たとえば、equalsに関連するフィールドをセットに追加した後にCourseに関連するフィールドを変更するか、equalsエンティティの誤ったCourseにある可能性があります。

StudentCourseに複数回参加できる場合(例:初めて失敗した場合)は、自分自身(実際のクライアントはビジネス上の決定であるため)を決定する必要があります。

11
atamanroman

Sequence/JPA/hibernの問題ではありません。ログによると、student_coursesでエラーが発生しました

insert into student_courses 

UK_1DBE7B943AC44E1B841DA2688EB_INDEX_5

コースがあると、student_courseテーブルにunquie制約があるようです。その不穏な制約を削除します。コースだけでなく、不穏な制約のために学生とコースの複合を追加する必要がある場合があります。

1
Mani

GenerationType.TABLEの代わりにGenerationType.AUTOを使用する必要があります。そうすることで、JPAはIDの割り当てにシーケンステーブルを使用するため、シーケンス値や自動インクリメント値、または移植性を低下させるトリガーを生成する必要がなくなる場合があります。

1
bNd

@OneToMany実際に@ManyToMany 関係。

Courseに自動生成されたIDがないと仮定すると、2人目の生徒が同じ名前のコースを追加するとすぐに問題が発生します。
1対多の関係であるため、同じ一意の制限付きIDで新しいコースが作成されます

0
Ari