简体   繁体   English

在 JPA 查询时 - JPQL

[英]when query at JPA - JPQL

My system required to add filters,and I'm wonder if there any query that like this我的系统需要添加过滤器,我想知道是否有任何这样的查询

SELECT * 
FROM posts p
when byDate is not null then (where p.createAt BETWEEN :startDate AND :endDate)
when byType is not null then (where p.type = :type)

I knew that the query is not valid, but I want at one query to get the data wherever the request has (no filter or all filters or some of filters).我知道该查询无效,但我希望在一个查询中获取请求所在的任何位置的数据(没有过滤器或所有过滤器或某些过滤器)。

My goal is to create one query to achieve all cases.我的目标是创建一个查询来实现所有情况。

It's usually not a good idea to write a big SQL query when you can tell in advance the actual query you want to run.当您可以提前知道要运行的实际查询时,编写大型 SQL 查询通常不是一个好主意。

If you want to run a different query based on conditions you know before running the query, there are different approaches in JPA or Spring that you can use如果您想在运行查询之前根据您知道的条件运行不同的查询,您可以使用 JPA 或 Spring 中的不同方法

Spring春天

You can define the different queries using Spring Data query methods ?您可以使用Spring Data 查询方法定义不同的查询吗?

public class PostRepository implements JpaRepository<Post, Long> {
    List<Post> findByCreatedAtBetween(Date startDate, Date endDate);
    List<Post> findByTypeIs(String type);
}

And then somewhere in the code, you can:然后在代码的某个地方,您可以:

List<Post> results = null;
if (byDate != null) {
   results = repository.findByCreatedAtBetween(startDate, endDate);
} else if (byType != null) {
   results = repository.findByTypeIs(type);
} else {
   results = repository.findAll();
}

Criteria标准

With criteria you can create a dynamic query at runtime and execute it:使用条件,您可以在运行时创建动态查询并执行它:

public class PostRepository implements PostRepositoryCustom {

    @PersistenceContext
    private EntityManager em;

    @Override
    public List<Post> findPosts(Filter filter) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<User> query = cb.createQuery(Post.class);
        Root<User> user = query.from(Post.class);

        if ((filter.getByDate() != null)) {
          // byDate is not null
          ParameterExpression<Date> startDate = builder.parameter( Date.class );
          ParameterExpression<Date> endDate = builder.parameter( Date.class );
          query.where(builder.between( b.get( "createdAt" ), startDate, endDate));
          return em.createQuery(query)
                   .setParameter(startDate, ...)
                   .setParameter(endDate, ...)
                   .getResultList();
        }
        
        if (filter.getByType() != null) {
          ParameterExpression<Date> typeParam = builder.parameter( Date.class );

          query.where(builder.and(root.get("type"), typeParam));
          return em.createQuery(query)
                   .setParameter(typeParam, ...)
                   .getResultList();

        }

        return entityManager.createQuery(query)
            .getResultList();
    }
}

Assuming that your entity has the fields type and createdAt .假设您的实体具有字段typecreatedAt This approach works well if you don't know in advance what's your query looks like.如果您事先不知道您的查询是什么样的,这种方法很有效。 For example, when you don't know how many conditions you will have to add to it.例如,当您不知道必须添加多少条件时。

But, if I know already which query I want to run, then I prefer to use HQL/JPQL.但是,如果我已经知道要运行哪个查询,那么我更喜欢使用 HQL/JPQL。

HQL高品质

If your queries don't change and you already know what they look like, I find it easier to define them with HQL :如果您的查询没有改变并且您已经知道它们的样子,我发现使用HQL更容易定义它们:

public class PostRepository implements PostRepositoryCustom {

    @PersistenceContext
    private EntityManager em;

    @Override
    public List<Post> findPosts(Filter filter) {
       if (filter.getByDate() != null) {
          return em.createQuery("from Post p where p.createdAt between :startDate and :endDate", Post.class)
                    .setParameter("startDate", ...)
                    .setParameter("endDate", ...)
                    .getResultList();
       }
       if (filter.getByType() != null) {
          return em.createQuery("from Post p where p.type =:type", Post.class)
                    .setParameter("type", ...)
                    .getResultList();
       }
       return em.createQuery("from Post", Post.class)
                    .getResultList();       
    }
}

You can refactor the code to make it more elegant, but it should give you an idea.您可以重构代码以使其更优雅,但它应该给您一个想法。 Note that if you need to reuse the same queries in different services, it might be helpful to define them using the annotation @NamedQuery .请注意,如果您需要在不同的服务中重用相同的查询,使用注释@NamedQuery定义它们可能会有所帮助。

Filters过滤器

In Hibernate (not JPA) you can also define filters .在 Hibernate(不是 JPA)中,您还可以定义过滤器 They are SQL filter conditions that one can apply at runtime:它们是可以在运行时应用的 SQL 过滤条件:

@Entity
@FilterDef(name = Post.BY_DATE, defaultCondition = "createdAt between :startDate and :endDate", parameters = {@ParamDef(name = "startDate", type = "date"), @ParamDef(name = "startDate", type = "date") })
@FilterDef(name = Post.BY_TYPE, defaultCondition = "type = :type", parameters = @ParamDef(name = "startDate", type = "date"))
@Filter(name = Post.BY_DATE)
@Filter(name = Post.BY_TYPE)
class Post {
   static final String BY_DATE = "Post.byDateFilter";
   static final String BY_TYPE = "Post.byFilter"

   private String type;
   private Date createdAt;
   ...
}

Then:然后:

public class PostRepository implements PostRepositoryCustom {

    @PersistenceContext
    private EntityManager em;

    @Override
    public List<Post> findPosts(Filter filter) {
       enableFilters(em);
       return em.createQuery("from Post", Post.class).getResultList();
    }

    private void enableFilters(Filter filter, EntityManager em) {
       if (filter.getByDate() != null) {
          em.unwrap(Session.class)
            .enableFilter( Post.BY_DATE )
            .setParameter("startDate", ...)
            .setParameter("endDate", ...);
       } else if (filter.getByType() != null) {
          em.unwrap(Session.class)
            .enableFilter( Post.BY_TYPE )
            .setParameter("type", ...);
       }
    }
}

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

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