我有两个(基于Hibernate的) Spring域类,一个是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;
}还有“多”边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;
}
}我有OrderRepository接口,它扩展了JpaRepository接口和JpaSpecificationExecutor接口:
public interface OrderRepository extends JpaRepository<Order, Long>, JpaSpecificationExecutor<Order> {
}我有一个带有静态方法OrderSpecification.class的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 + "%");
}
};
}
} 为了找出get("property")链和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());
}
}我发现: get("property")链使用交叉连接(这是非常糟糕的性能),而联接使用内部连接(因为默认情况下ManyToOne()是Fetch= FetchType.EAGER)。
/* get("property")链: Hibernate:选择order0_.order_id作为order_id1_1_,order0_.order_cust_id选择order_cu3_1_,order0_.order_name作为order_na2_1_从sys_order order0_ 交叉加入 sys_customer customer1_,其中order0_.order_cust_id=customer1_.cust_id和(customer1_.cust_name like ?)Hibernate:选择customer0_.cust_id作为cust_id1___,选择customer0_.cust_name作为cust_nam2___,从sys_customer customer0_选择customer0_.cust_id=?*/
/** *“联接”:* Hibernate:选择order0_.order_id作为order_id1_1_,order0_.order_cust_id作为order_cu3_1_,order0_.order_name作为order_na2_1_从sys_order order0_ 内部连接 sys_customer customer1_ on order0_.order_cust_id=customer1_.cust_id?* Hibernate:选择customer0_.cust_id作为cust_id1___,customer0_.cust_name as cust_nam2___ as sys_customer customer0_ customer0_.cust_id=?*/
我的问题是:
在使用cross-join?
发布于 2021-10-13 09:05:16
不能为路径指定联接类型。默认情况下,它将使用内部连接语义,这是JPA规范要求的。如果希望使用不同的联接类型,则必须显式创建联接。使用get呈现为交叉连接是Hibernate旧查询模型的一个限制,但Hibernate 6.0将修复这个问题。但是语义是相同的,数据库的查询规划师应该能够以相同的方式处理这两个查询。也许你只需要更新你的数据库版本?
没有“最佳实践”,也就是说,这取决于你的需要。显式联接就是显式的。因此,对join的多次调用将在SQL中创建多个联接。
至于面向对象的问题,我认为这很好,是的。
https://stackoverflow.com/questions/69322050
复制相似问题