Spring Data JPA - 忽略空参数的命名查询

3

我有以下代码库:

@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>作为可选请求参数(底层服务也使用相同的参数):

ResponseEntity<List<Entity>> entities(Optional<Long> firstId, Optional<Long> secondId);

我希望您能根据参数firstIdsecondId来筛选实体,这些参数在数据库中从不为null,但可以通过构造函数传递(搜索参数是可选的)。
问题出现在命名查询时,如果将null作为可选参数传递,则JpaRepository将使用null作为搜索数据库的条件。我不想这样,只要参数为null,就忽略它作为过滤器的作用。
我的解决方案基于Optional
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 Data - ignore parameter if it has a null value,并且创建了一个票据DATAJPA-209

既然这个问题几乎是3年前的了,而且票据也追溯到2012年,我想问问是否存在更舒适和通用的方法来避免处理Optional和重复存储库方法的开销。对于2个这样的参数,解决方案似乎是可接受的,但是我想为4-5个参数实现完全相同的过滤。
3个回答

4
你需要像这样的Specification实用类
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

@Repository
public interface EntityRepository 
    extends JpaRepository<Entity, Long>, JpaSpecificationExecutor<Entity> {

}

使用方法:

@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);        
    }
}

自Spring Data JPA 2.0起,“Specifications”已被弃用,应使用“Specification.where(...)...”代替。 - Nikolas Charalambidis
@Nikolas,谢谢你的留言。Spring Data JPA 2.0是相当新的版本。在我的项目中使用的是1.0版本。 - Oleksii Valuiskyi

2

io.swagger:swagger-codegen-maven-plugin 生成的是Optional,因为我请求它们不是必需的(默认为 required: false)。我可能会将它们生成为装箱类型,例如Long等。

这可能在某种程度上是个人喜好问题。如果是我,并且我可以选择,我会选择不带Optional的版本。我认为它们在这里没有任何有用的贡献。

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());
}
< p >据我所读,Optional类是为可能不存在的返回值而设计的,而不是为其他任何目的而设计的。我认为有一些罕见的情况下我会将它们用于其他用途,但这不是其中之一。< /p >

0
我建议您使用“规范”而不是其他。请参阅文档和示例here
简而言之,思路如下。对于每个属性,您定义一个规范。然后在搜索条件中检查每个属性,如果它不为空,则将相应的规范添加到“连接”的规范中。然后使用此“连接”的规范进行搜索。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接