如果您使用 JPA,我建议使用 Spring Data JPA 和 Specification API。当我最初查看 Spring Boot 和 Spring Data 时,我看到推广的唯一真正的方法是 Spring Data Rest,这对我来说太神奇了。
我个人喜欢让控制器调用服务上的方法(可重用的业务逻辑层,基本上是外观模式)。这些服务最终调用 Spring Data 存储库。
考虑一个User 对象
public class User{
String firstName;
String lastName;
}
假设我们有一个UserController 处理我们对User 资源的REST 请求。此控制器将带有 UserSearchCriteria 的请求转换为 Spring Data Page<User>
import org.springframework.data.domain.Page;
public class UserController{
@Autowired
UserService userService;
@RequestMapping(path = "/users", method = RequestMethod.GET)
Page<User> getAll(HttpServletRequest request, UserSearchCriteria searchCriteria){
return userService.findAllUsers(searchCriteria);
}
}
UserSearchCriteria 是您的客户端将作为查询参数传递的搜索参数类型,例如 GET /users?firstName=mista&lastName=henry,它们会自动映射到 UserSearchCriteria 字段。
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.PageRequest;
public class UserSearchCriteria {
UserSearchCriteria(){
super();
sort = "lastName";
}
Integer size;
Integer page;
String sort;
String sortDir;
String firstName;
String lastName;
Sort buildSort(){
return new Sort(new Sort.Order(Sort.Direction.ASC, sort).ignoreCase());
}
PageRequest toPageRequest(){
if(size == null){
size = Integer.MAX_VALUE; // may or may not be a good idea for your usecase
}
return new PageRequest(page, size, buildSort());
}
}
PageRequest 和 Sort 是 Spring Data 项目的一部分。在我的项目中,我让排序和分页存在于AbstractSearchCriteria 对象中,以便于重用,我的所有搜索条件都扩展了,但如上所示更简单。
在UserService 层,我将委托给我的存储库(我还可以检查访问要求、设置默认值等。
import org.springframework.data.domain.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Component
@Transactional
public class UserService{
@Autowired
UserRepository userRepository;
Page<User> findAllUsers(UserSearchCriteria userSearchCriteria){
if(userSearchCriteria == null){
userSearchCriteria = new UserSearchCriteria();
}
return userRepository.findAll(UserSearchSpecification.findByCriteria(userSearchCriteria), userSearchCriteria.toPageRequest());
}
}
UserSearchSpecification 使用Specification API 将搜索动态添加到您的JPA 调用中,我发现这比直接使用Criteria API 更简洁。
import org.springframework.data.jpa.domain.Specification;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;
public class UserSearchSpecification{
public static Specification<User> findByCriteria(final UserSearchCriteria searchCriteria){
return new Specification<User>() {
@Override
Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> predicates = new ArrayList<Predicate>();
// if firstName in criteria, do an uppercase prefix match
if(searchCriteria.firstName != null){
predicates.add(
cb.like(
cb.upper(root.get("firstName")),
"%" + searchCriteria.lastName.toUpperCase()
)
);
}
// if lastName in criteria, do an uppercase prefix match
if(searchCriteria.lastName != null){
predicates.add(
cb.like(
cb.upper(root.get("lastName")),
"%" + searchCriteria.lastName.toUpperCase()
)
);
}
if(predicates.size() > 0){
return cb.and(predicates.toArray());
}else{
return null;
}
}
}
}
}
这使您可以轻松地检查来自 rest 调用的传入参数(UserSearchSpecification 上的属性)并动态建立数据库调用的限制。最后,我选择将每个谓词and 放在一起,但你可以在这里做任何你想做的事情。您还可以检查相等而不是喜欢、大于/小于等。
最后,请注意此规范在UserService.findAllUsers 方法中传递给UserRepository。这是一个 Spring Data 存储库:
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;
public interface UserRepository extends PagingAndSortingRepository<User, Long>, JpaSpecificationExecutor<User>{
}
在 Spring Data 中,您通常只需要在像这里这样的接口中扩展某些存储库。 Spring Data 可以在后台处理其余部分。重要的是要注意PagingAndSortingRepository 的扩展,它处理分页和排序方面以及JpaSpecificationExecutor,它采用Specification 对象并生成正确的查询。
这可能看起来像很多代码,但随着代码库的增长,这对我来说扩展得非常好。制定规范后,您可以轻松地向 SearchCriteria 对象添加新字段,并通过检查新字段并创建 Predicate 在规范中添加新限制。