【发布时间】:2015-02-16 00:53:53
【问题描述】:
我正在编写一个 JPQL 查询(使用 Hibernate 作为我的 JPA 提供程序)来获取一个实体 Company 及其几个关联。这适用于我的“简单”多对多关联,如下所示:
@Entity
@Table(name = "company")
@NamedQueries({
@NamedQuery(
name = "Company.profile.view.byId",
query = "SELECT c " +
"FROM Company AS c " +
"INNER JOIN FETCH c.city AS city " + <-- @ManyToOne
"LEFT JOIN FETCH c.acknowledgements " + <-- @ManyToMany
"LEFT JOIN FETCH c.industries " + <-- @ManyToMany
"WHERE c.id = :companyId"
)
})
public class Company { ... }
Hibernate 创建一个查询来获取上述内容,这很好。但是,我的 Company 实体也与存储在中间表中的数据存在多对多关联,因此这被映射为三个实体之间的 @OneToMany 和 @ManyToOne 关联。
公司 服务
这是我的代码中的三个实体。所以Company 实例有一个CompanyService 实体的集合,每个实体都与Service 实例有关系。我希望这是有道理的 - 否则请检查问题末尾的源代码。
现在我想通过修改上述查询来获取给定公司的服务。我事先读到 JPA 不允许嵌套 fetch 连接,甚至不允许连接别名,但是一些 JPA 提供程序确实支持它,所以我用 Hibernate 试试运气。我尝试这样修改查询:
@Entity
@Table(name = "company")
@NamedQueries({
@NamedQuery(
name = "Company.profile.view.byId",
query = "SELECT c " +
"FROM Company AS c " +
"INNER JOIN FETCH c.city AS city " +
"LEFT JOIN FETCH c.acknowledgements " +
"LEFT JOIN FETCH c.industries " +
"LEFT JOIN FETCH c.companyServices AS companyService " +
"LEFT JOIN FETCH companyService.service AS service " +
"WHERE c.id = :companyId"
)
})
public class Company { ... }
现在,Hibernate 不再创建单个查询,而是创建以下查询:
#1
select ...
from company company0_
inner join City city1_ on company0_.postal_code = city1_.postal_code
[...]
left outer join company_service companyser6_ on company0_.id = companyser6_.company_id
left outer join service service7_ on companyser6_.service_id = service7_.id
where company0_.id = ?
#2
select ...
from company company0_
inner join City city1_ on company0_.postal_code = city1_.postal_code
where company0_.id = ?
#3
select service0_.id as id1_14_0_, service0_.default_description as default_2_14_0_, service0_.name as name3_14_0_
from service service0_
where service0_.id = ?
#4
select service0_.id as id1_14_0_, service0_.default_description as default_2_14_0_, service0_.name as name3_14_0_
from service service0_
where service0_.id = ?
查询 #1
我省略了不相关的连接,因为这些都可以。它似乎选择了我需要的所有数据,包括服务和中间实体数据 (CompanyService)。
查询 #2
此查询只是从数据库中获取公司及其City。城市关联是热切获取的,但即使我将其更改为延迟获取,仍然会生成查询。所以说实话,我不知道这个查询是干什么用的。
查询 #3 + 查询 #4
这些查询基于 ID 查找 Service 实例,可能基于在查询 #1 中获取的服务 ID。我认为不需要此查询,因为此数据已在查询 #1 中获取(正如来自查询 #2 的数据已在查询 #1 中获取)。此外,如果公司有很多服务,这种方法显然不能很好地扩展。
奇怪的是,查询 #1 似乎做了我想要的,或者至少它获取了我需要的数据。我只是不知道为什么 Hibernate 创建查询 #2、#3 和 #4。所以我有以下问题:
- 为什么 Hibernate 创建查询 #2、#3 和 #4?我可以避免吗?
- 即使 JPA 不支持,Hibernate 是否支持嵌套关联获取?如果是这样,我将如何处理?
- 这种行为是正常的,还是因为我正在尝试做的事情不受支持,因此我得到了奇怪的结果?这看起来很奇怪,因为查询 #1 看起来非常好
任何错误提示或完成我想要的替代解决方案将不胜感激。下面是我的代码(不包括 getter 和 setter)。提前非常感谢!
公司实体
@Entity
@Table(name = "company")
@NamedQueries({
@NamedQuery(
name = "Company.profile.view.byId",
query = "SELECT c " +
"FROM Company AS c " +
"INNER JOIN FETCH c.city AS city " +
"LEFT JOIN FETCH c.acknowledgements " +
"LEFT JOIN FETCH c.industries " +
"LEFT JOIN FETCH c.companyServices AS companyService " +
"LEFT JOIN FETCH companyService.service AS service " +
"WHERE c.id = :companyId"
)
})
public class Company {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private int id;
// ...
@ManyToOne(fetch = FetchType.EAGER, targetEntity = City.class, optional = false)
@JoinColumn(name = "postal_code")
private City city;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "company_acknowledgement", joinColumns = @JoinColumn(name = "company_id"), inverseJoinColumns = @JoinColumn(name = "acknowledgement_id"))
private Set<Acknowledgement> acknowledgements;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "company_industry", joinColumns = @JoinColumn(name = "company_id"), inverseJoinColumns = @JoinColumn(name = "industry_id"))
private Set<Industry> industries;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "company")
private Set<CompanyService> companyServices;
}
CompanyService 实体
@Entity
@Table(name = "company_service")
@IdClass(CompanyServicePK.class)
public class CompanyService implements Serializable {
@Id
@ManyToOne(targetEntity = Company.class)
@JoinColumn(name = "company_id")
private Company company;
@Id
@ManyToOne(targetEntity = Service.class)
@JoinColumn(name = "service_id")
private Service service;
@Column
private String description;
}
服务实体
@Entity
@Table(name = "service")
public class Service {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private int id;
@Column(length = 50, nullable = false)
private String name;
@Column(name = "default_description", nullable = false)
private String defaultDescription;
}
获取数据
public Company fetchTestCompany() {
TypedQuery<Company> query = this.getEntityManager().createNamedQuery("Company.profile.view.byId", Company.class);
query.setParameter("companyId", 123);
return query.getSingleResult();
}
【问题讨论】: