You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Spring JPQL基于非空DTO字段实现图书查询过滤?

基于DTO非空字段动态过滤图书的JPQL实现方案

1. 前置说明

假设你的Book实体类与提供的BookDTO字段一一对应(数据库表books映射到Book实体),先确保实体类定义如下:

@Entity
@Table(name = "books")
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String title;
    private String isbn;
    private String author;
    private int pages;
    private String size;
    
    // 省略getter、setter及构造方法
}

2. 动态构建JPQL查询(推荐方案)

由于DTO的非空字段是动态变化的,需要手动拼接JPQL的WHERE条件,仅将非空字段作为过滤条件。可以在业务层或自定义Repository中实现:

实现代码(使用EntityManager)

@Service
public class BookService {
    @PersistenceContext
    private EntityManager entityManager;

    public List<Book> filterBooks(BookDTO dto) {
        // 初始WHERE条件设为1=1,方便后续拼接AND条件
        StringBuilder jpql = new StringBuilder("SELECT b FROM Book b WHERE 1=1");
        List<Object> params = new ArrayList<>();

        // 处理字符串类型字段:非空且非空白字符串才加入过滤
        if (dto.getName() != null && !dto.getName().trim().isEmpty()) {
            jpql.append(" AND b.name = ?");
            params.add(dto.getName());
        }
        if (dto.getTitle() != null && !dto.getTitle().trim().isEmpty()) {
            jpql.append(" AND b.title = ?");
            params.add(dto.getTitle());
        }
        if (dto.getIsbn() != null && !dto.getIsbn().trim().isEmpty()) {
            jpql.append(" AND b.isbn = ?");
            params.add(dto.getIsbn());
        }
        if (dto.getAuthor() != null && !dto.getAuthor().trim().isEmpty()) {
            jpql.append(" AND b.author = ?");
            params.add(dto.getAuthor());
        }
        if (dto.getSize() != null && !dto.getSize().trim().isEmpty()) {
            jpql.append(" AND b.size = ?");
            params.add(dto.getSize());
        }

        // 处理int类型pages:注意int默认值为0,若需区分"未传入"和"传入0",建议将DTO的pages改为Integer类型
        // 此处假设pages>0时才作为过滤条件
        if (dto.getPages() > 0) {
            jpql.append(" AND b.pages = ?");
            params.add(dto.getPages());
        }

        // 绑定参数并执行查询
        TypedQuery<Book> query = entityManager.createQuery(jpql.toString(), Book.class);
        for (int i = 0; i < params.size(); i++) {
            query.setParameter(i + 1, params.get(i));
        }
        return query.getResultList();
    }
}

3. 简化方案:结合Spring Data JPA的@Query与SpEL

如果仅需等值匹配,可通过SpEL表达式在@Query中动态判断字段是否有效,无需手动拼接JPQL:

public interface BookRepository extends JpaRepository<Book, Long> {
    @Query("SELECT b FROM Book b WHERE " +
            "(:#{#dto.name == null or #dto.name.trim().isEmpty()} OR b.name = :#{#dto.name}) " +
            "AND (:#{#dto.title == null or #dto.title.trim().isEmpty()} OR b.title = :#{#dto.title}) " +
            "AND (:#{#dto.isbn == null or #dto.isbn.trim().isEmpty()} OR b.isbn = :#{#dto.isbn}) " +
            "AND (:#{#dto.author == null or #dto.author.trim().isEmpty()} OR b.author = :#{#dto.author}) " +
            "AND (:#{#dto.pages <= 0} OR b.pages = :#{#dto.pages}) " +
            "AND (:#{#dto.size == null or #dto.size.trim().isEmpty()} OR b.size = :#{#dto.size})")
    List<Book> findByFilter(@Param("dto") BookDTO dto);
}

这种写法更简洁,但灵活性不足,若需模糊查询(如LIKE)或复杂条件,还是手动拼接JPQL更合适。

关键注意点

  • 基本类型字段问题:DTO中的pages是int类型,默认值为0,无法区分“未传入”和“传入0”,建议将其改为Integer包装类,通过dto.getPages() != null判断是否过滤。
  • 防SQL注入:必须使用参数绑定(?或命名参数),禁止直接拼接字段值到JPQL中。
  • 模糊查询扩展:若需要模糊匹配,只需将JPQL中的=改为LIKE,并给参数添加通配符,例如params.add("%" + dto.getName() + "%")。

内容的提问来源于stack exchange,提问作者Ahmed M. Elzubair

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 11:10:15