programing

조회 방법의 Spring Data 선택적 매개 변수

batch 2023. 9. 3. 12:25
반응형

조회 방법의 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단계: 구현JpaSpecificationExecutorJPA 저장소에 있습니다.

예:

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

반응형