Hibernate OneToMany映射和查询生成:发现了多个具有给定标识符的行

问题描述

我正在使用spring-boot-starter-data-jpa 1.5.1.RELEASE,该内部使用hibernate-core 5.0.11.Final

我的实体看起来像这样:

AreaDto

@Entity
@Table(name = "AREA")
@EntityListeners(AuditingEntityListener.class)
public class AreaDto {

    @Id
    @Column(name = "AREA_ROWID")
    private String areaRowId;

    @OnetoMany(cascade = CascadeType.DETACH)
    @JoinColumn(name = "AREA_ROWID")
    private Collection<FestivalDto> festival;


    @OnetoMany(cascade = CascadeType.DETACH,mappedBy = "area")
    private Collection<ActionDto> actions;


    @OnetoMany(fetch = FetchType.LAZY)
    @JoinTable(name = "FESTIVAL",joinColumns = {
        @JoinColumn(name = "AREA_ROWID",referencedColumnName = "AREA_ROWID")},inverseJoinColumns = {
            @JoinColumn(name = "FESTIVAL_ROWID",referencedColumnName = "FESTIVAL_ROWID")})
    private Collection<ActionDto> festivalActions;


}

FestivalDto

@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "FESTIVAL")
public class FestivalDto {

    @Id
    @Column(name = "FESTIVAL_ROWID")
    @GeneratedValue(generator = "FESTIVAL_ROWID_SEQ")
    private Long festivalRowId;

    
    @ManyToOne(cascade = CascadeType.DETACH,fetch = FetchType.LAZY,optional = true)
    @JoinColumn(name = "AREA_ROWID")
    private AreaDto area;

    @OnetoMany(cascade = CascadeType.ALL,mappedBy = "festival")
    private Collection<ActionDto> actions = Lists.newArrayList();

}

ActionDto

@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "ACTION")
public class ActionDto implements Serializable {

...

    @Id
    @Column(name = "ACTION_ID")
    @GeneratedValue(generator = "ACTION_ID_SEQ")
    private Long actionId;

    @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
    @ManyToOne(cascade = DETACH,fetch = FetchType.LAZY)
    @JoinColumn(name = "FESTIVAL_ROWID")
    private FestivalDto festival;

    @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
    @ManyToOne(cascade = DETACH,fetch = FetchType.LAZY)
    @JoinColumn(name = "AREA_ROWID")
    private AreaDto area;


}

我正在尝试理解以下想法:

  1. 冬眠用来决定用于获取所有相关动作的Festival_rowid(或Festival_row ID)的策略是什么?如果我在LAZY和EAGER之间更改festivalActions提取策略,休眠生成SQL查询将如何变化?我了解代理,集合代理以及所有其他方面,我的问题是特定于这些sql生成方式以及它对决定bind参数值的影响。

  2. 我的地图绘制是否准确,或者我应该为此关系使用多图,因为一个地区可能有多个节日,每个节日可能有多个动作

背景: 如果将提取类型从LAZY更改为EAGER,我将得到错误消失的提示。希望了解这种行为,以便对修复程序有一定的信心。我已阅读SOerror

org.hibernate.HibernateException: More than one row with the given identifier was found: data.dto.ActionDto@280856b5

解决方法

此映射没有太大意义。您无法以这种方式映射festivalActions,因为无法通过这种映射正确地保持状态。 festival中的AreaDto也应由area中的FestivalDto映射。请尝试以下操作:

@Entity
@Table(name = "AREA")
@EntityListeners(AuditingEntityListener.class)
public class AreaDto {

    @Id
    @Column(name = "AREA_ROWID")
    private String areaRowId;

    @OneToMany(cascade = CascadeType.DETACH,mappedBy = "area")
    private Collection<FestivalDto> festival;


    @OneToMany(cascade = CascadeType.DETACH,mappedBy = "area")
    private Collection<ActionDto> actions;

    public Collection<ActionDto> getFestivalActions() {
        return festival.stream().flatMap(f -> f.actions.stream()).collect(Collectors.toList());
    }


}

@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "FESTIVAL")
public class FestivalDto {

    @Id
    @Column(name = "FESTIVAL_ROWID")
    @GeneratedValue(generator = "FESTIVAL_ROWID_SEQ")
    private Long festivalRowId;

    
    @ManyToOne(cascade = CascadeType.DETACH,fetch = FetchType.LAZY,optional = true)
    @JoinColumn(name = "AREA_ROWID")
    private AreaDto area;

    @OneToMany(cascade = CascadeType.ALL,mappedBy = "festival")
    private Collection<ActionDto> actions = Lists.newArrayList();

}

@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "ACTION")
public class ActionDto implements Serializable {

...

    @Id
    @Column(name = "ACTION_ID")
    @GeneratedValue(generator = "ACTION_ID_SEQ")
    private Long actionId;

    @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
    @ManyToOne(cascade = DETACH,fetch = FetchType.LAZY)
    @JoinColumn(name = "FESTIVAL_ROWID")
    private FestivalDto festival;

    @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
    @ManyToOne(cascade = DETACH,fetch = FetchType.LAZY)
    @JoinColumn(name = "AREA_ROWID")
    private AreaDto area;


}