스프링 부츠 & JPA:선택적이고 범위가 넓은 기준으로 검색 쿼리 구현
이것은 SSCCE이고, 연구를 보여주며, 속임수가 아니며 주제에 있습니다!!!
스프링 부트 REST 서비스와 MySQL입니다.나는 다음을 가지고 있습니다.Profile엔티티:
@Entity
@Table(name = "profiles")
public class Profile extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "profile_given_name")
private String givenName;
@Column(name = "profile_surname")
private String surname;
@Column(name = "profile_is_male")
private Integer isMale;
@Column(name = "profile_height_meters", columnDefinition = "DOUBLE")
private BigDecimal heightMeters;
@Column(name = "profile_weight_kilos", columnDefinition = "DOUBLE")
private BigDecimal weightKilos;
@Column(name = "profile_dob")
private Date dob;
// Getters, setters & ctor down here
}
저도 있습니다.ProfileController 유연하고 강력한 검색 방법을 하는 GET 끝점을 노출하고 .Profiles광범위한 기준에 기초함:
# Search for women between 1.2 and 1.8 meters tall.
GET /v1/profiles?isMale=0&heightMeters={"gt": 1.2, "lt": 1.8}
# Search for men born after Jan 1, 1990 who weigh less than 100 kg.
GET /v1/profiles?isMale=1&dob={"gt" : "1990-01-01 00:00:00"}&weightKilos={"lt": 100.0}
기타.
여기 제 컨트롤러가 있습니다.
@RestController
@RequestMapping("/v1/profiles")
public class ProfileResource {
@Autowired
ProfileRepository profileRepository;
@GetMapping
public ResponseEntity<Set<Profile>> searchProfiles(@RequestParam(value = "isMale", required = false) String isMaleVal,
@RequestParam(value = "heightMeters", required = false) String heightMetersVal,
@RequestParam(value = "weightKilos", required = false) String weightKilosVal,
@RequestParam(value = "dob", required = false) String dobVal) {
Integer isMaleVal;
BooleanCriteria isMaleCriteria;
if(isMaleVal != null) {
// Parse the value which could either be "0" for female, "1" for male or something like
// ?isMale={0,1} to indicate
// BooleanCriteria would store which values male, female or both) to include in the search
}
BigDecimal heighMeters;
BigDecimalCriteria heightCriteria;
if(heightMetersVal != null) {
// Parse the value which like in the examples could be something like:
// ?heightMeters={"gt" : "1.0"}
// BigDecimalCriteria stores range information
}
BigDecimal heighMeters;
BigDecimalCriteria weightCriteria;
if(weightKilosVal != null) {
// Parse the value which like in the examples could be something like:
// ?weightKilos={"eq" : "100.5"}
// BigDecimalCriteria stores range information
}
// Ditto for DOB and DateCriteria
// TODO: How to pack all of these "criteria" POJOs into a
// CrudRepository/JPQL query against the "profiles" table?
Set<Profile> profiles = profileRepository.searchProfiles(
isMaleCriteria, heightCriteria, weightCriteria, dobCriteria);
}
}
를 들면, 엔생각자, 면하말내,BigDecimalCriteria다음과 같은 것이 될 수 있습니다.
// Basically it just stores the (validated) search criteria that comes in over the wire
// on the controller method
public class BigDecimalCriteria {
private BigDecimal lowerBound;
private Boolean lowerBoundInclusive;
private BigDecimal upperBound;
private Boolean upperBoundInclusive;
// Getters, setters, ctors, etc.
}
이 모든 검색 기준은 선택 사항이므로 다음과 같이 할 수 있습니다.null , )에서를 작성하는 고민하고 있습니다.ProfileRepository:
public interface ProfileRepository extends CrudRepository<Profile,Long> {
@Query("???")
public Set<Profile> searchProfiles();
}
을 어떻게 할 수 ?@Query(...)위해서ProfileRepository#searchProfiles모든 검색 기준(검색할 수 있는 모든 허용 범위 및 기준 값이 주어짐)을 활성화하고 모든 기준이 null/선택 사항이 되도록 허용하는 방식?
물론, 작은 도서관이 많거나 Spring Boot/J.PA는 이미 이에 대한 해결책을 가지고 있습니다. 저는 귀를 기울입니다!
다음을 통해 사양이 포함된 복잡한 쿼리를 수행할 수 있습니다.JpaSpecificationExecutor춘계 자료로리포지토리 인터페이스는 다음을 확장해야 합니다.JpaSpecificationExecutor<T>할 수 . 새로운 쿼리를 생성하여 쿼리 조건을 지정할 수 있습니다.Specification<T>물건들.
를 Specification 인터페이스와 입니다.JpaSpecificationExecutor예는 다음과 같습니다.
@Entity
@Table(name = "person")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "surname")
private String surname;
@Column(name = "city")
private String city;
@Column(name = "age")
private Integer age;
....
}
그런 다음 저장소를 정의합니다.
public interface PersonRepository extends JpaRepository<Person, Long>, JpaSpecificationExecutor<Person> {
}
했습니다.JpaSpecificationExecutor이 인터페이스는 사양 클래스를 통해 검색을 수행하는 방법을 정의합니다.
이제 우리가 해야 할 일은 다음을 반환할 사양을 정의하는 것입니다.Predicate 조건 포함(예: " " " " " " " " "PersonSpecification= ? 예: = ? 및= ? 에서 select * 하는 중입니다. = ? (예: = ? = ?)의 경우 select *를 선택합니다.
public class PersonSpecification implements Specification<Person> {
private Person filter;
public PersonSpecification(Person filter) {
super();
this.filter = filter;
}
public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> cq,
CriteriaBuilder cb) {
Predicate p = cb.disjunction();
if (filter.getName() != null) {
p.getExpressions()
.add(cb.equal(root.get("name"), filter.getName()));
}
if (filter.getSurname() != null && filter.getAge() != null) {
p.getExpressions().add(
cb.and(cb.equal(root.get("surname"), filter.getSurname()),
cb.equal(root.get("age"), filter.getAge())));
}
return p;
}
}
이제 그것을 사용할 시간입니다.다음 코드 조각은 방금 만든 사양을 사용하는 방법을 보여줍니다.
...
Person filter = new Person();
filter.setName("Mario");
filter.setSurname("Verdi");
filter.setAge(25);
Specification<Person> spec = new PersonSpecification(filter);
List<Person> result = repository.findAll(spec);
다음은 github의 전체 예입니다.
또한 사양을 사용하여 복잡한 쿼리를 만들 수 있습니다.
Querydsl 및 웹 지원 Spring Data 확장의 도움으로 필요한 거의 모든 것이 Spring Data에 이미 구현되어 있습니다.
당신은 또한 당신의 레포를 확장해야 합니다.QuerydslPredicateExecutor또한 Spring Data REST를 사용하는 경우 기본 필터링, 페이징 및 정렬 지원을 통해 repo 데이터를 바로 '박스에서' 쿼리할 수 있습니다.
/profiles?isMale=0&heightMeters=1.7&sort=dob,desc&size=10&page=2
보다 복잡한 필터를 구현하려면 에서 레포를 확장해야 합니다.QuerydslBinderCustomizer그리고 그것을 사용합니다.customize메소드(레포에 저장되어 있음).
예를 들어 다음에 대한 '사이' 필터를 구현할 수 있습니다.heightMeters및 '좋아요' 필터:surname:
public interface ProfileRepository extends JpaRepository<Profile, Long>, QuerydslPredicateExecutor<Profile>, QuerydslBinderCustomizer<QProfile> {
@Override
default void customize(QuerydslBindings bindings, QProfile profile) {
bindings.excluding( // used to exclude unnecessary fields from the filter
profile.id,
profile.version,
// ...
);
bindings.bind(profile.heightMeters).all((path, value) -> {
Iterator<? extends BigDecimal> it = value.iterator();
BigDecimal from = it.next();
if (value.size() >= 2) {
BigDecimal to = it.next();
return path.between(from, to)); // between - if you specify heightMeters two times
} else {
return path.goe(from); // or greter than - if you specify heightMeters one time
}
});
bindings.bind(profile.surname).first(StringExpression::containsIgnoreCase);
}
}
그런 다음 프로필을 쿼리할 수 있습니다.
/profiles?isMale=0&heightMeters=1.4&heightMeters=1.6&surename=doe
예를 들어 - 키가 1.4~1.6m이고 이름에 'surename'이 포함된 모든 여성을 찾습니다.
Spring Data REST를 사용하지 않는 경우 QueryDSL 지원을 통해 자체 Rest Controller 방법을 구현할 수 있습니다.
@RestController
@RequestMapping("/profiles")
public class ProfileController {
@Autowired private ProfileRepository profileRepo;
@GetMapping
public ResponseEntity<?> getAll(@QuerydslPredicate(root = Profile.class, bindings = ProfileRepository.class) Predicate predicate, Pageable pageable) {
Page<Profile> profiles = profileRepo.findAll(predicate, pageable);
return ResponseEntity.ok(profiles);
}
}
참고: 프로젝트에 QueryDSL 종속성을 추가하는 것을 잊지 마십시오.
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<scope>provided</scope>
</dependency>
<build>
<plugins>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/annotations</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
그런 다음 프로젝트를 컴파일합니다(예:mvn compile) 'Q' 클래스를 만들 수 있도록 합니다.
답변은 매우 쉬우며 봄에 예제별 쿼리를 사용할 수 있습니다.
그리고 더 나아가 당신은 모든 것을 나열할 필요가 없습니다.Profile컨트롤러의 속성, 당신은 그냥.Profile매개 변수로서, 봄이 그것을 처리할 것입니다.
그리고 요청 매개 변수의 유효성을 검사하려면 "givenName"을 예로 들어 다음과 같이 보다 쉽게 통합할 수 있습니다.NotNull엔티티에서, 그리고 추가.@Valid컨트롤러에서 "givenName"이 요청 매개 변수에 없는 경우 "Bad Request" 응답을 받게 됩니다.
다음은 작동 코드입니다.
@Entity
@Table(name = "profiles")
public class Profile {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "profile_given_name")
@NotNull
private String givenName;
@Column(name = "profile_surname")
private String surname;
@Column(name = "profile_is_male")
private Integer isMale;
@Column(name = "profile_height_meters", columnDefinition = "DOUBLE")
private BigDecimal heightMeters;
@Column(name = "profile_weight_kilos", columnDefinition = "DOUBLE")
private BigDecimal weightKilos;
@Column(name = "profile_dob")
private Date dob;
}
프로필 리소스
@RestController
@RequestMapping("/v1/profiles")
public class ProfileResource {
@Autowired
ProfileRepository profileRepository;
@GetMapping
public ResponseEntity<List<Profile>> searchProfiles(@Valid Profile profile) {
List<Profile> all = profileRepository.findAll(Example.of(profile));
return ResponseEntity.ok(all);
}
}
프로필 리포지토리
public interface ProfileRepository extends JpaRepository<Profile, Long> {
}
그러면 다음을 보냅니다.GET /v1/profiles?isMale=0원하는 대로 HTTP 방식.
스프링 데이터에서 "예별 쿼리"를 확인하십시오.당신이 필요로 하는 것에 맞는 것 같습니다.
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/ #별로 표시
언급URL : https://stackoverflow.com/questions/48346341/spring-boot-jpa-implementing-search-queries-with-optional-ranged-criteria
'programing' 카테고리의 다른 글
| Oracle Database에서 SQL을 사용하여 XML Clob에서 데이터 추출 (0) | 2023.07.10 |
|---|---|
| ORA-02287: 시퀀스 번호는 여기에서 허용되지 않습니다. (0) | 2023.07.10 |
| 실행력을 높이는 다음과 같습니다.장시간 실행되는 쿼리의 시간 초과? (0) | 2023.07.10 |
| Mongodb: 처음 연결할 때 서버에 연결하지 못했습니다. (0) | 2023.05.11 |
| Git에 추적 중인 파일 목록을 표시하려면 어떻게 해야 합니까? (0) | 2023.05.11 |