当前位置: 首页 > 知识库问答 >
问题:

嵌套的获取与JPQL和Hibernate连接

甄正信
2023-03-14

我正在编写一个JPQL查询(Hibernate是我的JPA提供程序),以获取一个实体公司及其几个关联。这适用于我的“简单”的许多关联,比如:

@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创建一个查询来获取上述内容,这很好。然而,我的公司实体也与存储在中间表中的数据有多对多关联,因此这被映射为三个实体之间的@OneToMany@manytone关联。

公司介绍

这是我代码中的三个实体。因此,一个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 = ?

问题#1我忽略了不相关的连接,因为它们都是可以的。它似乎选择了我需要的所有数据,包括服务和中间实体数据(CompanyService)。

查询#2此查询仅从数据库及其城市中获取公司。city association被急切地获取,但即使我将其更改为lazy fetching,查询仍然会生成。所以说实话,我不知道这个问题是为了什么。

查询#3查询#4这些查询基于ID查找服务实例,可能基于查询#1中获取的服务ID。我不认为有必要进行此查询,因为此数据已经在查询#1中获取(就像查询#2中的数据已经在查询#1中获取一样)。此外,如果一家公司有很多服务,这种方法显然不能很好地扩展。

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

  • 为什么Hibernate会创建查询#2、#3和#4?我能避免吗

如果有任何错误提示或其他解决方案,我将不胜感激。下面是我的代码(不包括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;
}

服务实体

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

共有2个答案

那正初
2023-03-14

从你写的内容来看,我认为嵌套抓取不受支持。以下是我对你们结果的理解:

  • 查询#1可以,并加入它需要的所有内容,这很好

我知道这不是一个答案,但它可能会帮助你理解背景中发生的事情。

卞坚成
2023-03-14

好吧,看来我明白了。通过将fetch type设置为FetchType。懒惰公司服务中,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执行另一个查询来获取服务。在我的特定用例中,这比用一个查询获取一吨冗余数据要好。

我希望这能帮助别人。如果有人有更好的解决方案/改进,我当然会很高兴听到他们。

 类似资料:
  • 我想知道JPQL是否可以嵌套查询。我正在学习Spring Data JPA,我也上传了几个相关的问题。 如果MySQL中有以下sql,我如何生成JPQL: 我有两个实体。 上面的实体有一个@OneTo多集合,集合实体在下面。 我想得到不到10个孩子的作弊实体。

  • 我有一个连接两个表的非常简单的连接表,为了简化问题,我们可以说表1是a,表2是B,有一个连接表AB。 其中A_id_fk和B_id_fk分别是外键。我试图创建一个查询来检索a中与B有关系的所有行,所以我的函数接收B_id作为参数,我想搜索AB以获得B_id_fk==B_id的所有行,然后使用搜索a搜索A_id==上一次搜索返回的A_id_fk的所有行。 我进行了测试,可以在简单的SQL中使用嵌套的

  • 问题内容: 使用Criteria或HQL有什么利弊?Criteria API是在Hibernate中表达查询的一种很好的面向对象的方式,但是有时候Criteria Queries比HQL更难以理解/构建。 您何时使用标准以及何时使用HQL?在哪种用例中,您更喜欢什么?还是只是口味问题? 问题答案: 我最喜欢动态查询的条件查询。例如,动态添加一些订购或根据某些参数省去某些零件(例如限制)要容易得多。

  • 我有一个使用Hibernate/JPA的持久性Spring Boot应用程序。 我正在使用事务来管理我的数据库持久性,并且我正在使用注释来定义应该以事务方式执行的方法。 在持久化时,我有三个主要的事务粒度级别: 要保留的实体批次 要保留的单个实体 保留实体的单一数据库操作 因此,在考虑整个持久性流量时,您可以想象我有三个级别的嵌套事务。 第2层和第3层之间的交互如我所愿透明地工作,因为在没有为事务

  • 我对Hibernate不太熟悉。谁能帮我解决代码中出现的错误。 我正在使用Eclipse Helios,Hibernate 3 生成的类和配置文件,如: Emp.java 部门java hibernate.cfg.xml Emp.hbm.xml 部门hbm。xml Test.java 我得到的例外是: 线程“main”组织中出现异常。冬眠HibernateException:无法分析配置:/hib

  • 问题内容: 我只想获取嵌套字段,但不能,因为它不是叶字段。 我在下面尝试过,但是无法匹配嵌套对象中的每个ID和名称。 结果: 这是我的预期结果: 问题答案: 如果您没有某个查询应以某种方式匹配嵌套字段,则可以这样进行: 如果您还有一个查询,并且想返回 匹配 的 嵌套文档,则 可以这样操作(使用):