带有连接表的双向@ManyToOne会创建重复键

时间:2016-01-25 03:29:21

标签: java hibernate jpa jointable hibernate-onetomany

我正在使用 hibernate 连接表实现 @ManyToOne 双向关系,但是当我为了保留一些数据,hibernate声称关系表中的记录被插入两次,违反了唯一约束,如下面的错误消息所示:

ERROR: org.hibernate.engine.jdbc.spi.SqlExceptionHelper - ERROR: duplicate key value violates unique constraint "tillage_sample_pkey"
Detail: Key (id_tillage, id_sample)=(82, 110) already exists.

我有以下表格:

  • 耕作(id,其他一些数据)(One Tillage可以有很多样本)
  • 示例(id,其他一些数据)(一个样本只能有一个耕作)
  • tillage_sample(id_tillage,id_sample)PK(id_tillage,id_sample)

当我创建耕种对象时,我会填充示例。在示例对象中,我指向耕种对象,创建“双重绑定”

我猜这个“双重绑定”造成了麻烦,因为当保存耕作并且当它试图将耕作持续存在于Tillage内时,休眠/样本关系会被hibernate持久化样品(与耕作对象相同)。

这里是我的代码,以帮助您理解我的问题:

Tillage.java

@Entity
@Table(name = "tillage")
public class Tillage implements Serializable {

private static final long serialVersionUID = 3605331584324240290L;

@Id
@GeneratedValue(generator = "tillage_id_seq", strategy = GenerationType.SEQUENCE)
@SequenceGenerator(name = "tillage_id_seq", sequenceName = "tillage_id_seq", allocationSize = 1)
private Integer id;

@Column(name = "name")
private String name;
// Other simple attributes

@ManyToOne
@JoinColumn(name = "id_farm")
@JsonBackReference
private Farm farm;

// This relation is the problematic one
@OneToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "tillage_sample",
           joinColumns = { @JoinColumn(name = "id_tillage") }, 
           inverseJoinColumns = {@JoinColumn(name = "id_sample") })
private List<Sample> sampleList;

// Although similar, this one is doing OK
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "tillage_owner", 
           joinColumns = { @JoinColumn(name = "id_tillage") }, 
           inverseJoinColumns = {@JoinColumn(name = "id_owner") })
private List<Owner> ownerList;

// getters & setters
}

Sample.java

@Entity
@Table(name = "sample")
public class Sample implements Serializable {

private static final long serialVersionUID = 7064809078222302493L;

@Id
@GeneratedValue(generator = "sample_id_seq", strategy = GenerationType.SEQUENCE)
@SequenceGenerator(name = "sample_id_seq", sequenceName = "sample_id_seq", allocationSize = 1)
private Integer id;

@Column(name = "name")
private String name;
// Other simple attributes

// This completes the relation Tillage-Sample
@ManyToOne(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "tillage_sample", 
   joinColumns = { @JoinColumn(name = "id_sample") }, 
   inverseJoinColumns = {@JoinColumn(name = "id_tillage") })
private Tillage tillage = new Tillage();

@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
@JoinTable(name = "sample_sample_item", 
           joinColumns = { @JoinColumn(name = "id_sample") }, 
           inverseJoinColumns = {@JoinColumn(name = "id_sample_item") })
private List<SampleItem> sampleItemList;


// Getters and Setters  
}

SomeService.java

...
@Override
public Tillage toTillage(TillageDTO dto) {

    Tillage tillage = new Tillage();
    tillage.setName(dto.getNameTillage());

    // Fill the samples of the tillage
    for(ArrSample sample : dto.getSamples().getArrSample()){

        Sample s = new Sample();
        s.setName(sample.getName());

        // Setting the tillage in the Sample object
        s.setTillage(tillage);

        // Fill the items of the sample
        for(Array arr : sample.getAreas().getArray()){

            SampleItem si = new SampleItem();

            si.setProduction(Double.parseDouble(arr.getProduction()));

            // Double binding between sample and sampleItem
            si.setSample(s);
            s.getSampleItemList().add(si);
        }
        // Adding a sample to Tillage
        tillage.getSampleList().add(s);
    }
    return tillage;
}


public void save(TillageDTO dto){
    Tillage t = this.toTillage(dto);

    // The error occurs when we persist the data
    // The entityManager is Autowired by Spring and works in other places
    entityManager.persist(tillage);

}

1 个答案:

答案 0 :(得分:2)

那不是双向的OneToMany。这是使用相同连接表的单独的单向关联。

在双向关联中,一方必须是另一方的倒数。对于OneToMany,一方必须是反面:

package models
import play.api.db._
import play.api.Play.current
import scala.collection.mutable._
import anorm._
import anorm.SqlParser._

    case class Brand(id: Int, name: String)


        object Brand {

             /**
            * Parse a Brand from a ResultSet
            */
            val simple = {
                get[Int]("m_brand.idbrand") ~
                get[String]("m_brand.brandName") map {
                case id~name => Brand(id, name)
                }
            }

            /**
            * Construct the Map[String,String] needed to fill a select options set.
            */
            def options: Seq[(String,String)] = DB.withConnection { implicit connection =>
                 SQL("select * from m_brand order by brandName").as(Brand.simple *).
                    foldLeft[Seq[(String, String)]](Nil) { (cs, c) => 
                     c.id.fold(cs) { id => cs :+ (id.toString -> c.name) }
                }
            }

}