Spring Data JPA - 忽略空参数的命名查询
Spring Data JPA - Named query ignoring null parameters
我有以下存储库:
@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);
我想根据参数 firstId
和 secondId
过滤实体,这些参数在数据库中永远不会 null
但可以通过构造函数传递(参数搜索是可选的)。
当作为可选参数传递 null
时,命名查询会出现问题,JpaReposotory
使用 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 and a ticket created DATAJPA-209。
只要这个问题已经有将近 3 年的历史并且票证可以追溯到 2012 年,我想问一下是否有更舒适和通用的方法来避免处理 Optional
和复制存储库方法。 2 个这样的参数的解决方案看起来可以接受,但是我想对 4-5 个参数实施完全相同的过滤。
我建议您改用 规格。请参阅文档和示例 here。
简单来说,思路如下。您为每个属性定义一个规范。然后检查搜索条件中的每个属性,如果它不为空,则将相应的规范添加到 "concatenated" 规范。然后您使用此 "concatenated" 规范进行搜索。
你需要这样的 Specification
实用工具 class
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);
}
}
The io.swagger:swagger-codegen-maven-plugin
generates them as
Optional
since I request them as not required (required: false
by
default). I might generate them as boxed types, such as 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());
}
Optional
class 被设计用于可能不存在的 return 值,而不是其他任何东西,所以我已经阅读了。我认为在极少数情况下我会将它们用于其他用途,但这不是其中之一。
我有以下存储库:
@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);
我想根据参数 firstId
和 secondId
过滤实体,这些参数在数据库中永远不会 null
但可以通过构造函数传递(参数搜索是可选的)。
当作为可选参数传递 null
时,命名查询会出现问题,JpaReposotory
使用 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 and a ticket created DATAJPA-209。
只要这个问题已经有将近 3 年的历史并且票证可以追溯到 2012 年,我想问一下是否有更舒适和通用的方法来避免处理 Optional
和复制存储库方法。 2 个这样的参数的解决方案看起来可以接受,但是我想对 4-5 个参数实施完全相同的过滤。
我建议您改用 规格。请参阅文档和示例 here。
简单来说,思路如下。您为每个属性定义一个规范。然后检查搜索条件中的每个属性,如果它不为空,则将相应的规范添加到 "concatenated" 规范。然后您使用此 "concatenated" 规范进行搜索。
你需要这样的 Specification
实用工具 class
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);
}
}
The
io.swagger:swagger-codegen-maven-plugin
generates them asOptional
since I request them as not required (required: false
by default). I might generate them as boxed types, such asLong
, …
这可能部分是品味问题。如果是我并且我可以,我会选择没有 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());
}
Optional
class 被设计用于可能不存在的 return 值,而不是其他任何东西,所以我已经阅读了。我认为在极少数情况下我会将它们用于其他用途,但这不是其中之一。