springdata jpa使用Example快速实现动态查询

本文详述了Spring Data JPA中Example查询技术的使用方法,包括动态查询的创建,ExampleMatcher的自定义规则设置,以及如何进行模糊匹配和忽略特定字段。通过示例展示了如何构建实体映射及测试查询。

Example官方介绍
Query by Example (QBE) is a user-friendly querying technique with a simple interface. It allows dynamic query creation and does not require to write queries containing field names. In fact, Query by Example does not require to write queries using store-specific query languages at all.

谷歌翻译:
按例查询(QBE)是一种用户界面友好的查询技术。 它允许动态创建查询,并且不需要编写包含字段名称的查询。 实际上,按示例查询不需要使用特定的数据库的查询语言来编写查询语句。

Example api的组成
Probe: 含有对应字段的实例对象。
ExampleMatcher:ExampleMatcher携带有关如何匹配特定字段的详细信息,相当于匹配条件。
Example:由Probe和ExampleMatcher组成,用于查询。
限制
属性不支持嵌套或者分组约束,比如这样的查询 firstname = ?0 or (firstname = ?1 and lastname = ?2)
灵活匹配只支持字符串类型,其他类型只支持精确匹配
Limitations
1. No support for nested/grouped property constraints like firstname = ?0 or (firstname = ?1 and lastname = ?2)
2. Only supports starts/contains/ends/regex matching for strings and exact matching for other property types

使用
创建实体映射:

@Entity
@Table(name="t_user")
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {

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

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

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

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

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

    @Column(name="address")
    private String address;
}

测试查询:

@Test
public void contextLoads() {
    User user = new User();
    user.setUsername("admin");
    Example<User> example = Example.of(user);
    List<User> list = userRepository.findAll(example);
    System.out.println(list);
}

打印的sql语句如下:

Hibernate: 
    select
        user0_.id as id1_0_,
        user0_.address as address2_0_,
        user0_.email as email3_0_,
        user0_.password as password4_0_,
        user0_.phone as phone5_0_,
        user0_.username as username6_0_ 
    from
        t_user user0_ 
    where
        user0_.username=?

可以发现,试用Example查询,默认情况下会忽略空值,官方文档也有说明:

This is a simple domain object. You can use it to create an Example. By default, fields having null values are ignored, and strings are matched using the store specific defaults. Examples can be built by either using the of factory method or by using ExampleMatcher. Example is immutable.

在上面的测试之中,我们只是只是定义了Probe而没有ExampleMatcher,是因为默认会不传时会使用默认的匹配器。点进方法可以看到下面的代码:

static <T> Example<T> of(T probe) {
    return new TypedExample(probe, ExampleMatcher.matching());
}

static ExampleMatcher matching() {
    return matchingAll();
}

static ExampleMatcher matchingAll() {
    return (new TypedExampleMatcher()).withMode(ExampleMatcher.MatchMode.ALL);
}

自定匹配器规则

@Test
public void contextLoads() {
    User user = new User();
    user.setUsername("y");
    user.setAddress("sh");
    user.setPassword("admin");
    ExampleMatcher matcher = ExampleMatcher.matching()
            .withMatcher("username", ExampleMatcher.GenericPropertyMatchers.startsWith())//模糊查询匹配开头,即{username}%
            .withMatcher("address" ,ExampleMatcher.GenericPropertyMatchers.contains())//全部模糊查询,即%{address}%
            .withIgnorePaths("password");//忽略字段,即不管password是什么值都不加入查询条件
    Example<User> example = Example.of(user ,matcher);
    List<User> list = userRepository.findAll(example);
    System.out.println(list);
}

打印的sql语句如下:
select
    user0_.id as id1_0_,
    user0_.address as address2_0_,
    user0_.email as email3_0_,
    user0_.password as password4_0_,
    user0_.phone as phone5_0_,
    user0_.username as username6_0_ 
from
    t_user user0_ 
where
    (
        user0_.username like ?
    ) 
    and (
        user0_.address like ?
    )

参数如下:
2018-03-24 13:26:57.425 TRACE 5880 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [y%]
2018-03-24 13:26:57.425 TRACE 5880 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [VARCHAR] - [%sh%]

补充
官方创建ExampleMatcher例子(1.8 lambda)

ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("firstname", match -> match.endsWith())
.withMatcher("firstname", match -> match.startsWith());
}

 

StringMatcher 参数
Matching 生成的语句 说明

DEFAULT (case-sensitive)    firstname = ?0    默认(大小写敏感)
DEFAULT (case-insensitive)    LOWER(firstname) = LOWER(?0)    默认(忽略大小写)
EXACT (case-sensitive)    firstname = ?0    精确匹配(大小写敏感)
EXACT (case-insensitive)    LOWER(firstname) = LOWER(?0)    精确匹配(忽略大小写)
STARTING (case-sensitive)    firstname like ?0 + ‘%’    前缀匹配(大小写敏感)
STARTING (case-insensitive)    LOWER(firstname) like LOWER(?0) + ‘%’    前缀匹配(忽略大小写)
ENDING (case-sensitive)    firstname like ‘%’ + ?0    后缀匹配(大小写敏感)
ENDING (case-insensitive)    LOWER(firstname) like ‘%’ + LOWER(?0)    后缀匹配(忽略大小写)
CONTAINING (case-sensitive)    firstname like ‘%’ + ?0 + ‘%’    模糊查询(大小写敏感)
CONTAINING (case-insensitive)    LOWER(firstname) like ‘%’ + LOWER(?0) + ‘%’    模糊查询(忽略大小写)

 

说明:
1. 在默认情况下(没有调用withIgnoreCase())都是大小写敏感的。
2. api之中还有个regex,但是我在mysql下测试报错,不了解具体作用。

总结
通过在使用springdata jpa时可以通过Example来快速的实现动态查询,同时配合Pageable可以实现快速的分页查询功能。
对于非字符串属性的只能精确匹配,比如想查询在某个时间段内注册的用户信息,就不能通过Example来查询

转载于:https://www.cnblogs.com/powerwu/articles/10716070.html

在Spring Data JPA中,使用HQL(Hibernate Query Language)实现联合查询是一种常见需求,尤其在处理多个实体之间的关联数据时。HQL是一种面向对象的查询语言,类似于SQL,但它操作的是持久化对象及其属性,而不是直接操作数据库表。 ### 使用HQL实现联合查询 1. **定义实体类之间的关联关系** 在使用HQL进行联合查询之前,必须确保实体类之间已经正确地定义了关联关系。常见的关联类型包括`@OneToOne`、`@OneToMany`、`@ManyToOne`和`@ManyToMany`。这些注解用于描述实体之间的关系,并在数据库中映射为相应的外键约束。 ```java @Entity public class Order { @Id private Long id; @ManyToOne @JoinColumn(name = "customer_id") private Customer customer; // 其他字段和方法 } @Entity public class Customer { @Id private Long id; private String name; @OneToMany(mappedBy = "customer") private List<Order> orders; // 其他字段和方法 } ``` 2. **编写HQL查询语句** 在定义好实体类之间的关联关系后,可以使用HQL编写联合查询语句。HQL支持多种类型的联合查询,包括`JOIN`、`LEFT JOIN`、`INNER JOIN`等。以下是一个使用`JOIN`进行联合查询的示例: ```java String hql = "SELECT o FROM Order o JOIN o.customer c WHERE c.name = :customerName"; TypedQuery<Order> query = entityManager.createQuery(hql, Order.class); query.setParameter("customerName", "John Doe"); List<Order> orders = query.getResultList(); ``` 在这个例子中,`JOIN o.customer c`表示将`Order`实体与`Customer`实体进行联合查询,并通过`c.name`筛选出特定客户的订单。 3. **使用`@Query`注解在Repository中定义HQL查询** Spring Data JPA允许在Repository接口中使用`@Query`注解来定义自定义的HQL查询。这种方式可以简化查询的调用过程,并且可以与Spring Data JPA的分页、排序等功能结合使用。 ```java public interface OrderRepository extends JpaRepository<Order, Long> { @Query("SELECT o FROM Order o JOIN o.customer c WHERE c.name = :customerName") List<Order> findOrdersByCustomerName(@Param("customerName") String customerName); } ``` 在这个例子中,`findOrdersByCustomerName`方法可以通过传入的`customerName`参数来查找所有属于该客户的订单。 4. **处理联合查询的结果** HQL查询的结果可以是单个实体、多个实体的组合,或者是特定字段的投影。如果需要返回多个实体的组合,可以使用`Object[]`或自定义的DTO(Data Transfer Object)类来封装结果。 ```java @Data public class OrderCustomerDTO { private Long orderId; private String customerName; public OrderCustomerDTO(Long orderId, String customerName) { this.orderId = orderId; this.customerName = customerName; } } @Repository public class OrderRepositoryImpl implements OrderRepositoryCustom { @PersistenceContext private EntityManager entityManager; @Override public List<OrderCustomerDTO> findOrderAndCustomerNames() { String hql = "SELECT new com.example.dto.OrderCustomerDTO(o.id, c.name) FROM Order o JOIN o.customer c"; return entityManager.createQuery(hql, OrderCustomerDTO.class).getResultList(); } } ``` 在这个例子中,`OrderCustomerDTO`是一个自定义的DTO类,用于封装查询结果中的订单ID和客户名称。 5. **优化联合查询的性能** 在进行联合查询时,可能会遇到性能问题,尤其是在处理大量数据时。为了优化性能,可以考虑以下几种方法: - **使用`fetch`进行预加载**:通过在HQL中使用`fetch`关键字,可以一次性加载关联的实体,避免多次查询数据库。 ```java String hql = "SELECT o FROM Order o FETCH JOIN o.customer c WHERE c.name = :customerName"; ``` - **使用分页**:如果查询结果集较大,可以使用分页功能来减少每次查询返回的数据量。 ```java @Query("SELECT o FROM Order o JOIN o.customer c WHERE c.name = :customerName") Page<Order> findOrdersByCustomerName(@Param("customerName") String customerName, Pageable pageable); ``` - **使用索引**:确保数据库中的关联字段上有适当的索引,以加快查询速度。 6. **处理复杂的联合查询逻辑** 对于更复杂的联合查询逻辑,可以使用`Criteria API`或`JPQL`拼接工具来构建动态查询。`Criteria API`提供了一种类型安全的方式来构建查询,适用于需要根据条件动态生成查询语句的场景。 ```java public List<Order> findOrdersByCustomerNameAndStatus(String customerName, String status) { CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<Order> cq = cb.createQuery(Order.class); Root<Order> orderRoot = cq.from(Order.class); Join<Order, Customer> customerJoin = orderRoot.join("customer"); Predicate customerNamePredicate = cb.equal(customerJoin.get("name"), customerName); Predicate statusPredicate = cb.equal(orderRoot.get("status"), status); cq.where(customerNamePredicate, statusPredicate); return entityManager.createQuery(cq).getResultList(); } ``` 在这个例子中,`Criteria API`用于构建一个包含两个条件的查询:客户名称和订单状态。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值