Hibernate OneToMany 映射和查询生成:找到具有给定标识符的多行

Hibernate OneToMany mapping & Query generation : More than one row with the given identifier was found

我正在使用 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, fetch = FetchType.LAZY, 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. hibernate 使用什么策略来决定用于获取所有关联操作的 festival_rowid(或 festival_row id)?如果我在 LAZY 和 EAGER 之间更改 festivalActions 获取策略,休眠生成的 SQL 查询将如何变化?我知道代理、集合代理等等,我的问题是这些 sql 是如何生成的,以及它如何影响绑定参数的值。

  2. 我的映射是否准确,或者我应该为这种关系使用多重地图,因为一个区域可以有多个节日,每个节日可以有多个动作

背景: 如果我将获取类型从 LAZY 更改为 EAGER,我会遇到以下错误,该错误会消失。希望了解行为以获得对修复的一些信心。我已阅读SO and error

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

这个映射没有多大意义。您不能以这种方式映射 festivalActions,因为无法通过这种映射正确地持久化状态。另外 AreaDto 中的 festival 应该被 FestivalDto 中的 area 映射。请尝试以下操作:

@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, fetch = FetchType.LAZY, 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;


}