spring-data-jpa 및 spring-mvc로 데이터베이스 행 필터링
저는 스프링-mvc 프로젝트가 있는데, 스프링-data-jpa를 데이터 접근에 사용하고 있습니다.이라는 도메인 개체가 있습니다.Travel
최종 사용자가 여러 가지 필터를 적용할 수 있도록 하고 싶습니다.
이를 위해 다음과 같은 컨트롤러를 구현했습니다.
@Autowired
private TravelRepository travelRep;
@RequestMapping("/search")
public ModelAndView search(
@RequestParam(required= false, defaultValue="") String lastName,
Pageable pageable) {
ModelAndView mav = new ModelAndView("travels/list");
Page<Travel> travels = travelRep.findByLastNameLike("%"+lastName+"%", pageable);
PageWrapper<Travel> page = new PageWrapper<Travel>(travels, "/search");
mav.addObject("page", page);
mav.addObject("lastName", lastName);
return mav;
}
잘 작동합니다.사용자는 다음과 같은 양식을 가지고 있습니다.lastName
이동 경로를 필터링하는 데 사용할 수 있는 입력 상자입니다.
성을 넘어서, 나의Travel
도메인 개체에는 필터링할 속성이 훨씬 더 많습니다.이 속성들이 모두 문자열이라면 다음과 같이 추가할 수 있을 것 같습니다.@RequestParam
s 및 spring-data-jpa 메서드를 추가하여 쿼리합니다.예를 들어 방법을 추가하고 싶습니다.findByLastNameLikeAndFirstNameLikeAndShipNameLike
.
하지만 외국 키를 필터링해야 하는데 어떻게 해야 하는지 모르겠습니다.그래서 나의Travel
가 있습니다.period
에 대한 외부 키인 속성.Period
도메인 개체, 사용자가 선택할 때 드롭다운으로 사용해야 합니다.Period
.
기간이 null일 때 lastName으로 필터링된 모든 여행을 검색하고 기간이 null이 아닐 때 lastName으로 필터링된 이 기간의 모든 여행을 검색합니다.
저는 제 저장소에 두 가지 방법을 구현하고 하나의 방법을 사용하면 이것이 가능하다는 것을 알고 있습니다.if
내 컨트롤러에게:
public ModelAndView search(
@RequestParam(required= false, defaultValue="") String lastName,
@RequestParam(required= false, defaultValue=null) Period period,
Pageable pageable) {
ModelAndView mav = new ModelAndView("travels/list");
Page travels = null;
if(period==null) {
travels = travelRep.findByLastNameLike("%"+lastName+"%", pageable);
} else {
travels = travelRep.findByPeriodAndLastNameLike(period,"%"+lastName+"%", pageable);
}
mav.addObject("page", page);
mav.addObject("period", period);
mav.addObject("lastName", lastName);
return mav;
}
이것을 사용하지 않고 할 수 있는 방법이 있습니까?if
? 마이트래블에는 기간뿐만 아니라 드롭다운을 이용하여 필터링해야 하는 다른 속성들도 있습니다!!이해하실 수 있듯이, 드롭다운을 더 많이 사용해야 할 때는 모든 조합을 고려해야 하기 때문에 복잡성이 기하급수적으로 증가할 것입니다. :(
업데이트 03/12/13: M부터 계속.Deinum의 훌륭한 답변, 그리고 이를 실제로 실행한 후 질문/답변의 완성도를 위해 몇 가지 의견을 드리고자 합니다.
구현하는 대신
JpaSpecificationExecutor
당신은 실행해야 합니다.JpaSpecificationExecutor<Travel>
유형 확인 경고를 피할 수 있습니다.이 질문에 대한 kostja의 훌륭한 답변 Really dynamic JPA CriteriaBuilder를 확인해 주세요. 올바른 필터를 사용하려면 이를 구현해야 합니다.
Criteria API에 대한 최고의 문서는 http://www.ibm.com/developerworks/library/j-typesafejpa/ 이었습니다.이것은 다소 긴 읽기이지만 완전히 권장합니다. 이를 읽은 후 Root 및 CriteriaBuilder에 대한 대부분의 질문에 답했습니다. :)
재사용
Travel
체함)다를 가 포함되어 있어 할 수 .Like
- 대신에 나는 a를 사용했습니다.TravelSearch
검색해야 할 필드가 들어 있는 개체입니다.
업데이트 10/05/15: @priyank의 요청에 따라 TravelSearch 객체를 구현한 방법은 다음과 같습니다.
public class TravelSearch {
private String lastName;
private School school;
private Period period;
private String companyName;
private TravelTypeEnum travelType;
private TravelStatusEnum travelStatus;
// Setters + Getters
}
이 개체는 TravelSpecification에서 사용되었습니다(대부분의 코드는 도메인별이지만 예제로 남겨둡니다).
public class TravelSpecification implements Specification<Travel> {
private TravelSearch criteria;
public TravelSpecification(TravelSearch ts) {
criteria= ts;
}
@Override
public Predicate toPredicate(Root<Travel> root, CriteriaQuery<?> query,
CriteriaBuilder cb) {
Join<Travel, Candidacy> o = root.join(Travel_.candidacy);
Path<Candidacy> candidacy = root.get(Travel_.candidacy);
Path<Student> student = candidacy.get(Candidacy_.student);
Path<String> lastName = student.get(Student_.lastName);
Path<School> school = student.get(Student_.school);
Path<Period> period = candidacy.get(Candidacy_.period);
Path<TravelStatusEnum> travelStatus = root.get(Travel_.travelStatus);
Path<TravelTypeEnum> travelType = root.get(Travel_.travelType);
Path<Company> company = root.get(Travel_.company);
Path<String> companyName = company.get(Company_.name);
final List<Predicate> predicates = new ArrayList<Predicate>();
if(criteria.getSchool()!=null) {
predicates.add(cb.equal(school, criteria.getSchool()));
}
if(criteria.getCompanyName()!=null) {
predicates.add(cb.like(companyName, "%"+criteria.getCompanyName()+"%"));
}
if(criteria.getPeriod()!=null) {
predicates.add(cb.equal(period, criteria.getPeriod()));
}
if(criteria.getTravelStatus()!=null) {
predicates.add(cb.equal(travelStatus, criteria.getTravelStatus()));
}
if(criteria.getTravelType()!=null) {
predicates.add(cb.equal(travelType, criteria.getTravelType()));
}
if(criteria.getLastName()!=null ) {
predicates.add(cb.like(lastName, "%"+criteria.getLastName()+"%"));
}
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
}
}
마지막으로 검색 방법은 다음과 같습니다.
@RequestMapping("/search")
public ModelAndView search(
@ModelAttribute TravelSearch travelSearch,
Pageable pageable) {
ModelAndView mav = new ModelAndView("travels/list");
TravelSpecification tspec = new TravelSpecification(travelSearch);
Page<Travel> travels = travelRep.findAll(tspec, pageable);
PageWrapper<Travel> page = new PageWrapper<Travel>(travels, "/search");
mav.addObject(travelSearch);
mav.addObject("page", page);
mav.addObject("schools", schoolRep.findAll() );
mav.addObject("periods", periodRep.findAll() );
mav.addObject("travelTypes", TravelTypeEnum.values());
mav.addObject("travelStatuses", TravelStatusEnum.values());
return mav;
}
내가 도와줬기를!
.@RequestParam
모든 검색 필드를 개체에 저장합니다(여행 개체를 재사용할 수도 있음).다를 데할 수 두 .
- 을 합니다.
JpaSpecificationExecutor
그리고 a를 씁니다.Specification
- 을 합니다.
QueryDslPredicateExecutor
그리고 QueryDSL을 사용하여 술어를 작성합니다.
으로.JpaSpecificationExecutor
.JpaSpecificationExecutor
o대로TravelRepository
이것은 당신에게 줄 것입니다.findAll(Specification)
및할 수 finder다를정합니다.
public interface TravelRepository extends JpaRepository<Travel, Long>, JpaSpecificationExecutor<Travel> {}
에 수 .Specification
기본적으로 쿼리를 구축합니다.이에 대해서는 Spring Data JPA 설명서를 참조하십시오.
를 만 만들면 .Specification
사용 가능한 필드를 기반으로 쿼리를 구성합니다.쿼리는 JPA Criteria API 링크를 사용하여 빌드됩니다.
public class TravelSpecification implements Specification<Travel> {
private final Travel criteria;
public TravelSpecification(Travel criteria) {
this.criteria=criteria;
}
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
// create query/predicate here.
}
}
를 .findAll
method (나는 자유롭게 그것을 조금 치웠습니다.
@RequestMapping("/search")
public String search(@ModelAttribute Travel search, Pageable pageable, Model model) {
Specification<Travel> spec = new TravelSpecification(search);
Page<Travel> travels = travelRep.findAll(spec, pageable);
model.addObject("page", new PageWrapper(travels, "/search"));
return "travels/list";
}
으로.QueryDslPredicateExecutor
.QueryDslPredicateExecutor
o대로TravelRepository
이것은 당신에게 줄 것입니다.findAll(Predicate)
및할 수 finder다를정합니다.
public interface TravelRepository extends JpaRepository<Travel, Long>, QueryDslPredicateExecutor<Travel> {}
으로 합니다.Travel
QueryDSL을 사용하여 술어를 만들 개체입니다.
@Service
@Transactional
public class TravelService {
private final TravelRepository travels;
public TravelService(TravelRepository travels) {
this.travels=travels;
}
public Iterable<Travel> search(Travel criteria) {
BooleanExpression predicate = QTravel.travel...
return travels.findAll(predicate);
}
}
이 보그 포스트도 참조하십시오.
언급URL : https://stackoverflow.com/questions/20280708/filtering-database-rows-with-spring-data-jpa-and-spring-mvc
'programing' 카테고리의 다른 글
jQuery에서 사용자가 해당 필드를 편집하는 동안 텍스트 필드의 첫 글자를 대문자로 쓰는 방법은 무엇입니까? (0) | 2023.10.06 |
---|---|
모든 항목이 중간 텍스트 크기의 필드 안에 들어갈 때 MySQL 긴 텍스트 크기의 필드를 사용하면 어떤 단점이 있습니까? (0) | 2023.10.06 |
WordPress와 데이터베이스 연결 설정 오류 디버그 방법 (0) | 2023.10.06 |
발신자 기능의 이름은 어떻게 알 수 있습니까? (0) | 2023.10.06 |
사전 증분 연산자가 r값을 C로 제공하는 이유는 무엇입니까? (0) | 2023.10.06 |