java 休眠异常:键“PRIMARY”的重复条目

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37528962/
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-11-03 02:35:33  来源:igfitidea点击:

Hibernate exception: Duplicate entry for key 'PRIMARY'

javamysqlhibernatejpa

提问by RubioRic

I've these (simplified) tables in my database

我的数据库中有这些(简化的)表

enter image description here

在此处输入图片说明

I've mapped the tables with these (simplified) two classes

我已经用这些(简化的)两个类映射了表

COMPANY

公司

@Entity
public class Company {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
    @JoinTable(name="company_employee", 
    joinColumns={@JoinColumn(name="company_id")},
    inverseJoinColumns={@JoinColumn(name="employee_id")})
    @OrderBy("name ASC")
    private SortedSet<Employee> employees = new TreeSet<Employee>();

    // contructors + getters + setters
 }

EMPLOYEE

员工

@Entity
public class Employee implements Comparable<Employee> {
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;

     private String name;

     @OneToOne(fetch = FetchType.LAZY, optional = true)
     @JoinTable(name="company_employee", 
        joinColumns={@JoinColumn(name="employee_id")},
        inverseJoinColumns={@JoinColumn(name="company_id")}
     )
     private Company company;

     @Override
     public int compareTo(Employee anotherEmployee) {
          return this.name.compareTo(anotherEmployee.name);
     }

     // contructors + getters + setters + equals + hashcode
 }

I'm using a SortedSet/TreeSetin employees defined in Companyto obtain the employees sorted by name.

我使用SortedSet/TreeSetin 定义Company的员工来获取按名称排序的员工。

The problem arises when I persist the objects. I've read that you must establish the relation in both sides. I set the employee in company and the company in the employee before persisting the company. When I try to execute the persist, I'm obtaining the following exception

当我坚持对象时出现问题。我读过你必须建立双方的关系。在坚持公司之前,我将员工设置为公司,将公司设置为员工。当我尝试执行持久化时,我收到以下异常

Exception in thread "main" javax.persistence.RollbackException: Error while committing the transaction
    at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:86)
    at es.rubioric.hibernate.MainTest.main(MainTest.java:43)
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1692)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1602)
    at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:67)
    ... 1 more
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
    at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:59)
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:109)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:95)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:207)
    at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:45)
    at org.hibernate.persister.collection.AbstractCollectionPersister.recreate(AbstractCollectionPersister.java:1314)
    at org.hibernate.action.internal.CollectionRecreateAction.execute(CollectionRecreateAction.java:50)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:447)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:333)
    at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:335)
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
    at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1224)
    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:464)
    at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:2890)
    at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2266)
    at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:485)
    at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:146)
    at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access0(JdbcResourceLocalTransactionCoordinatorImpl.java:38)
    at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:230)
    at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:65)
    at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:61)
    ... 1 more
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '32-80' for key 'PRIMARY'
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:406)
    at com.mysql.jdbc.Util.getInstance(Util.java:381)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1015)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3558)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3490)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1959)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2109)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2643)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2077)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2362)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2280)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2265)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:204)
    ... 18 more

This is a simplified code that generates that exception. [EDITED commit location after comment by @NicolasFilotto]

这是生成该异常的简化代码。[在@NicolasFilotto 发表评论后编辑提交位置]

    public static void main(String[] args) throws ParseException {

         EntityManagerFactory emf = Persistence.createEntityManagerFactory("TestDB");

         EntityManager em = emf.createEntityManager();
         EntityTransaction tx = em.getTransaction();
         tx.begin();

        try {
             Company c = new Company("Nike");

             Employee e1 = new Employee("Anthony");
             e1.setCompany(c);  // line 26
             Employee e2 = new Employee("Zenobia");
             e2.setCompany(c);   // line 28
             Employee e3 = new Employee("Chuck");
             e3.setCompany(c);   // line 30
             Employee e4 = new Employee("Bernard");
             e4.setCompany(c);  // line 32

             c.getEmployees().add(e1);
             c.getEmployees().add(e2);
             c.getEmployees().add(e3);
             c.getEmployees().add(e4);

             em.persist(c);

             tx.commit();

        } finally {
            em.close();
        }
    }

These are the SQL sentences internally executed by Hibernate.

这些是 Hibernate 内部执行的 SQL 语句。

Hibernate: insert into Company (name) values (?)
Hibernate: insert into Employee (name) values (?)
Hibernate: insert into company_employee (company_id, employee_id) values (?, ?)
Hibernate: insert into Employee (name) values (?)
Hibernate: insert into company_employee (company_id, employee_id) values (?, ?)
Hibernate: insert into Employee (name) values (?)
Hibernate: insert into company_employee (company_id, employee_id) values (?, ?)
Hibernate: insert into Employee (name) values (?)
Hibernate: insert into company_employee (company_id, employee_id) values (?, ?)
Hibernate: insert into company_employee (company_id, employee_id) values (?, ?)

The same code works perfectly if I comment lines 26, 28, 30 and 32 (marked above). But I want to know why is that exception being generated. Why the duplicated key?

如果我注释第 26、28、30 和 32 行(如上标记),则相同的代码可以完美运行。但我想知道为什么会产生异常。为什么是重复的密钥?

Thanks in advance.

提前致谢。

回答by Chris

The duplicates are caused by your company-employee relationship being duplicated in both entities. Each has been setup as a unidirectional 1:M (one side is using a 1:1 for some reason), and both are using the same join table, causing duplicates when JPA goes to insert entries for both sides.

重复是由您的公司-员工关系在两个实体中重复造成的。每个都设置为单向 1:M(一侧出于某种原因使用 1:1),并且两者都使用相同的连接表,当 JPA 为双方插入条目时会导致重复。

The solution is to mark one side as 'mappedby' the other.

解决方案是将一侧标记为“mappedby”另一侧。

@OneToMany( mappedBy="company", fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@OrderBy("name ASC")
private SortedSet<Employee> employees = new TreeSet<Employee>();