休眠多个@OneToMany 关系在父关系中生成更多结果
hibernate multiple @OneToMany relationships generating more results in parent relationship
我将从结果开始。在下一张图片中,我将显示 company.userAccount 的结果内容。在数据库中,我只有两家公司分配给同一用户,其中一家公司有 7 项服务。
@Entity
@Table(name = "user_account")
@Data
@Introspected
@JsonIgnoreProperties(value = {"id"})
public class UserAccount {
...
@OneToMany(mappedBy = "userAccount", fetch = FetchType.EAGER)
private List<Company> companies = new ArrayList<>();
}
@Entity
@Table(name = "company")
@Data
@Introspected
@ToString(exclude = {"userAccount"})
@JsonIgnoreProperties(value = {"id", "userAccount"})
public class Company {
...
@ManyToOne(optional = false)
@JoinColumn(name = "user_account_id", updatable = false, nullable = false)
private UserAccount userAccount;
}
@Entity
@Table(name = "service")
@Data
@Introspected
@ToString(exclude = {"company"})
@JsonIgnoreProperties(value= {"id", "company"})
public class Service {
...
@ManyToOne(optional = false)
@JoinColumn(name = "company_id", updatable = false, nullable = false)
private Company company;
}
@Transactional
public Service saveService(@Valid @Uuid UUID companyUuid, SaveServiceCommand command) {
Company company = companyRepository.getCompanyByUuid(companyUuid).orElseThrow(() -> new NoSuchElementException("unrecognized company"));
Service service = new Service();
service.setCompany(company);
service.setName(command.getName());
service.setUuid(UUID.randomUUID().toString());
return companyRepository.saveCompanyService(service);
}
@Transactional
public Service saveCompanyService(@Valid Service service) {
entityManager.persist(service);
return service;
}
生成的查询(简化)是
select * from user_account a left outer join company c on a.id=c.user_account_id left outer join service s on c.id=s.company_id;
这将生成
预期结果:我原本希望在 UserAccount 中看到只有 2 家公司,而其中一家公司有 6 项服务。为什么我会有这个结果?为什么同一个公司对象多次出现在列表中?我怎样才能避免这种情况?
我认为一种解决方案是将 @OneToMany 关系的获取类型更改为 LAZY(因为它们是默认的)(已经测试过该解决方案并且它有效)但是如果我需要这种情况怎么办?
是的,就像你说的,有2家公司。您会注意到,所有 objects 都是相同的(哈希匹配)。这背后的原因是查询为一家公司生成 6 行,而 hibernate 使用的 PersistenceBag
默认情况下不会删除重复项。将 distinct
传递给您的 sql 是行不通的,因为只有一个 parent 实体。
但这可以通过使用hibernate的查询提示来实现。将 QueryHints.HINT_PASS_DISTINCT_THROUGH
传递给您的 Query
只会删除重复的 children。
有关更多信息,您可以参考 this 来源。
我将从结果开始。在下一张图片中,我将显示 company.userAccount 的结果内容。在数据库中,我只有两家公司分配给同一用户,其中一家公司有 7 项服务。
@Entity
@Table(name = "user_account")
@Data
@Introspected
@JsonIgnoreProperties(value = {"id"})
public class UserAccount {
...
@OneToMany(mappedBy = "userAccount", fetch = FetchType.EAGER)
private List<Company> companies = new ArrayList<>();
}
@Entity
@Table(name = "company")
@Data
@Introspected
@ToString(exclude = {"userAccount"})
@JsonIgnoreProperties(value = {"id", "userAccount"})
public class Company {
...
@ManyToOne(optional = false)
@JoinColumn(name = "user_account_id", updatable = false, nullable = false)
private UserAccount userAccount;
}
@Entity
@Table(name = "service")
@Data
@Introspected
@ToString(exclude = {"company"})
@JsonIgnoreProperties(value= {"id", "company"})
public class Service {
...
@ManyToOne(optional = false)
@JoinColumn(name = "company_id", updatable = false, nullable = false)
private Company company;
}
@Transactional
public Service saveService(@Valid @Uuid UUID companyUuid, SaveServiceCommand command) {
Company company = companyRepository.getCompanyByUuid(companyUuid).orElseThrow(() -> new NoSuchElementException("unrecognized company"));
Service service = new Service();
service.setCompany(company);
service.setName(command.getName());
service.setUuid(UUID.randomUUID().toString());
return companyRepository.saveCompanyService(service);
}
@Transactional
public Service saveCompanyService(@Valid Service service) {
entityManager.persist(service);
return service;
}
生成的查询(简化)是
select * from user_account a left outer join company c on a.id=c.user_account_id left outer join service s on c.id=s.company_id;
这将生成
预期结果:我原本希望在 UserAccount 中看到只有 2 家公司,而其中一家公司有 6 项服务。为什么我会有这个结果?为什么同一个公司对象多次出现在列表中?我怎样才能避免这种情况?
我认为一种解决方案是将 @OneToMany 关系的获取类型更改为 LAZY(因为它们是默认的)(已经测试过该解决方案并且它有效)但是如果我需要这种情况怎么办?
是的,就像你说的,有2家公司。您会注意到,所有 objects 都是相同的(哈希匹配)。这背后的原因是查询为一家公司生成 6 行,而 hibernate 使用的 PersistenceBag
默认情况下不会删除重复项。将 distinct
传递给您的 sql 是行不通的,因为只有一个 parent 实体。
但这可以通过使用hibernate的查询提示来实现。将 QueryHints.HINT_PASS_DISTINCT_THROUGH
传递给您的 Query
只会删除重复的 children。
有关更多信息,您可以参考 this 来源。