programing

임의 AND 절이 있는 동적 스프링 데이터 jpa 저장소 쿼리

bestprogram 2023. 10. 29. 19:53

임의 AND 절이 있는 동적 스프링 데이터 jpa 저장소 쿼리

사용하고 있습니다.Spring data jpa repositories, 다른 필드로 검색 기능을 제공해야 합니다.검색 전에 필드를 입력하는 것은 선택사항입니다.저는 5개의 분야가 있습니다.EmployeeNumber,Name,Married,Profession그리고.DateOfBirth.
여기서 나는 사용자별로 주어진 값으로만 질의해야 하며 다른 필드는 무시해야 합니다.

Input : EmployeeNumber: ,Name:St,Married: ,Professsion:IT,DateOfBirth: 
Query : Select * from Employee e where Name like 'St%' and Profession like 'IT%';  

Input : EmployeeNumber:10,Name: ,Married: ,Professsion:IT,DateOfBirth:
Query : Select * from Employee e where EmployeeNumber like '10%' and Profession like 'IT%';  

그래서 여기서는 입력한 값과 쿼리를 고려하고 있습니다.이 경우 Spring data는 본 게시물에 언급된 것과 같이 제한이 있습니다. (확장이 불가능하고 가능한 모든 쿼리를 작성해야 함) 제가 사용하고 있는Querydsl, 하지만 문제는 여전히 존재합니다.null필드는 무시하고 가능한 거의 모든 쿼리를 개발해야 합니다.이 점에서.case 31 queries. 검색 필드가6,7,8...??

선택 필드로 검색 옵션을 구현하는 가장 좋은 방법은 무엇입니까?

Spring-data에서 제공하는 Specifications를 즉시 사용할 수 있으며 기준 API를 사용하여 쿼리를 프로그래밍 방식으로 구축할 수 있습니다.규격을 지원하기 위해 Jpa 규격을 사용하여 저장소 인터페이스를 확장할 수 있습니다.실행기 인터페이스

public interface CustomerRepository extends SimpleJpaRepository<T, ID>, JpaSpecificationExecutor {

}

부가 인터페이스 (Jpa 규격)실행자)는 다양한 방법으로 사양을 실행할 수 있는 메서드를 제공합니다.

예를 들어 findAll 메서드는 규격과 일치하는 모든 엔터티를 반환합니다.

List<T> findAll(Specification<T> spec);

사양 인터페이스는 다음과 같습니다.

public interface Specification<T> {
     Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
            CriteriaBuilder builder);
}

좋아요, 그럼 전형적인 사용 사례는 무엇인가요?규격을 쉽게 사용하여 엔티티 위에 확장 가능한 술어 집합을 구축할 수 있으며, 이를 통해 필요한 모든 조합에 대한 쿼리(메소드)를 선언할 필요 없이 JpaRepository와 결합하여 사용할 수 있습니다.여기 예가 있습니다: 예 2.15.고객사양

public class CustomerSpecs {
    public static Specification<Customer> isLongTermCustomer() {
        return new Specification<Customer>() {
            public Predicate toPredicate(
                Root<Customer> root, CriteriaQuery<?> query,
                CriteriaBuilder builder) {
                LocalDate date = new LocalDate().minusYears(2);
                return builder.lessThan(root.get('dateField'), date);
            }
        };
    }

    public static Specification<Customer> hasSalesOfMoreThan(MontaryAmount value) {
        return new Specification<Customer>() {
            public Predicate toPredicate(
                Root<T> root, CriteriaQuery<?> query,
                CriteriaBuilder builder) {
                // build query here
            }
        };
    }
}

비즈니스 요구사항 추상화 수준에 대한 몇 가지 기준을 표현하고 실행 가능한 사양을 만들었습니다.따라서 클라이언트는 다음과 같이 규격을 사용할 수 있습니다.

List customers = customerRepository.findAll(isLongTermCustomer());

사양 예제 2.17을 결합할 수도 있습니다.조합사양

    MonetaryAmount amount = new MonetaryAmount(200.0, Currencies.DOLLAR);
    List<Customer> customers = customerRepository.findAll(
        where(isLongTermCustomer()).or(hasSalesOfMoreThan(amount)));

보다시피 사양은 사양을 체인으로 연결하고 결합하는 몇 가지 글루 코드 방식을 제공합니다.따라서 데이터 액세스 계층을 확장하는 것은 새로운 Specification 구현을 생성하고 이미 존재하는 구현과 결합하는 문제일 뿐입니다.

복잡한 사양을 생성할 수 있습니다. 여기 예가 있습니다.

public class WorkInProgressSpecification {
    public static Specification<WorkInProgress> findByCriteria(final SearchCriteria searchCriteria) {

        return new Specification<WorkInProgress>() {

            @Override
            public Predicate toPredicate(
                Root<WorkInProgress> root,
                CriteriaQuery<?> query, CriteriaBuilder cb) {

                List<Predicate> predicates = new ArrayList<Predicate>();

                if (searchCriteria.getView() != null && !searchCriteria.getView().isEmpty()) {
                    predicates.add(cb.equal(root.get("viewType"), searchCriteria.getView()));
                }
                if (searchCriteria.getFeature() != null && !searchCriteria.getFeature().isEmpty()) {
                    predicates.add(cb.equal(root.get("title"), searchCriteria.getFeature()));
                }
                if (searchCriteria.getEpic() != null && !searchCriteria.getEpic().isEmpty()) {
                    predicates.add(cb.equal(root.get("epic"), searchCriteria.getEpic()));
                }
                if (searchCriteria.getPerformingGroup() != null && !searchCriteria.getPerformingGroup().isEmpty()) {
                    predicates.add(cb.equal(root.get("performingGroup"), searchCriteria.getPerformingGroup()));
                }
                if (searchCriteria.getPlannedStartDate() != null) {
                    System.out.println("searchCriteria.getPlannedStartDate():" + searchCriteria.getPlannedStartDate());
                    predicates.add(cb.greaterThanOrEqualTo(root.<Date>get("plndStartDate"), searchCriteria.getPlannedStartDate()));
                }
                if (searchCriteria.getPlannedCompletionDate() != null) {
                    predicates.add(cb.lessThanOrEqualTo(root.<Date>get("plndComplDate"), searchCriteria.getPlannedCompletionDate()));
                }
                if (searchCriteria.getTeam() != null && !searchCriteria.getTeam().isEmpty()) {
                    predicates.add(cb.equal(root.get("agileTeam"), searchCriteria.getTeam()));
                }

                return cb.and(predicates.toArray(new Predicate[] {}));
            }
        };
    }
}

여기 JPA Respositorys 문서가 있습니다.

QueryDSL(4.x) 및 querydsl-jpa의 새 주 버전을 사용하려면 변경해야 합니다.


우리의 프로젝트 중 하나에서, 우리는QueryDSL와 함께QueryDslPredicateExecutor<T>.

  public Predicate createPredicate(DataEntity dataEntity) {
    QDataEntity qDataEntity = QDataEntity.dataEntity;
    BooleanBuilder booleanBuilder = new BooleanBuilder();
    if (!StringUtils.isEmpty(dataEntity.getCnsiConsumerNo())) {
      booleanBuilder
        .or(qDataEntity.cnsiConsumerNo.contains(dataEntity.getCnsiConsumerNo()));
    }
    if (!StringUtils.isEmpty(dataEntity.getCnsiMeterNo())) {
      booleanBuilder.or(qDataEntity.cnsiMeterNo.contains(dataEntity.getCnsiMeterNo()));
    }

    return booleanBuilder.getValue();
  }

저장소에서 이를 사용할 수 있습니다.

@Repository
public interface DataEntityRepository
  extends DaoRepository<DataEntity, Long> {

어디에DaoRepository

@NoRepositoryBean
public interface DaoRepository<T, K extends Serializable>
  extends JpaRepository<T, K>,
  QueryDslPredicateExecutor<T> {
}

그러면 리포지토리 술어 메서드를 사용할 수 있기 때문입니다.

Iterable<DataEntity> results = dataEntityRepository.findAll(dataEntityPredicateCreator.createPredicate(dataEntity));

갖기 위해QClasses, pom.xml에 QueryDSL APT Maven 플러그인을 지정해야 합니다.

  <build>
    <plugins>
      <plugin>
        <groupId>com.mysema.maven</groupId>
        <artifactId>maven-apt-plugin</artifactId>
        <version>1.0.4</version>
        <executions>
          <execution>
            <phase>generate-sources</phase>
            <goals>
              <goal>process</goal>
            </goals>
            <configuration>
              <outputDirectory>target/generated-sources</outputDirectory>
              <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
            </configuration>
          </execution>
        </executions>
      </plugin>

종속성은

    <!-- querydsl -->
    <dependency>
        <groupId>com.mysema.querydsl</groupId>
        <artifactId>querydsl-core</artifactId>
        <version>${querydsl.version}</version>
    </dependency>
    <dependency>
        <groupId>com.mysema.querydsl</groupId>
        <artifactId>querydsl-apt</artifactId>
        <version>${querydsl.version}</version>
    </dependency>
    <dependency>
        <groupId>com.mysema.querydsl</groupId>
        <artifactId>querydsl-jpa</artifactId>
        <version>${querydsl.version}</version>
    </dependency>

또는 그라들의 경우:

sourceSets {
    generated
}
sourceSets.generated.java.srcDirs = ['src/main/generated']
configurations {
    querydslapt
}
dependencies {
    // other deps ....
    compile "com.mysema.querydsl:querydsl-jpa:3.6.3"
    compile "com.mysema.querydsl:querydsl-apt:3.6.3:jpa"
}
task generateQueryDSL(type: JavaCompile, group: 'build', description: 'Generates the QueryDSL query types') {
    source = sourceSets.main.java
    classpath = configurations.compile + configurations.querydslapt
    options.compilerArgs = [
            "-proc:only",
            "-processor", "com.mysema.query.apt.jpa.JPAAnnotationProcessor"
    ]
    destinationDir = sourceSets.generated.java.srcDirs.iterator().next()
}

compileJava {
    dependsOn generateQueryDSL
    source generateQueryDSL.destinationDir
}

compileGeneratedJava {
    dependsOn generateQueryDSL
    classpath += sourceSets.main.runtimeClasspath
}

Spring Data JPA 1.10부터는 예제별 쿼리라는 다른 옵션이 있습니다.저장소는 다음과 별도로 구현해야 합니다.JpaRepository또한 쿼리 기준예시다음과 같은 메서드를 얻을 수 있는 실행자 인터페이스:

<S extends T> Iterable<S> findAll(Example<S> example)

그런 다음 다음 검색할 예제를 만듭니다.

Employee e = new Employee();
e.setEmployeeNumber(getEmployeeNumberSomewherFrom());
e.setName(getNameSomewhereFrom());
e.setMarried(getMarriedSomewhereFrom());
e.setProfession(getProfessionSomewhereFrom());
e.setDateOfBirth(getDateOfBirthSomewhereFrom());

그 다음:

employeeRepository.findAll(Example.of(e));

일부 매개 변수가 null일 경우 WHERE 절에 포함되지 않으므로 동적 쿼리를 얻을 수 있습니다.

문자열 특성의 일치를 세분화하려면 의 를 참조하십시오.

ExampleMatcher사건에 민감하지 않은 사람이like는 예를 들어 다음과 같습니다.

ExampleMatcher matcher = ExampleMatcher.matching().
          withMatcher("profession", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.CONTAINING).ignoreCase());

QBE 예제: https://github.com/spring-projects/spring-data-examples/tree/master/jpa/query-by-example

게임에 좀 늦었지만 답이 너무 복잡해서...만약 당신이 당신의 개체의 필드를 바꾸면 어떻게 됩니까?다른 엔티티에 대한 검색을 지원하려면 어떻게 해야 합니까?

그냥 이 라이브러리를 사용해도 됩니다: https://github.com/turkraft/spring-filter

다음과 같은 검색 쿼리를 실행할 수 있습니다.

/search?filter=평균(ratings)> 4.5 및 brand.name in ('audi', '랜드로버') 및 (2018년 또는 km < 50000)에서 색상: '흰색' 및 사고가 비어 있습니다.

Spring's Pageable과 함께 다음 페이지를 호출할 수 있습니다.&page=11&size=20

언급URL : https://stackoverflow.com/questions/28874135/dynamic-spring-data-jpa-repository-query-with-arbitrary-and-clauses