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

Posted

技术标签:

【中文标题】带有连接表的双向@ManyToOne 创建重复键【英文标题】:Bidirectional @ManyToOne with join table creates duplicate key 【发布时间】:2016-01-25 03:29:21 【问题描述】:

我正在使用 hibernatejoin table 实现 @ManyToOne 双向 关系,但是当我在持久化一些数据时,休眠声称关系表中的记录被插入两次,违反了唯一约束,如下面的错误消息所示:

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.

我有以下表格:

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

当我创建一个 Tillage 对象时,我会填充一个 Sample。在 Sample 对象中,我指向 Tillage 对象,创建了一个“双重绑定”

我猜是这个“双重绑定”造成了麻烦,因为当保存耕作并尝试将耕作保留在样本(同一个耕作对象)。

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

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】:

这不是双向 OneToMany。使用同一个连接表的单向关联太分离了。

在双向关联中,一侧必须与另一侧相反。对于 OneToMany,One 面必须是反面:

@OneToMany(mappedBy = "tillage", cascade=CascadeType.ALL, fetch = FetchType.EAGER)
private List<Sample> sampleList;

【讨论】:

完美!我认为 mappedBy 仅用于使用连接列的关联。非常感谢

以上是关于带有连接表的双向@ManyToOne 创建重复键的主要内容,如果未能解决你的问题,请参考以下文章

使用连接表将 JPA 双向 @ManyToOne 关系映射到多个表

如何使用主键作为JPA和Hibernate的外键引用?

onetomany / manytoone 的无限循环?春天/休息api

如何通过级联级联@ManyToOne 双向关系?

在 Hibernate 双向 ManytoOne、OnetoMany 的映射列中获取 null

Doctrine ORM self ManyToOne 由于重复条目而无法插入