首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Spring数据JPA命名的查询忽略空参数

Spring数据JPA命名的查询忽略空参数
EN

Stack Overflow用户
提问于 2020-02-15 22:29:48
回答 3查看 2.4K关注 0票数 3

我有以下存储库:

代码语言:javascript
复制
@Repository
public interface EntityRepository extends JpaRepository<Entity, Long> {

    List<Entity> findAllByFirstId(Long firstId);
    List<Entity> findAllBySecondId(Long secondId);
    List<Entity> findAllByFirstIdAndSecondId(Long firstId, Long secondId);
}

实现用io.swagger:swagger-codegen-maven-plugin生成的接口的构造函数使用Optional<Long>作为可选的请求参数(底层服务也使用相同的参数):

代码语言:javascript
复制
ResponseEntity<List<Entity>> entities(Optional<Long> firstId, Optional<Long> secondId);

我想根据参数firstIdsecondId过滤实体,这些参数从来不是数据库中的null,但可以通过构造函数传递(用于搜索的参数是可选的)。

当传递null参数为可选参数时,命名查询会出现问题,JpaReposotory使用null作为数据库中搜索的标准。这就是我不想要的--只要是null,我就想忽略基于这个参数的过滤。

我基于Optional的解决方案是:

代码语言:javascript
复制
public List<Entity> entities(Optional<Long> firstId, Optional<Long> secondId) {

    return firstId
        .or(() -> secondId)
        .map(value -> {
            if (firstId.isEmpty()) {
                return entityRepository.findAllBySecondId(value);
            }
            if (secondId.isEmpty()) {
                return entityRepository.findAllByFirstId(value);
            }
            return entityRepository.findAllByFirstIdAndSecondId(
            firstId.get(), secondId.get());
        })
        .orElse(entityRepository.findAll())
        .stream()
        .map(...)     // Mapping between DTO and entity. For sake of brevity
                      // I used the same onject Entity for both controler and repository 
                      // as long as it not related to the question   

        .collect(Collectors.toList());
}

这个问题已经被问到了:Spring数据-如果参数为空值,则忽略参数和一张创建DATAJPA-209的票据。

只要这个问题已经存在了将近3年,并且票证可以追溯到2012年,我想问,是否有一种更舒适和更通用的方法来避免处理Optional和复制存储库方法的开销。对于两个这样的参数的解决方案看起来是可以接受的,但是我想对4-5参数实现相同的过滤。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2020-02-16 06:52:37

您需要像这样的Specification实用程序类

代码语言:javascript
复制
public class EntitySpecifications {
    public static Specification<Entity> firstIdEquals(Optional<Long> firstId) {// or Long firstId. It is better to avoid Optional method parameters.
        return (root, query, builder) -> 
            firstId.isPresent() ? // or firstId != null if you use Long method parameter
            builder.equal(root.get("firstId"), firstId.get()) :
            builder.conjunction(); // to ignore this clause
    }

    public static Specification<Entity> secondIdEquals(Optional<Long> secondId) {
        return (root, query, builder) -> 
            secondId.isPresent() ? 
            builder.equal(root.get("secondId"), secondId.get()) :
            builder.conjunction(); // to ignore this clause
    }
}

那么您的EntityRepository必须扩展JpaSpecificationExecutor

代码语言:javascript
复制
@Repository
public interface EntityRepository 
    extends JpaRepository<Entity, Long>, JpaSpecificationExecutor<Entity> {

}

用法:

代码语言:javascript
复制
@Service
public class EntityService {    

    @Autowired
    EntityRepository repository;

    public List<Entity> getEntities(Optional<Long> firstId, Optional<Long> secondId) {
        Specification<Entity> spec = 
            Specifications.where(EntitySpecifications.firstIdEquals(firstId)) //Spring Data JPA 2.0: use Specification.where
                          .and(EntitySpecifications.secondIdEquals(secondId));

        return repository.findAll(spec);        
    }
}
票数 4
EN

Stack Overflow用户

发布于 2020-02-16 14:11:29

io.swagger:swagger-codegen-maven-pluginOptional的形式生成它们,因为我请求它们不是必需的(默认情况下是required: false)。我可能会将它们生成为装箱类型,例如Long、…

这可能在一定程度上取决于品味。如果是我和我可以的话,我会选择没有Optional的版本。我认为他们在这里没有贡献任何有用的东西。

代码语言:javascript
复制
public List<Entity> entities(Long firstId, Long secondId) {

    List<Dto> dtos;
    if (firstId == null) {
        if (secondId == null) {
            dtos = entityRepository.findAll();
        } else {
            dtos = entityRepository.findAllBySecondId(secondId);
        }
    } else {
        if (secondId == null) {
            dtos = entityRepository.findAllByFirstId(firstId);
        } else {
            dtos = entityRepository.findAllByFirstIdAndSecondId(firstId, secondId);
        }
    }

    return dtos.stream()
        .map(...)
        .collect(Collectors.toList());
}

Optional类被设计为用于返回可能没有的值,而不是用于其他任何内容,因此我已经阅读了。我想我很少会用它们来做其他的事情,但这不是其中之一。

票数 2
EN

Stack Overflow用户

发布于 2020-02-16 01:12:47

我建议你用规格代替。请参阅文档和示例这里

简单地说,这个想法如下。对于每个属性,都定义了一个规范。然后检查搜索条件中的每个属性,如果不是null,将相应的规范添加到“串联”规范中。然后使用这个“级联”规范进行搜索。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60243712

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档