Java 为什么我会收到 @OneToMany 属性的主键违规?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20805188/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 04:27:59  来源:igfitidea点击:

Why am I getting a Primary Key violation for an @OneToMany property?

javahibernatejpah2spring-data-jpa

提问by Adam

I have an entity:

我有一个实体:

@Entity
public class Student {

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

    @OneToMany
    private Set<Course> courses;
}

When I try to persist the first entity of this type it works fine, but then when I try to save a new Student with the same course as the already stored entity it fails. Here is the error:

当我尝试保留这种类型的第一个实体时,它工作正常,但是当我尝试使用与已存储实体相同的课程保存新学生时,它失败了。这是错误:

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)

I am using Spring Data JPA with Hibernate and H2 as the database.

我使用带有 Hibernate 和 H2 作为数据库的 Spring Data JPA。

The application created its own database tables:

该应用程序创建了自己的数据库表:

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

So, why would there be a unique constraint?

那么,为什么会有唯一约束呢?

Update:

更新:

I see that Hibernate added the constraint:

我看到 Hibernate 添加了约束:

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

How do I tell Hibernate not to create that?

我如何告诉 Hibernate 不要创建它?

Thanks!

谢谢!

采纳答案by atamanroman

Since the violation happens in the STUDENT_COURSEStable, it seems like you are trying to persist the same relationship twice. You mapped a Set, which signals Hibernate that these relationships must occur not more than once. Check the generated DDL for unique indexes on COURSE_ID, STUDENT_ID).

由于违规发生在STUDENT_COURSES表中,因此您似乎试图两次保持相同的关系。您映射了 a Set,它向 Hibernate 发出信号,这些关系不能多次出现。检查生成的 DDL 以获取 COURSE_ID、STUDENT_ID 上的唯一索引)。

The reason may be a flaw in your program logic (eg. modifying fields relevant to equalson Courses after they are added to the set or a faulty equalsin your Courseentity.

原因可能是您的程序逻辑存在缺陷(例如,在将与equalson Courses 添加到集合中后修改相关字段或equals您的Course实体中存在错误。

You have to decide yourself (your client in the real world, since this is a business decision), if a Studentcan participate in a Coursemore than once (e.g. failed the first time).

您必须自己决定(您在现实世界中的客户,因为这是一个商业决策),是否Student可以Course多次参与(例如第一次失败)。

回答by bNd

You have to use GenerationType.TABLEinstead of GenerationType.AUTO. That way, jpa uses a sequence table for id assignment and you may never need to generate sequence or auto-increment values or triggers that lowers portability.

你必须使用GenerationType.TABLE而不是GenerationType.AUTO. 这样,jpa 使用序列表进行 id 分配,您可能永远不需要生成降低可移植性的序列或自动递增值或触发器。

回答by Mani

It is not the problem with Sequence / JPA /hibern.As per your log the error happend on student_courses

这不是 Sequence/JPA/hibern 的问题。根据您的日志,错误发生在 student_courses 上

insert into student_courses 

UK_1DBE7B943AC44E1B841DA2688EB_INDEX_5

Seems , you have unquie constraint in student_course table with courses. remove that unquie constraint / you may need to add composite of student and courses for unquie constraint not just courses.

似乎,您在带有课程的 student_course 表中有 unquie 约束。删除该 unquie 约束/您可能需要为 unquie 约束添加学生和课程的组合,而不仅仅是课程。

回答by Ari

The is likely that you set @OneToManywhile you actually have a @ManyToManyrelationship.

这很可能是您@OneToMany在实际建立@ManyToMany关系时设定的。

Assuming that Coursehas no autogenerated Id, then there will be a problem as soon as the second student will add a Course with the same name.
Since it is a one to many relationship a new course will be created with the same unique restricted Id

假设Course没有自动生成的Id,那么只要第二个学生添加一个同名的课程就会出现问题。
由于它是一对多的关系,因此将创建具有相同唯一受限 ID 的新课程