Hibernate @OneToMany 错误:键 'PRIMARY' 的重复条目 '0'

Posted

技术标签:

【中文标题】Hibernate @OneToMany 错误:键 \'PRIMARY\' 的重复条目 \'0\'【英文标题】:Hibernate @OneToMany error: Duplicate entry '0' for key 'PRIMARY'Hibernate @OneToMany 错误:键 'PRIMARY' 的重复条目 '0' 【发布时间】:2014-05-30 14:47:55 【问题描述】:

我在两个实体 Pack 和 Board 之间存在一对多关系。尝试使用存储库进行保存/更新时,出现重复输入错误。出于某种原因,hibernate 正在尝试更新 Boards 的“id”字段,它们是这种关系中的孩子(查询可以在下面的错误日志中看到)。

这是我正在使用的代码:

Pack.class

package com.vdts.lumberassets.domain;

import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.hibernate.annotations.IndexColumn;
import org.hibernate.annotations.Type;
import org.joda.time.DateTime;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name="packs")
public class Pack extends AbstractEntity
    private SimpleStringProperty slug = new SimpleStringProperty();
    private Load load;
    private List<Board> boards = new ArrayList<>();
    private Customer customer;
    private Vendor vendor;
    private DateTime dateTallied;
    private DateTime dateCreated;
    private DateTime dateModified;

    @PrePersist
    protected void onCreate() 
        dateCreated = new DateTime();
        dateModified = new DateTime();
    

    @PreUpdate
    protected void onUpdate() 
        dateModified = new DateTime();
    

    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER, orphanRemoval=true)
    @JoinColumn(name="pack_id")
    @IndexColumn(name="id")
    public List<Board> getBoards() 
        return boards;
    

    @Transient
    public ObservableList<Board> getBoardsObservable()
        return FXCollections.observableArrayList(boards);
    

    public void setBoards(List<Board> boards) 
        this.boards = boards;
    

    public void addBoard(Board board)
        this.boards.add(board);
    

    @ManyToOne(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
    @JoinColumn(name="load_id")
    public Load getLoad() 
        return load;
    

    public void setLoad(Load load) 
        this.load = load;
    

    @Column(name="slug")
    public String getSlug() 
        return slug.get();
    

    public SimpleStringProperty slugProperty() 
        return slug;
    

    public void setSlug(String slug) 
        this.slug.set(slug);
    

    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name="customer_id")
    public Customer getCustomer() 
        return customer;
    

    public void setCustomer(Customer customer) 
        this.customer = customer;
    

    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name="vendor_id")
    public Vendor getVendor() 
        return vendor;
    

    public void setVendor(Vendor vendor) 
        this.vendor = vendor;
    

    @Column(name="date_created", updatable = false)
    @Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
    public DateTime getDateCreated() 
        return dateCreated;
    

    public void setDateCreated(DateTime dateCreated) 
        this.dateCreated = dateCreated;
    

    @Column(name="date_modified")
    @Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
    public DateTime getDateModified() 
        return dateModified;
    

    public void setDateModified(DateTime dateModified) 
        this.dateModified = dateModified;
    

    @Column(name="date_tallied")
    @Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
    public DateTime getDateTallied() 
        return dateTallied;
    

    public void setDateTallied(DateTime dateTallied) 
        this.dateTallied = dateTallied;
    

Board.class

package com.vdts.lumberassets.domain;

import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import org.hibernate.annotations.Type;
import org.joda.time.DateTime;

import javax.persistence.*;

@Entity
@Table(name="boards")
public class Board extends AbstractEntity 

    private SimpleIntegerProperty surfaceMeasure = new SimpleIntegerProperty();

    private Thickness thickness;

    private Specie specie;

    private Grade grade;

    private Grader grader;

    private Pack pack;

    private SimpleDoubleProperty length = new SimpleDoubleProperty();

    private SimpleIntegerProperty width = new SimpleIntegerProperty();

    private DateTime dateCreated;

    private DateTime dateModified;

    private SimpleDoubleProperty price = new SimpleDoubleProperty();

    @PrePersist
    protected void onCreate() 
        dateCreated = new DateTime();
        dateModified = new DateTime();
    

    @PreUpdate
    protected void onUpdate() 
        dateModified = new DateTime();
    

    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name="pack_id")
    public Pack getPack() 
        return pack;
    

    public void setPack(Pack pack) 
        this.pack = pack;
    

    @Column(name="surface_measure")
    public int getSurfaceMeasure() 
        return surfaceMeasure.get();
    

    public SimpleIntegerProperty surfaceMeasureProperty() 
        return surfaceMeasure;
    

    public void setSurfaceMeasure(int surfaceMeasure) 
        this.surfaceMeasure.set(surfaceMeasure);
    

    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name="thickness_id")
    public Thickness getThickness() 
        return thickness;
    

    public void setThickness(Thickness thickness) 
        this.thickness = thickness;
    

    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name="specie_id")
    public Specie getSpecie() 
        return specie;
    

    public void setSpecie(Specie specie) 
        this.specie = specie;
    

    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name="grade_id")
    public Grade getGrade() 
        return grade;
    

    public void setGrade(Grade grade) 
        this.grade = grade;
    


    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name="grader_id")
    public Grader getGrader() 
        return grader;
    

    public void setGrader(Grader grader) 
        this.grader = grader;
    

    @Column(name="length")
    public double getLength() 
        return length.get();
    

    public SimpleDoubleProperty lengthProperty() 
        return length;
    

    public void setLength(double length) 
        this.length.set(length);
    

    @Column(name="width")
    public int getWidth() 
        return width.get();
    

    public SimpleIntegerProperty widthProperty() 
        return width;
    

    public void setWidth(int width) 
        this.width.set(width);
    

    @Column(name="date_created", updatable = false)
    @Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
    public DateTime getDateCreated() 
        return dateCreated;
    

    public void setDateCreated(DateTime dateCreated) 
        this.dateCreated = dateCreated;
    

    @Column(name="date_modified")
    @Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
    public DateTime getDateModified() 
        return dateModified;


public void setDateModified(DateTime dateModified) 
    this.dateModified = dateModified;


@Column(name="price")
public double getPrice() 
    return price.get();


public SimpleDoubleProperty priceProperty() 
    return price;


public void setPrice(double price) 
    this.price.set(price);


当尝试做一些插入然后更新时:

Pack pack = new Pack();
pack.addBoard(new Board());
pack.addBoard(new Board());
pack.addBoard(new Board());
ObservableList<Board> boards = pack.getBoardsObservable();
for (Board board : boards) 
    board.setLength(13.33);

jpaPackRepository.save(pack);
for (Board board : boards) 
    board.setLength(26.66);

jpaPackRepository.save(pack);

仅当 Boards 表为空时,对 jpaPackRepository.save() 的第一次调用才有效。第二次调用 jpaPackRepository.save() 将失败。

如果 board 表不为空,则两者都不起作用。

这是我存储库中的简单保存代码:

Session session = em.unwrap(Session.class);
session.saveOrUpdate(pack);
return pack;

最后,这是我收到的错误控制台消息:

Exception in thread "main" org.springframework.dao.DataIntegrityViolationException: Duplicate entry '0' for key 'PRIMARY'; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: Duplicate entry '0' for key 'PRIMARY'
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:188)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:154)
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:519)
    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.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    at com.sun.proxy.$Proxy55.save(Unknown Source)
    at com.vdts.lumberassets.impl.testclasses.TestBoards.run(TestBoards.java:191)
    at com.vdts.lumberassets.impl.RunLumberAssets.main(RunLumberAssets.java:15)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: org.hibernate.exception.ConstraintViolationException: Duplicate entry '0' for key 'PRIMARY'
    at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:74)
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:125)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:110)
    at org.hibernate.engine.jdbc.internal.proxy.AbstractStatementProxyHandler.continueInvocation(AbstractStatementProxyHandler.java:129)
    at org.hibernate.engine.jdbc.internal.proxy.AbstractProxyHandler.invoke(AbstractProxyHandler.java:81)
    at com.sun.proxy.$Proxy72.executeUpdate(Unknown Source)
    at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:56)
    at org.hibernate.persister.collection.AbstractCollectionPersister.recreate(AbstractCollectionPersister.java:1260)
    at org.hibernate.action.internal.CollectionRecreateAction.execute(CollectionRecreateAction.java:58)
    at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:362)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:354)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:279)
    at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:326)
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:52)
    at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1213)
    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:402)
    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)
    ... 15 more
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '0' for key 'PRIMARY'
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
    at com.mysql.jdbc.Util.getInstance(Util.java:386)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1041)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4237)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4169)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2617)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2778)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2825)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2156)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2459)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2376)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2360)
    at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
    at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.hibernate.engine.jdbc.internal.proxy.AbstractStatementProxyHandler.continueInvocation(AbstractStatementProxyHandler.java:122)
    ... 31 more

最后,调试日志中的这条奇怪的行:

21:28:55.699 [main] DEBUG o.h.p.c.AbstractCollectionPersister - Inserting collection: [com.vdts.lumberassets.domain.Pack.boards#27]
21:28:55.707 [main] DEBUG org.hibernate.SQL - update boards set pack_id=?, id=? where id=?

我完全不确定 Hibernate 为什么会尝试更新 id 字段,因为它是主键(由 Board 扩展的类 AbstractEntity 设置)。

非常感谢任何想法!我完全被难住了。

【问题讨论】:

***.com/questions/12179770/… 【参考方案1】:

当您尝试使用现有主键 id 0 添加新行时会出现此错误。因此,您可以通过将@GeneratedValue(strategy=GenerationType.AUTO) 添加到实体类的主键来避免重复相同的主键。使用它,无论您提供什么作为主键,Hibernate 都会生成一个新的主键。

这是自动生成主键的示例。

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

【讨论】:

以上是关于Hibernate @OneToMany 错误:键 'PRIMARY' 的重复条目 '0'的主要内容,如果未能解决你的问题,请参考以下文章

非主键之间的休眠关系OneToMany

码农小汪-Hibernate学习8-hibernate关联关系注解表示@OneToMany mappedBy @ManyToMany @JoinTable

使用 SpringBoot 和 Hibernate 与复合 pk 的双向 @OneToMany 关系

Hibernate/H2 @OneToMany 删除子时“违反参照完整性约束”?

JPA 2 / Hibernate 孤儿删除仍然无法与@OneToMany 一起使用?

Hibernate @OneToMany 关联尝试设置空 FK 值