조회 방법의 Spring Data 선택적 매개 변수
저장소 계층에 쿼리 메서드를 몇 가지 작성하려고 합니다.이 메서드는 null 매개 변수를 무시해야 합니다.예:
List<Foo> findByBarAndGoo(Bar barParam, @optional Goo gooParam);
다음 조건에서는 이 메서드가 Foo를 반환해야 합니다.
bar == barParam && goo == gooParam;
gooParam이 null이 아닌 경우. gooParam이 null인 경우 조건이 다음으로 변경됩니다.
bar == barParam;
해결책이 있습니까?누가 나 좀 도와줄래요?
쿼리 정의에 대한 메서드 이름 접근 방식으로는 그렇게 할 수 없을 것 같습니다.설명서(참고 자료)에서 다음을 수행합니다.
메서드 이름에서 파생된 쿼리를 얻는 것이 매우 편리하지만 메서드 이름 구문 분석기가 사용하려는 키워드를 지원하지 않거나 메서드 이름이 불필요하게 못생겨지는 상황에 직면할 수 있습니다.따라서 명명 규칙을 통해 JPA 명명된 쿼리를 사용하거나(자세한 내용은 JPA 명명된 쿼리 사용 참조) @Query를 사용하여 쿼리 방법에 주석을 달 수 있습니다.
여기에 그런 상황이 있다고 생각하기 때문에 아래 답변은 @Query 주석 접근법을 사용하는데, 이는 메소드 이름 접근법(참조)만큼 편리합니다.
@Query("select foo from Foo foo where foo.bar = :bar and "
+ "(:goo is null or foo.goo = :goo)")
public List<Foo> findByBarAndOptionalGoo(
@Param("bar") Bar bar,
@Param("goo") Goo goo);
대답하기에는 너무 늦었습니다.Bar와 Go의 관계에 대해서는 잘 모르겠습니다.예제가 도움이 되는지 확인합니다.
그것은 나에게 효과가 있었다.나도 비슷한 상황이 있는데, 엔티티 사용자는 속성 집합을 가지고 있고 속성(선택 사항)을 기준으로 사용자를 검색하는 findAll 메서드가 있습니다.
예,
Class User{
String firstName;
String lastName;
String id;
}
Class UserService{
// All are optional
List<User> findBy(String firstName, String lastName, String id){
User u = new User();
u.setFirstName(firstName);
u.setLastName(lastName);
u.setId(id);
userRepository.findAll(Example.of(user));
// userRepository is a JpaRepository class
}
}
@chaserb의 답변을 보완하여, 저는 개인적으로 옵션 필터인 의미론을 메서드의 서명에서 명시적으로 만들기 위해 매개 변수를 Java8 옵션 유형으로 추가할 것입니다.
@Query("select foo from Foo foo where foo.bar = :bar and "
+ "(:goo is null or foo.goo = :goo)")
public List<Foo> findByBarAndOptionalGoo(
@Param("bar") Bar bar,
@Param("goo") Optional<Goo> goo);
사용할 수 있습니다.JpaSpecificationExecutor
//import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
1단계: 구현JpaSpecificationExecutor
JPA 저장소에 있습니다.
예:
public interface TicketRepo extends JpaRepository<Ticket, Long>, JpaSpecificationExecutor<Ticket> {
2단계 이제 선택적 매개 변수를 기반으로 티켓을 가져오려면 CriteriaBuilder를 사용하여 사양 쿼리를 작성할 수 있습니다.
예:
public Specification<Ticket> getTicketQuery(Integer domainId, Calendar startDate, Calendar endDate, Integer gameId, Integer drawId) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
predicates.add(criteriaBuilder.equal(root.get("domainId"), domainId));
predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createdAt"), startDate));
predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("createdAt"), endDate));
if (gameId != null) {
predicates.add(criteriaBuilder.equal(root.get("gameId"), gameId));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
3단계: 사양 인스턴스를 jpaRepo에 전달합니다.findAll(사양): 엔티티 개체의 목록을 반환합니다(실행 중인 예제의 티켓)
ticketRepo.findAll(specification); // Pass output of function in step 2 to findAll
이미 많은 훌륭한 답변이 있었지만, 저는 @Pankaj Garg(스프링 사양 API 사용)의 답변을 사용하여 구체적으로 이를 구현했습니다.답변에 몇 가지 사용 사례를 추가합니다.
- null일 수도 있고 아닐 수도 있는 매개 변수 4개.
- 리포지토리의 페이지화된 응답입니다.
- 중첩된 개체의 필드를 기준으로 필터링합니다.
- 특정 필드를 기준으로 정렬합니다.
두. 구체적으로는 먼저몇엔를만듭다니티티의개,다만▁first니. 구체적으로는Ticket
,Movie
그리고.Customer
여기는 화려하지 않습니다.
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.UUID;
@Entity
@Table(name = "ticket", schema = "public")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
public class Ticket implements Serializable {
@Id
@Basic(optional = false)
@NotNull
@Column(name = "id", nullable = false)
private UUID id;
@JoinColumn(name = "movie_id", referencedColumnName = "id", nullable = false)
@ManyToOne(fetch = FetchType.EAGER)
private Movie movie;
@JoinColumn(name = "customer_id", referencedColumnName = "id", nullable = false)
@ManyToOne(fetch = FetchType.EAGER)
private Customer customer;
@Column(name = "booking_date")
@Temporal(TemporalType.TIMESTAMP)
private Date bookingDate;
}
동영상:
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
@Entity
@Table(name = "movie", schema = "public")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
public class Movie implements Serializable {
@Id
@Basic(optional = false)
@NotNull
@Column(name = "id", nullable = false)
private UUID id;
@Basic(optional = false)
@NotNull
@Size(max = 100)
@Column(name = "movie_name", nullable = false, length = 100)
private String movieName;
}
고객:
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
@Entity
@Table(name = "customer", schema = "public")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
public class Customer implements Serializable {
@Id
@Basic(optional = false)
@NotNull
@Column(name = "id", nullable = false)
private UUID id;
@Basic(optional = false)
@NotNull
@Size(max = 100)
@Column(name = "full_name", nullable = false, length = 100)
private String fullName;
}
그런 다음 필터링할 매개 변수에 대한 필드가 있는 클래스를 만듭니다.
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.Date;
import java.util.UUID;
@Data
@AllArgsConstructor
public class TicketFilterParam {
private UUID movieId;
private UUID customerId;
private Date start;
private Date end;
}
다으로클생성다니합를래를 합니다.Specification
필터 매개 변수를 기반으로 합니다.중첩된 개체에 액세스하는 방법과 순서를 쿼리에 추가하는 방법을 기록합니다.
import org.springframework.data.jpa.domain.Specification;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class TicketSpecifications {
public static Specification<Ticket> getFilteredTickets(TicketFilterParam params) {
return (root, criteriaQuery, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
if (params.getMovieId() != null) {
predicates.add(criteriaBuilder.equal(root.get("movie").<UUID> get("id"), params.getMarketerId()));
}
if (params.getCustomerId() != null) {
predicates.add(criteriaBuilder.equal(root.get("customer").<UUID> get("id"), params.getDepotId()));
}
if (params.getStart() != null && params.getEnd() != null) {
predicates.add(criteriaBuilder.between(root.get("bookingDate"), params.getStart(), params.getEnd()));
}
criteriaQuery.orderBy(criteriaBuilder.desc(root.get("bookingDate")));
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
}
다음으로 리포지토리 인터페이스를 정의합니다.이것은 단지 뿐만이 아닙니다.JpaRepository
,하지만 또한JpaSpecificationExecutor
:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
@Repository
public interface TicketRepository extends JpaRepository<Ticket, UUID>, JpaSpecificationExecutor<Ticket> {
}
마지막으로 일부 서비스 클래스에서 다음과 같은 결과를 얻습니다.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
@Service
public class TicketService {
@Autowired
private TicketRepository ticketRepository;
public Page<Ticket> getTickets(TicketFilterParam params, PageRequest pageRequest) {
Specification<Ticket> specification = TicketSpecifications.getFilteredTickets(params);
return ticketRepository.findAll(specification, pageRequest);
}
}
PageRequest
그리고.TicketFilterParam
복원 끝점의 일부 매개 변수 및 값에서 가져올 수 있습니다.
몇 줄로 직접 코딩할 수 있습니다.
List<Foo> findByBarAndOptionalGoo(Bar bar, Goo goo) {
return (goo == null) ? this.findByBar(bar) : this.findByBarAndGoo(bar, goo);
}
그렇지 않으면 Spring-Data에서 이를 즉시 지원하는지 알 수 없습니다.
대답하기에는 너무 늦었지만, 해결책을 찾는 사람들에게 아래와 같은 더 간단한 방법이 있습니다. 저는 같은 문제에 직면했고 마침내 다른 것들보다 매우 간단하고 효율적으로 보이는 이 해결책을 찾을 수 있었습니다.
내 컨트롤러 클래스:
@RestController
@RequestMapping("/order")
public class OrderController {
private final IOrderService service;
public OrderController(IOrderService service) {
this.service = service;
}
@RequestMapping(value = "/{username}/", method = RequestMethod.GET)
public ResponseEntity<ListResponse<UserOrdersResponse>> getUserOrders(
@RequestHeader Map<String, String> requestHeaders,
@RequestParam(required=false) Long id,
@RequestParam(required=false) Long flags,
@RequestParam(required=true) Long offset,
@RequestParam(required=true) Long length) {
// Return successful response
return new ResponseEntity<>(service.getUserOrders(requestDTO), HttpStatus.OK);
}
}
는 시피다, 는나가 있습니다.Username
~하듯이@PathVariable
그리고.length
그리고.offset
내가 필요로 하는 매개변수들이지만, 나는 받아들인다.id
그리고.flags
검색 결과를 필터링하기 위해 REST 서비스를 호출하는 데 필요하지 않은 옵션 매개 변수입니다.
myRepository 인터페이스:
@Query("select new com.ada.bourse.wealth.services.models.response.UserOrdersResponse(FIELDS ARE DELETED TO BECOME MORE READABLE)" +
" from User u join Orders o on u.id = o.user.id where u.userName = :username" +
" and (:orderId is null or o.id = :orderId) and (:flag is null or o.flags = :flag)")
Page<UserOrdersResponse> findUsersOrders(String username, Long orderId, Long flag, Pageable page);
인 주장을 상니다입으로 을 알 수 . 제가 선택적 인수를 확인한 것을 보실 수 있습니다.(:orderId is null or o.id = :orderId)
그리고.(:flag is null or o.flags = :flag)
그리고 저는 제 주장을 확인했다는 것이 강조될 필요가 있다고 생각합니다.is null
조건이 내 열 데이터가 아닙니다. 따라서 클라이언트가 전송하는 경우Id
그리고.flags
매개 변수를 사용하여 결과를 필터링합니다. 그렇지 않으면 그냥 쿼리합니다.username
그것은 나의 것이었습니다.@PathVariable
.
언급URL : https://stackoverflow.com/questions/32728843/spring-data-optional-parameter-in-query-method
'programing' 카테고리의 다른 글
어떻게 하면 최소한의 오버헤드로 간단한 서버리스 p2p 브라우저를 브라우저 메시징에 구현할 수 있습니까? (0) | 2023.09.08 |
---|---|
MySQL 클라이언트에 대한 자동 커밋을 해제하려면 어떻게 해야 합니까? (0) | 2023.09.08 |
ASP.NET 사용자 지정 컨트롤 - 알 수 없는 서버 태그 (0) | 2023.09.03 |
SQL Server 잘라내기 및 8192 제한 (0) | 2023.09.03 |
파워셸에서 프로세스 출력을 비동기식으로 캡처하는 방법은 무엇입니까? (0) | 2023.09.03 |