【问题标题】:Spring data JPA and parameters that can be nullSpring数据JPA和可以为空的参数
【发布时间】:2015-07-25 00:23:06
【问题描述】:

我的理解是,使用 Spring data JPA,我无法使用查询方法来获取列等于给定非空方法参数的所有行,并使用相同的方法来获取该列为 NULL 的所有行参数为空。

对吗?

所以我必须在我的 JAVA 代码中区分这一点,并且我必须使用单独的查询方法明确要求空值,如下例所示?

// Query methods
List<Something> findByParameter(Parameter parameter);
List<Something> findByParameterIsNull();

...

List<Something> result = new ArrayList<>();

if (parameter == null)
  result = findByParameterIsNull();
else
  result = findByParameter(parameter);

如果我有 4 个可能为 null 的参数并且必须编写 16 种不同的查询方法,那就太糟糕了。

【问题讨论】:

    标签: java parameters null spring-data-jpa


    【解决方案1】:

    我能够使用以下解决方法在空输入的情况下适当地应用IS NULL

        @Query("SELECT c FROM ConfigRLLOAContent c WHERE ((:suffixId IS NOT NULL AND c.suffixId = :suffixId) OR (:suffixId IS NULL AND c.suffixId IS NULL))")
    Optional<ConfigRLLOAContent> findByRatableUnitId(@Param("suffixId") String suffixId);
    

    仅当suffixId 为非空时,上述方法才会应用过滤器, 否则,将应用 IS NULL 过滤器。

    github上也提了一个问题,建议引入@NullMeanshere

    【讨论】:

      【解决方案2】:

      虽然已经回答了这个问题并且接受的答案与当前问题相关,但是还有另一种方法可以处理 JpaRespository 中的空参数。在此处发布此内容,因为当有人想要通过在 null 时忽略字段并构建动态查询来进行查询时,可以利用这一点。 下面的代码示例应该演示相同

      public class User{
        private String firstName;
        private String lastName;
      }
      
      import javax.persistence.criteria.Predicate;
      import org.springframework.data.jpa.domain.Specification;
      import org.springframework.data.jpa.repository.JpaRepository;
      
      public interface UserRepository extends JpaRepository<User,Long>{
      
        public Page<AppUser> findAll(Specification<AppUser> user,Pageable page);
      
        public default Page<AppUser> findAll(User user,Pageable page){
          return findAll(search(user),page);
        }
      
        static Specification<User> search(User entity) {
          return (root, cq, cb) -> {
            //To ensure we start with a predicate
            Predicate predicate = cb.isTrue(cb.literal(true));
            if(entity.getFirstName() != null && !entity.getFirstName().isBlank()) {
              Predicate _predicate = cb.like(cb.lower(root.get("firstName")), "%"+entity.getFirstName().toLowerCase()+"%");
              predicate = cb.and(predicate,_predicate);
            }
            if(entity.getLastName() != null && !entity.getLastName().isBlank()) {
              Predicate _predicate = cb.like(cb.lower(root.get("lastName")), "%"+entity.getLastName().toLowerCase()+"%");
              predicate = cb.and(predicate,_predicate);
            }
            return predicate;
          }
        }
      }
      

      【讨论】:

      • 这是我一直在寻找的示例,但是我在构建时收到错误消息:No property findAll found for type User.
      • 完整的堆栈可能会有所帮助,或者可能是由于未添加 JPA2 启用注释。在这种情况下,明确定义了 findAll 方法。
      【解决方案3】:

      在我的情况下,membershipNumber 是可以为空的,我就是这样处理的。这将处理 table.membershipNumber 也为空的所有情况。

            @Query(value = "SELECT pr FROM ABCTable pr " +
                  "WHERE LOWER(pr.xyz) = LOWER(:xyz) " +
                  "and LOWER(pr.subscriptionReference) = LOWER(:subscriptionReference) " +
                  "and pr.billId = :billId " +
                  "and ((pr.membershipNumber = :membershipId) or (pr.membershipNumber = null and :membershipId = null))")
          List<PaymentRequest> getSomething (@Param("xyz") String xyz,
                                                       @Param("subscriptionReference") String subscriptionReference,
                                                       @Param("billId") Integer billId,
                                                       @Param("membershipId") String membershipNumber);
      
      

      【讨论】:

      • 这可以处理我猜想的所有情况。
      【解决方案4】:

      我发现了一些东西......如果你像这样将参数放在 jpa 方法中

      @Param("value") String value,
      

      那么它可以为空,在查询中你将有这个条件:

      (table.value = :value OR :value IS NULL)
      

      如果值为null,它会自动返回true,如果不是null,它会在表中搜索该值。

      【讨论】:

      • 让我感动的一件事是,如果我这样做了,它就不起作用了 (:value IS NULL OR table.value = :value) 它告诉我我需要强制转换 :value 但是在你写作品的顺序。
      • :vales is null 如果值是一个集合将不起作用。
      • 我发现这个解决方案比实现 JpaSpecificationExecutor 或 QueryDslPredicateExecutor 更好(也更简单); reference。我也关注了这个blog
      • 对于传递的空值,它只会跳过选择查询的字段。可能会导致额外的结果集。
      【解决方案5】:

      截至 2018 年 6 月的今天,通过查看 https://jira.spring.io/browse/DATAJPA-121,如果您的参数为空,查询将自动形成 is null

      我在我的项目中做到了,这是真的:

      compile group: 'org.springframework.data', name: 'spring-data-jpa', version: '2.0.7.RELEASE'
      

      --

      public interface AccountDao extends CrudRepository<T, ID> {
      
          //this can accept null and it will become isNull
          public List<MyAccount> findByEmail(String email);
      
      }
      

      如果参数为空:

      select
              myaccount0_.id as id1_0_,
              myaccount0_.email as email2_0_,
              myaccount0_.password as password3_0_,
              myaccount0_.user_id as user_id4_0_ 
          from
              my_account myaccount0_ 
          where
              myaccount0_.email is null
      

      如果参数不为空:

      select
              myaccount0_.id as id1_0_,
              myaccount0_.email as email2_0_,
              myaccount0_.password as password3_0_,
              myaccount0_.user_id as user_id4_0_ 
          from
              my_account myaccount0_ 
          where
              myaccount0_.email=?
      11:02:41.623 [qtp1507181879-72] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [1] as [VARCHAR] - [testing@hotmail.com] 
      

      然后到了一个有趣的问题,一些开发者想要更好的控制来忽略查询中的参数如果它为空,这仍在https://jira.spring.io/browse/DATAJPA-209中调查。

      【讨论】:

        【解决方案6】:

        你是对的。

        已请求支持更好地处理空参数。 https://jira.spring.io/browse/DATAJPA-121

        在您的情况下,我建议您编写存储库实现并使用自定义CriteriaQuery 来处理您的情况。

        您还可以将@Query 注释与is null 语法一起使用:

        @Query("[...] where :parameter is null"
        public List<Something> getSomethingWithNullParameter();
        

        编辑

        从 Spring data jpa 2.0 开始,spring 现在支持 @Nullable 注解。这有助于处理传递的空参数。

        来自documentation

        @Nullable – 用于可以为 null 的参数或返回值。

        【讨论】:

        • 我已经扩展了答案:使用 Spring data JPA @Query 注解时,您必须使用两种单独的查询方法。一种用于处理空值,一种用于处理非空值。但是当使用由 Spring 数据解析器解析的查询方法时(没有 @Query 注释),可以使用一种方法处理 null 或非 null 值!
        • @Query :parameter is null 不起作用。它会因异常而失败,但即使不会,SQL 也会将空值视为未知数。因此,未知不等于未知......基本上null 不等于 SQL 中的null
        • 如何为 Nullable 提供帮助?如果我们将参数注释为@Nullable,我认为Spring Data不会将查询变为“为空”。
        【解决方案7】:

        看来Query by Example 可能是您需要的。

        Query by Example 是 Spring Data 中的一项新功能(since version Hopper,2016 年 4 月推出),它允许使用这样的代码创建简单的动态查询

        Person person = new Person();                          
        person.setFirstname("Dave");                           
        
        ExampleMatcher matcher = ExampleMatcher.matching()     
          .withIncludeNullValues();                        
        
        Example<Person> example = Example.of(person, matcher);
        
        personRepository.count(example);
        personRepository.findOne(example);
        personRepository.findAll(example);
        

        org.springframework.data.domain.Example 的实例作为参数的count/findOne/findAll 方法(其中一些还采用排序/分页参数)来自org.springframework.data.repository.query.QueryByExampleExecutor&lt;T&gt; 接口,该接口由org.springframework.data.jpa.repository.JpaRepository&lt;T, ID extends Serializable&gt; 接口扩展。

        简而言之,所有JpaRepository 实例现在都有这些方法。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-03-13
          • 2019-05-30
          • 2019-07-02
          • 1970-01-01
          • 2013-06-02
          • 2019-04-09
          • 2018-05-08
          • 1970-01-01
          相关资源
          最近更新 更多