I am using spring-boot-starter-data-jpa 1.5.1.RELEASE which internally uses hibernate-core 5.0.11.Final
My entity looks like this:
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;
}
I'm trying to make sense of the below ideas:
What is the strategy used by hibernate to decide on the festival_rowid (or festival_row ids) used to get all the associated action? How will hibernate generated SQL query vary if i change festivalActions fetch strategies between LAZY and EAGER? I know about proxying, collection proxying and all, my question is specific to how those sql is generated and how it may have an impact on deciding the value of bind parameter.
Is my mapping accurate or should I be using a multimap for this relationship since an area could have multiple festival and each festival could have multiple actions
Background: I am getting below error which goes away if I change the fetch type from LAZY to EAGER. Hoping to understand the behaviour for gaining some confidence in the fix. I have read SO and error
org.hibernate.HibernateException: More than one row with the given identifier was found: data.dto.ActionDto@280856b5
This mapping does not make much sense. You can't map festivalActions
this way because there is no way to persist the state properly through such a mapping. Also festival
in AreaDto
should be mapped by the area
in FestivalDto
. Try the following instead:
@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;
}