java - 当 JOIN FETCH 与主 ID 以外的引用列一起使用时,Hibernate 执行多个查询

标签 java hibernate jpa fetch hql

我有以下问题。

每当使用 JOIN FETCH 子句通过查询调用我的存储库时,Hibernate 都会生成一个根本不需要的额外查询。这里的主要问题是,通过相应的数据库结构,两个实体(对应于两个单独的表)应该由一个不同列连接,而不是两个实体的ID(即“customerid"在实体 Order 中由 @JoinColumn(name = "customerid", referencedColumnName = "customerid")"表示。

存储库如下:

public interface CustomerRepository extends CrudRepository<Customer, Long>{

    Customer findByMsisdn(String msisdn);

    @Query("SELECT c FROM Customer c JOIN FETCH c.orders WHERE c.msisdn =:msisdn")
    Customer getCustomersAndOrdersByMsisdn(@Param("msisdn") String msisdn);
}

每当我调用 getCustomersAndOrdersByMsisdn 方法时,都会生成以下两个查询(自然地,我们希望只生成查询 1):

--Query1
select
   customer0_.id as id1_0_0_,
   orders1_.id as id1_1_1_,
   customer0_.customerid as customer2_0_0_,
   customer0_.msisdn as msisdn3_0_0_,
   customer0_.status as status4_0_0_,
   orders1_.customerid as customer4_1_1_,
   orders1_.orderid as orderid2_1_1_,
   orders1_.orderstatus as ordersta3_1_1_,
   orders1_.customerid as customer4_1_0__,
   orders1_.id as id1_1_0__ 
from
   customers customer0_ 
   inner join
      orders orders1_ 
      on customer0_.customerid = orders1_.customerid 
where
   customer0_.msisdn =?

--Query2
select
      customer0_.id as id1_0_0_,
      customer0_.customerid as customer2_0_0_,
      customer0_.msisdn as msisdn3_0_0_,
      customer0_.status as status4_0_0_ 
   from
      customers customer0_ 
   where
      customer0_.customerid =?

我的两个实体(为清楚起见,我在这里省略了 getter 和 setter,它们当然存在于我的代码中)是:

@Entity
@Table(name = "customers")
public class Customer implements Serializable {
    static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;


    @Column(name = "msisdn")
    private String msisdn;

    @Column(name = "status")
    private String status;

    @Column(name = "customerid", unique = true)
    @NaturalId
    private String customerid;

    @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL,fetch = FetchType.LAZY)
    private Set<Order> orders;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Customer customer = (Customer) o;
        return id == customer.id &&
                msisdn.equals(customer.msisdn) &&
                customerid.equals(customer.customerid);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, msisdn, customerid);
    }
}

@Entity
@Table(name = "orders")
public class Order implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Column(name = "orderid")
    private int orderid;

    @Column(name = "orderstatus")
    private String orderstatus;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "customerid", referencedColumnName = "customerid")
    private Customer customer;


    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Order orders = (Order) o;
        return id == orders.id &&
                orderid == orders.orderid;
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, orderid);
    }
}

最佳答案

这是绝对正常的,它的发生是因为连接是使用自然 ID 而不是实体的 ID 进行的。 Hibernate 使用实体的 id 而不是定义的自然 id 来对实体执行操作。

因此在进行连接时,hibernate 需要获取具有自然 id 的客户的实体。

有关详细信息,请阅读文章 @NaturalId – A good way to persist natural IDs with Hibernate?

关于java - 当 JOIN FETCH 与主 ID 以外的引用列一起使用时,Hibernate 执行多个查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62281835/

相关文章:

java - CDI 将服务注入(inject) JPA 托管实体

java - Hibernate @ManyToMany 注解支持哪些集合

java - 在主键列上加入 JPA OneToOne 关系

java - JDBC 连接/结果集/语句的最佳实践是什么

java - java中检查文件是否存在的最快方法

java.security.cert.CertPathValidatorException : Trust anchor for certification path not found

spring - 在 Hibernate 实体上使用 DTO 映射避免 N+1

java - 在 Spring 配置 XML 文件 :org. xml.sax.SAXParseException 中获取错误

java - Hibernate的SequenceStyleGenerator生成序列下面得到的序列值

java - 如何以编程方式打开在后台运行的应用程序