【问题标题】:Why am I getting a Primary Key violation for an @OneToMany property?为什么我的 @OneToMany 属性违反主键?
【发布时间】:2014-01-15 07:27:47
【问题描述】:

我有一个实体:

@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 不要创建它?

谢谢!

【问题讨论】:

    标签: java hibernate jpa h2 spring-data-jpa


    【解决方案1】:

    由于违规发生在STUDENT_COURSES 表中,您似乎试图将相同的关系保持两次。您映射了一个Set,它向 Hibernate 发出信号,表明这些关系必须不超过一次。检查生成的 DDL 是否有关于 COURSE_ID、STUDENT_ID 的唯一索引。

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

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

    【讨论】:

    • 这是试图插入两个学生相同的课程。学生是独一无二的。课程是一样的。为什么 Hibernate 会在那里创建唯一索引?
    • 请出示您的Courses实体。看起来你的映射是错误的。可能是@ManyToMany,或者你在弱侧缺少mappedBy,如果映射在两边。
    • 是的,你是对的,我应该使用@ManyToMany。我在写实体的时候只想到一个学生参加多门课程,但是由于多个学生可以参加同一门课程,所以真的是@ManyToMany,解决了这个问题,谢谢!
    【解决方案2】:

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

    【讨论】:

    • 那行不通。我将学生和课程的 ID 更改为 GenerationType.TABLE 而不是 AUTO。我仍然遇到同样的错误。
    【解决方案3】:

    很可能您设置了@OneToMany,而您实际上有@ManyToMany 关系。

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

    【讨论】:

      【解决方案4】:

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

      insert into student_courses 
      
      UK_1DBE7B943AC44E1B841DA2688EB_INDEX_5
      

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

      【讨论】:

      • 约束和表都是应用自己生成的{{hibernateJpaVendorAdapter.setGenerateDdl(true)}},那么问题就变成了Hibernate为什么要创建那个约束?
      • 由于您的课程已设置,因此假定它具有 unquie。您可以使用@CollectionTable 注释来避免这种情况。检查stackoverflow.com/questions/13123076/…
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多