jpa 使用JPQL和Hibernate的嵌套获取连接

wlp8pajw  于 2023-11-18  发布在  其他
关注(0)|答案(3)|浏览(102)

我正在编写一个JPQL查询(使用Hibernate作为我的JPA提供程序)来获取实体Company及其几个关联。这对于我的“简单”ManyToMany关联很好,如下所示:

@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实体也与存储在中间表中的数据有多对多的关联,因此为什么这被Map为三个实体之间的@OneToMany@ManyToOne关联。
公司介绍<--公司介绍服务支持-->服务支持
这是我的代码中的三个实体。所以Company示例有一个CompanyService实体的集合,每个实体都与Service示例有关系。我希望这是有意义的-否则请检查问题末尾的源代码。
现在我想通过修改上面的查询来获取给定公司的服务。我事先了解到JPA不允许嵌套的获取连接,甚至不允许连接的别名,但一些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 = ?

Query #1我省略了不相关的连接,因为这些都是可以的。它似乎选择了我需要的所有数据,包括服务和中间实体数据(CompanyService)。
Query #2这个查询只是从数据库中获取公司及其City。城市协会是急切获取的,但即使我将其更改为延迟获取,仍然会生成查询。所以老实说,我不知道这个查询是为了什么。
查询#3 +查询#4这些查询是基于ID查找Service示例,大概是基于查询#1中获取的服务ID。我认为不需要此查询,因为此数据已在查询#1中获取(正如来自查询#2的数据已经在查询#1中获取)。此外,如果公司有许多服务,这种方法显然不能很好地扩展。

奇怪的是,查询#1似乎做了我想要的,或者至少它获取了我需要的数据。我只是不知道为什么Hibernate创建了查询#2,#3和#4。所以我有以下问题:

  • 为什么Hibernate要创建查询#2、#3和#4?我可以避免吗?
  • Hibernate是否支持嵌套的关联获取,即使JPA不支持?如果是的话,在我的情况下我该怎么做?
  • 这种行为是正常的,还是因为我尝试做的事情不受支持,因此我得到了奇怪的结果?

任何错误或替代解决方案,以完成我想要的指针将不胜感激。下面是我的代码(getters和setters排除)。提前感谢!

公司实体

@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;
}

公司服务实体

@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();
}

pobjuy32

pobjuy321#

好吧,看起来我已经弄明白了。通过在CompanyService中将fetch类型设置为FetchType.LAZY,Hibernate停止生成所有冗余查询,这些查询基本上再次获取相同的数据。下面是实体的新版本:

@Entity
@Table(name = "company_service")
@IdClass(CompanyServicePK.class)
public class CompanyService implements Serializable {
    @Id
    @ManyToOne(fetch = FetchType.LAZY, targetEntity = Company.class)
    @JoinColumn(name = "company_id")
    private Company company;

    @Id
    @ManyToOne(fetch = FetchType.LAZY, targetEntity = Service.class)
    @JoinColumn(name = "service_id")
    private Service service;

    @Column
    private String description;
}

字符串
JPQL查询保持不变。
然而,在我的Company实体具有的关联数量的特定情况下,我得到了很多重复的数据,因此让Hibernate执行额外的查询会更有效。我通过从JPQL查询中删除两个连接获取并将查询代码更改为以下代码来实现这一点。

@Transactional
public Company fetchTestCompany() {
    TypedQuery<Company> query = this.getEntityManager().createNamedQuery("Company.profile.view.byId", Company.class);
    query.setParameter("companyId", 123);

    try {
        Company company = query.getSingleResult();
        Hibernate.initialize(company.getCompanyServices());

        return company;
    } catch (NoResultException nre) {
        return null;
    }
}


通过初始化companyServices关联,Hibernate执行另一个查询来获取服务。在我的特定用例中,这比用一个查询获取大量冗余数据要好。
我希望这对某人有帮助。如果任何人有更好的解决方案/改进,那么我当然很乐意听到他们。

wbrvyc0a

wbrvyc0a2#

从你写的,我会说,嵌套抓取不支持。这是我对你的结果的理解:

*Query #1可以,并加入了它需要的所有内容,这很好

  • 然而,Query #2我认为得到CompanyService#company(与eager city导致inner join City
    *查询#3得到CompanyService#service
    *第四个问题对我来说是个谜

我知道这不是一个答案,但它可能会帮助你了解背景中发生了什么。

rsaldnfx

rsaldnfx3#

我知道他的主题很老了,但这可能会对某些人有所帮助。我认为hibernate在join fetch期间发出额外查询的原因是你还需要使用多对一的join fetch-当你使用jpql时,默认情况下不会加载这些。

相关问题