3

I have a One to Many Relationship between two entities, Pack and Board. When trying to do save/updates using a repository, I get a duplicate entry error. For some reason, hibernate is trying to update the "id" field of the Boards, which are the child in this relationship (the querys can be seen in the error log below).

Here is the code I'm working with:

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);
}
}

When trying to do some inserts and then updates such as this:

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);

The first call to jpaPackRepository.save() will work ONLY if the Boards table is empty. The second call to jpaPackRepository.save() will fail.

If the boards table is not empty, neither will work.

Here is the simple save code in my repository:

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

Lastly, here are the error console messages I'm getting:

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

And lastly, this curious line in the debug log:

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=?

I'm not at all sure why Hibernate would be trying to update an id field, as it is the primary key (As set by AbstractEntity, the class which Board extends).

Any ideas much appreciated! I'm completely stumped.

jacheson
  • 1,303
  • 2
  • 12
  • 16
  • http://stackoverflow.com/questions/12179770/mysql-1062-duplicate-entry-0-for-key-primary – Jay Apr 16 '14 at 09:39

1 Answers1

0

This error gives when you try to add a new row with the existing primary key id 0. Therefore you can you avoid duplicating the same primary key by adding @GeneratedValue(strategy=GenerationType.AUTO) to the primary key of the entity class. Using this, whatever you give as the primary key, Hibernate generates a new primary key.

Here is an example for auto generated primary key.

   @Id
   @GeneratedValue(strategy=GenerationType.AUTO)
   private int id;
Bumuthu Dilshan
  • 430
  • 5
  • 14