简体   繁体   English

Spring Data JPA 如何在使用 get("property") 链与 Join 时指定 Join 类型或 Fetch Mode

[英]Spring Data JPA how to specify Join type or Fetch Mode when using get("property") chain vs Join

I have two (Hibernate-based) Spring Data JPA domain classes, the "One" side Customer.class:我有两个(基于 Hibernate 的)Spring Data JPA 域类,即“一个”端 Customer.class:

@Entity
@Table(name = "sys_customer")
@Data
public class Customer implements Serializable {
    @Id
    @Column(name = "cust_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "cust_name")
    private String customerName;

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

}

and the "Many" side Order.class:和“多”方 Order.class:

@Entity
@Table(name = "sys_order")
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Order implements Serializable {

    @Id
    @Column(name = "order_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "order_name")
    private String orderName;

    @ManyToOne
    @JoinColumn(name = "order_cust_id", referencedColumnName = "cust_id")
    private Customer customer;

    public Order( String orderName) {
        this.orderName = orderName;
    }

    public Order(String orderName, Customer customer) {
        this.orderName = orderName;
        this.customer = customer;
    }
}

I have OrderRepository interface which extends JpaRepository interface and JpaSpecificationExecutor interface:我有 OrderRepository 接口,它扩展了 JpaRepository 接口和 JpaSpecificationExecutor 接口:

public interface OrderRepository extends JpaRepository<Order, Long>, JpaSpecificationExecutor<Order> {
}

I have a OrderSpecification.class with the static method searchByCustomerName :我有一个带有静态方法searchByCustomerName

public class OrderSpecification {
    public static Specification<Order> searchByCustomerName(String customerName) {
        return new Specification<Order>() {
            @Override
            public Predicate toPredicate(Root<Order> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                Join<Order, Customer> join = root.join("customer");
                return criteriaBuilder.like(join.get("customerName"), "%" + customerName + "%");
                //return criteriaBuilder.like(root.get("customer").get("customerName"), "%" + customerName + "%");
            }
        };
    }
} 

To find the differences between get("property") chain and Join , I wrote a simple test method and comment out the above OrderSpecificatin.class code为了找出get("property") chain 和Join的区别,我写了一个简单的测试方法,并注释掉上面的 OrderSpecificatin.class 代码

    @Test
    @Transactional
    public void testFindOrderByCustomerName(){
        String name = "adam";
        List<Order> orders = orderRepository.findAll(OrderSpecification.searchByCustomerName(name));
        for(Order order: orders){
            Customer customer = order.getCustomer();
            log.info(new StringBuilder().append(customer.getId()).append(" ").append(customer.getCustomerName()).toString());

        }
    }

I found that: get("property") chain use a cross-join(which is very bad performancing) while Join use inner-join(since ManyToOne() by default is Fetch= FetchType.EAGER)我发现: get("property") 链使用交叉连接(这是非常糟糕的性能)而 Join 使用内连接(因为 ManyToOne() 默认情况下是 Fetch= FetchType.EAGER)

/* get("property") chain: Hibernate: select order0_.order_id as order_id1_1_, order0_.order_cust_id as order_cu3_1_, order0_.order_name as order_na2_1_ from sys_order order0_ cross join sys_customer customer1_ where order0_.order_cust_id=customer1_.cust_id and (customer1_.cust_name like ?) Hibernate: select customer0_.cust_id as cust_id1_0_0_, customer0_.cust_name as cust_nam2_0_0_ from sys_customer customer0_ where customer0_.cust_id=? /* get("property") chain: Hibernate: select order0_.order_id as order_id1_1_, order0_.order_cust_id as order_cu3_1_, order0_.order_name as order_na2_1_ from sys_order order0_ cross join sys_customer customer1_ where order0_.customer_cust_id_1_比如*/ */

/** * "Join": * Hibernate: select order0_.order_id as order_id1_1_, order0_.order_cust_id as order_cu3_1_, order0_.order_name as order_na2_1_ from sys_order order0_ inner join sys_customer customer1_ on order0_.order_cust_id=customer1_.cust_id where customer1_.cust_name like ? /** * "Join": * Hibernate: select order0_.order_id as order_id1_1_, order0_.order_cust_id as order_cu3_1_, order0_.order_name as order_na2_1_ from sys_order order0_inner join sys_customer customer1_ on order0_.order_cust_id=cust_customer1? * Hibernate: select customer0_.cust_id as cust_id1_0_0_, customer0_.cust_name as cust_nam2_0_0_ from sys_customer customer0_ where customer0_.cust_id=? * Hibernate:选择 customer0_.cust_id 作为 cust_id1_0_0_,customer0_.cust_name 作为 cust_nam2_0_0_ from sys_customer customer0_ where customer0_.cust_id=? */ */

My questions are:我的问题是:

  1. Can I specify the Join type(inner, all three outers) or Fetch Type(LAZY, EAGER) when using get("property") chain approach to avoid cross-join?在使用get("property")链方法以避免交叉连接时,我可以指定连接类型(内部,所有三个外部)或获取类型(LAZY,EAGER)吗?
  2. What scenario/best practice should I use get("chain") or always stay in Join?我应该使用 get("chain") 还是始终留在 Join 中?
  3. Does the approach OrderSpecification.class with static method obey a good OOP design pattern?带有静态方法的方法 OrderSpecification.class 是否遵循良好的 OOP 设计模式?

You can't specify the join type for paths.您不能为路径指定连接类型。 It will use INNER join semantics by default and that is mandated by the JPA specification.默认情况下,它将使用 INNER 连接语义,这是 JPA 规范所要求的。 If you want a different join type, you will have to create joins explicitly.如果您想要不同的联接类型,则必须明确创建联接。 The fact that using get renders as cross joins is a limitation of the old query model of Hibernate, but Hibernate 6.0 will fix this.使用get渲染作为交叉连接的事实是 Hibernate 旧查询模型的一个限制,但 Hibernate 6.0 将解决这个问题。 The semantics are the same though and the query planner of your database should be able to treat both queries the same way.语义是相同的,并且您的数据库的查询计划器应该能够以相同的方式处理这两个查询。 Maybe you just need to update your database version?也许您只需要更新数据库版本?

There is no "best practice" ie this really depends on your needs.没有“最佳实践”,即这实际上取决于您的需求。 Explicit joins are just that, explicit.显式连接就是显式连接。 So multiple calls to join will create multiple joins in SQL.所以多次调用join会在 SQL 中创建多个 join。

As for the OOP question, I think this is fine, yes.至于面向对象的问题,我认为这很好,是的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM