© 2008-2015 The original authors.

이 문서의 복사본은 당신 자신을 위해서나, 다른 사람들에게 배포를 위해 만들어질 수 있습니다. 복사에 대해 어떤 비용을 청구하지 않아야 하며, 프린트되거나 전자적으로 배포될 때에도 Copyright 문구를 포함해야 합니다.
Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.
목차

서문

1.프로젝트 메타데이터

2. 의존성들

스프링 데이터 모듈들의 각자 다른 시작으로 인하여, 모듈들의 대부분은 각자 다른 메이저, 마이너 버젼 넘버를 가지고 있습니다. 가장 쉬운 호환성이 맞는 것을 찾는 방법은 Spring Data Release Train BOM에서 저희가 정의한 호환성 버젼을 찾는 것입니다. 메이븐 프로젝트에서 당신은 이 의존성 <dependencyManagement />섹션을 당신의 폼 부분에 선언할 수 있습니다.

Due to different inception dates of individual Spring Data modules, most of them carry different major and minor version numbers. The easiest way to find compatible ones is by relying on the Spring Data Release Train BOM we ship with the compatible versions defined. In a Maven project you’d declare this dependency in the <dependencyManagement /> section of your POM:

예제 1. Spring Data release train BOM 사용하기
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-releasetrain</artifactId>
      <version>${release-train}</version>
      <scope>import</scope>
      <type>pom</type>
    </dependency>
  <dependencies>
</dependencyManagement>

현재 릴리즈 트레인 버젼은 Fowler-BUILD-SNAPSHOT입니다. 트레인 이름은 알파벳 오름차순이며 현재 가능한 버젼은 여기에 나와있습니다. 버젼이름은 다음 패턴을 따르고-${name}-${release} 릴리즈는 다음의 하나를 따르게 됩니다:

The current release train version is Fowler-BUILD-SNAPSHOT. The train names are ascending alphabetically and currently available ones are listed here. The version name follows the following pattern: ${name}-${release} where release can be one of the following:

  • BUILD-SNAPSHOT - 현재 스냅샷

    BUILD-SNAPSHOT - current snapshots

  • M1, M2 etc. - 마일스톤

    M1, M2 etc. - milestones

  • RC1, RC2 etc. - 릴리즈 후보

    RC1, RC2 etc. - release candidates

  • RELEASE - GA 릴리즈

    RELEASE - GA release

  • SR1, SR2 etc. - 서비스 릴리즈

    SR1, SR2 etc. - service releases

Spring Data examples repository에서 BOM을 사용하는 작업 예제를 발견할 수가 있습니다. .

A working example of using the BOMs can be found in our Spring Data examples repository.

저 자리의 <dependencies />블록에 버젼을 사용하지 않고 스프링 데이터 모듈을 선언했습니다. (번역하면서 애매했던 부분임. )

If that’s in place declare the Spring Data modules you’d like to use without a version in the <dependencies /> block.

예제 2. 스프링 데이터 모듈 의존성 선언
Example 2. Declaring a dependency to a Spring Data module
<dependencies>
  <dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
  </dependency>
<dependencies>

2.1. 스프링 부트에서의 의존성 관리
2.1. Dependency management with Spring Boot

스프링 부트는 이미 매우 최근의 스프링 데이터 모듈을 선택했습니다. 그럼에도 불구하고 새로운 버젼을 사용하고 싶은 경우를 위하여 간단히 spring-data-releasetrain.version 프로퍼티를, trainname 과 당신이 사용하고자 하는 iteration에 설정해주세요.

Spring Boot already selects a very recent version of Spring Data modules for you. In case you want to upgrade to a newer version nonetheless, simply configure the property spring-data-releasetrain.version to the train name and iteration you’d like to use.

2.2. 스프링 프레임 워크
2.2. Spring Framework

현재 스프링 데이터 모듈의 버젼은 스프링 프레임워크 4.0.9 릴리즈 이상을 필요로 합니다. 저 마이너 버젼보다 좀 더 오래된 버그픽스 버젼과 함께 작업될 수 있을 것이지만, 최근의 버젼을 사용하는 것을 강력히 추천드립니다.

The current version of Spring Data modules require Spring Framework in version 4.0.9.RELEASE or better. The modules might also work with an older bugfix version of that minor version. However, using the most recent version within that generation is highly recommended.

3. 스프링 데이터 리파지토리들과 작업하기
3. Working with Spring Data Repositories

스프링 데이터 레파지토리 추상화의 목표는, 다양한 영속화 저장소를 위한 데이터 액세스 레이어를 구현하는 데 있어 필요한, 상당한양의 보일러 플레이한 코드들을 줄이는 데 있습니다

The goal of Spring Data repository abstraction is to significantly reduce the amount of boilerplate code required to implement data access layers for various persistence stores.

스프링 데이터 리파지토리 문서와 당신의 모듈

Spring Data repository documentation and your module

이 챕터는 스프링 데이터 리파지토리들의 인터페이스와 핵심개념을 설명합니다. 이 챕터의 정보는 스프링데이터 공통모듈에서 나왔으며, JPA모듈을 위한 설정과 코드 샘플을 사용해봅니다. XML 네임스페이스 선언과 당신이 사용할 특별한 모듈들같은 확장되는 타입들에 적응해봅시다. (의역) Namespace reference 는 repository API를 지원하는 모든 스프링데이터모듈쪽에서 XML 설정을 다루며, Repository query keywords는 일반적으로 레파지토리추상화에 의해 지원되는 쿼리 메소드 키워드들을 다룹니다. 당신 모듈의 특별한 특징에 관한 정보를 찾는다면, 이 문서에서 그 모듈에 대한 챕터를 살펴보세요.

This chapter explains the core concepts and interfaces of Spring Data repositories. The information in this chapter is pulled from the Spring Data Commons module. It uses the configuration and code samples for the Java Persistence API (JPA) module. Adapt the XML namespace declaration and the types to be extended to the equivalents of the particular module that you are using. Namespace reference covers XML configuration which is supported across all Spring Data modules supporting the repository API, Repository query keywords covers the query method keywords supported by the repository abstraction in general. For detailed information on the specific features of your module, consult the chapter on that module of this document.

3.1. 핵심 개념
3.1. Core concepts

스프링 데이터 리파지토리 추상화에서 중심적인 인터페이스는 Repository(아마 놀라시않으실테지만)입니다.이것은 도메인클래스와 도메인의 id 타입을 타입아규먼트로 받습니다. 이 인터페이스는 주로 마커 인터페이스로 동작하며, 작업할 타입을 가지고 있으면서, 당신이 이것을 확장할 인터페이스를 발견하게 해줍니다. CrudRepository 는 관리되는 엔티티클래스에서 복잡한 CRUD기능을 제공해줍니다.

The central interface in Spring Data repository abstraction is Repository (probably not that much of a surprise). It takes the domain class to manage as well as the id type of the domain class as type arguments. This interface acts primarily as a marker interface to capture the types to work with and to help you to discover interfaces that extend this one. The CrudRepository provides sophisticated CRUD functionality for the entity class that is being managed.

예제 3. CrudRepository interface
Example 3. CrudRepository interface
public interface CrudRepository<T, ID extends Serializable>
    extends Repository<T, ID> {

    <S extends T> S save(S entity); (1)

    T findOne(ID primaryKey);       (2)

    Iterable<T> findAll();          (3)

    Long count();                   (4)

    void delete(T entity);          (5)

    boolean exists(ID primaryKey);  (6)

    // … more functionality omitted.
}
1 주어진 엔티티를 저장합니다.
2 주어진 아이디로 식별된 엔티티를 반환합니다.
3 모든 엔티티를 반환합니다.
4 엔티티의 숫자를 반환합니다.
5 주어진 엔티티를 삭제합니다.
6 주어진 아이디로 엔티티가 존재하는지를 반환합니다.
1 Saves the given entity.
2 Returns the entity identified by the given id.
3 Returns all entities.
4 Returns the number of entities.
5 Deletes the given entity.
6 Indicates whether an entity with the given id exists.
우리는 또한 JpaRepositoryMongoRepository같은 기술특징적인 추상화를 제공합니다. 이러한 인터페이스는 CrudRepository 를 확장하여 일반적인 기능에 좀 더 해당기술에 해당하는 기술을 사용할 수 있게 해줍니다(의역. 중요한 부분이 아니니 시간소비없이..=3=3)
We also provide persistence technology-specific abstractions like e.g. JpaRepository or MongoRepository. Those interfaces extend CrudRepository and expose the capabilities of the underlying persistence technology in addition to the rather generic persistence technology-agnostic interfaces like e.g. CrudRepository.

CrudRepository를 확장한 것 중에 PagingAndSortingRepository 는 쉽게 엔티티들에 대해 페이징을 할수있는 메소드를 제공합니다.

On top of the CrudRepository there is a PagingAndSortingRepository abstraction that adds additional methods to ease paginated access to entities:

예제 4. PagingAndSortingRepository
Example 4. PagingAndSortingRepository
public interface PagingAndSortingRepository<T, ID extends Serializable>
  extends CrudRepository<T, ID> {

  Iterable<T> findAll(Sort sort);

  Page<T> findAll(Pageable pageable);
}

User의 페이지사이즈를 20으로 잡고 두번째 페이지에 접근하는 것은간단하게 이렇게 해볼수 있습니다.:

Accessing the second page of User by a page size of 20 you could simply do something like this:

PagingAndSortingRepository<User, Long> repository = // … get access to a bean
Page<User> users = repository.findAll(new PageRequest(1, 20));

쿼리메소드들을 더 말해보자면, 쿼리 카운트나 삭제 만들기가 가능합니다.

In addition to query methods, query derivation for both count and delete queries, is available.

예제 5. 카운트 쿼리
Example 5. Derived Count Query
public interface UserRepository extends CrudRepository<User, Long> {

  Long countByLastname(String lastname);
}
예제 6. 삭제 쿼리
Example 6. Derived Delete Query
public interface UserRepository extends CrudRepository<User, Long> {

  Long deleteByLastname(String lastname);

  List<User> removeByLastname(String lastname);

}

3.2. Query 메소드들
3.2. Query methods

표준 CRUD기능들을 가진 리파지토들은 데이터소스에 대한 쿼리들을 가지고 있습니다. 스프링데이터와 함께 이러한 쿼리들을 4단계로 만들어봅시다.

Standard CRUD functionality repositories usually have queries on the underlying datastore. With Spring Data, declaring those queries becomes a four-step process:

  1. Repository나 그 하위 인터페이스를 상속하는 인터페이스를 상속하여, 도메인클래스와 ID 타입을 적어줍시다.

    Declare an interface extending Repository or one of its subinterfaces and type it to the domain class and ID type that it will handle.

    interface PersonRepository extends Repository<User, Long> { … }
  2. 그 인터페이스에 대해서 쿼리메소드를 선언합니다.
    Declare query methods on the interface.
    interface PersonRepository extends Repository<User, Long> {
      List<Person> findByLastname(String lastname);
    }
  3. 스프링을 설정하여 이러한 인터페이스들을 위한 프록시 인스턴스를 생성하게 해줍니다. Set up Spring to create proxy instances for those interfaces. Either via 자바 설정 :

    이나, XML을 통해 가능합니다.

    Set up Spring to create proxy instances for those interfaces. Either via JavaConfig:

    import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
    
    @EnableJpaRepositories
    class Config {}
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/data/jpa
         http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
    
       <jpa:repositories base-package="com.acme.repositories"/>
    
    </beans>

    JPA 네임스페이스는 이 예제에서 사용되었습니다. 만약 당신이 다른 데이터 저장소를 위한 추상화 리파지토리를 사용한다면, 이것을 위해 적적한 네임스페이스 선언으로 바꿔줘야할 것입니다. 예를 들자면 몽고디비나 JPA같이 말이죠 (생략의역)

    The JPA namespace is used in this example. If you are using the repository abstraction for any other store, you need to change this to the appropriate namespace declaration of your store module which should be exchanging jpa in favor of, for example, mongodb.

또한, 자바설정 방법은 기본적으로 사용되는 어노테이션된 클래스를 깔끔하게 설정을 해주지않으므로 패키지를 스캔하기 위하여 다음과 같은 설정을 적어주게 됩니다. basePackage…, @Enable…-어노테이션.

Also, note that the JavaConfig variant doesn’t configure a package explictly as the package of the annotated class is used by default. To customize the package to scan use one of the basePackage… attribute of the data-store specific repository @Enable…-annotation.

  1. 리파지토리 인스턴스를 주입시키고 사용하세요

    Get the repository instance injected and use it.

    public class SomeClient {
    
      @Autowired
      private PersonRepository repository;
    
      public void doSomething() {
        List<Person> persons = repository.findByLastname("Matthews");
      }
    }
다음의 섹션이 각각의 단계를 세부적으로 설명할 것입니다.
The sections that follow explain each step in detail.

3.3. 리파지토리 인터페이스 정의하기
3.3. Defining repository interfaces

첫번째 단계로, 당신은 도메인특정 리파지토리 인터페이스를 정의해야 합니다. 이 인터페이스는 반드시 Repository 를 상속해야 하며, 도메인 클래스와 아이디 타입을 적어줘야 합니다. 만약 당신이 CRUD메소드를 사용하기 원한다면, Repository대신에 CrudRepository 를 사용해줍니다.

As a first step you define a domain class-specific repository interface. The interface must extend Repository and be typed to the domain class and an ID type. If you want to expose CRUD methods for that domain type, extend CrudRepository instead of Repository.

3.3.1. 좀 더 세밀한 리파지토리 정의 조정
3.3.1. Fine-tuning repository definition

일반적으로, 당신의 리파지토리 인터페이스는 Repository, CrudRepository or PagingAndSortingRepository같은 것을 상속할 것입니다. 또 다르게는, 만약 당신이 스프링 데이터 인터페이스를 상속하기를 원하지 않는다면 당신은 당신의 리파지토리에 @RepositoryDefinition를 붙일 수 있습니다(역주: 예제에서의 @NoRepositoryBean를 말하는 것인듯하다.). CrudRepository를 상속하는 것은 당신의 엔티티를 다루는 메소드들의 집합을 노출시킵니다. 만약 당신이 노출되는 메소드들에 관해서 선택적으로 하고 싶다면, 단순히 CrudRepository에서 당신이 노출시키는 부분을 복사하여 당신의 도메인 리파지토리에 붙여보셔요(^^)

Typically, your repository interface will extend Repository, CrudRepository or PagingAndSortingRepository. Alternatively, if you do not want to extend Spring Data interfaces, you can also annotate your repository interface with @RepositoryDefinition. Extending CrudRepository exposes a complete set of methods to manipulate your entities. If you prefer to be selective about the methods being exposed, simply copy the ones you want to expose from CrudRepository into your domain repository.

이것은 제공된 스프링데이터 리파지토리에서, 당신 자신의 추상화를 정의할 수 있게 합니다.
This allows you to define your own abstractions on top of the provided Spring Data Repositories functionality.
Example 7. 선택적으로 CRUD 메소드 노출시키기
Example 7. Selectively exposing CRUD methods
@NoRepositoryBean
interface MyBaseRepository<T, ID extends Serializable> extends Repository<T, ID> {

  T findOne(ID id);

  T save(T entity);
}

interface UserRepository extends MyBaseRepository<User, Long> {
  User findByEmailAddress(EmailAddress emailAddress);
}

이 첫번째 단계에서 당신은 모든 도메인 리파지토리들을 위한 기본 작업 인터페이스를 정의하였고, findOne(…)save(…)를 노출시켰습니다. 이러한 메소드들은 리파지토리 구현 구현체에 위치하게 될 것입니다. 당신이 스프링데이터에서 선택한 저장소. 예를 들자면 JPA이면 SimpleJpaRepository같은 것에 위치할 것입니다. 왜냐하면 이것들(역주:구현체?)은 CrudRepository의 메소드 시그니처를 매칭시키기 때문입니다. 그러므로 UserRepository는 사용자들을 저장하고 id로 단일유저를 찾을 수 있고, 뿐만 아니라 그들의 이메일 주소로 Users들을 찾는 쿼리를 동작시킬 것입니다.

In this first step you defined a common base interface for all your domain repositories and exposed findOne(…) as well as save(…).These methods will be routed into the base repository implementation of the store of your choice provided by Spring Data ,e.g. in the case if JPA SimpleJpaRepository, because they are matching the method signatures in CrudRepository. So the UserRepository will now be able to save users, and find single ones by id, as well as triggering a query to find Users by their email address.

@NoRepositoryBean 중간 레파지토리 인터페이스를 한번 보세요. 당신이 저 어노테이션을 붙인 모든 리파지토리는 런타임에서 인스턴스를 생성하지 않습니다.
Note, that the intermediate repository interface is annotated with @NoRepositoryBean. Make sure you add that annotation to all repository interfaces that Spring Data should not create instances for at runtime.

3.4. 쿼리 메소드 정의하기

리파지토리 프록시는 메소드 이름으로 저장소에 맞는 쿼리를 만들어내는 두가지 방법이 있습니다. 하나는 메소드이름으로 직접적으로 쿼리를 만들어내는 것이고, 아니면 수동적으로 정의된 쿼리를 사용하는 것입니다. 저장소에 따라서 옵션이 더 있을 수도 있습니다. 실제 쿼리를 생성하는 것을 결정할 전략이 있을 수 있습니다. 사용가능한 옵션을 살펴볼까요?

The repository proxy has two ways to derive a store-specific query from the method name. It can derive the query from the method name directly, or by using a manually defined query. Available options depend on the actual store. However, there’s got to be a strategy that decides what actual query is created. Let’s have a look at the available options.

3.4.1. 쿼리 탐색(lookup) 전략

다음 전략에서 리파지토리 인프라스트럭쳐가 쿼리를 해석하는 것이 가능합니다. XML 설정의 네임스페이스에서 query-lookup-strategy속성을 통하거나, 자바설정에서 Enable${store}Repositories어노테이션이 있는 곳에서 queryLookupStrategy속성을 통해 전략을 설정할 수 있습니다. 몇몇 전략은 특정 데이터베이스에서 지원되지 않습니다 .

The following strategies are available for the repository infrastructure to resolve the query. You can configure the strategy at the namespace through the query-lookup-strategy attribute in case of XML configuration or via the queryLookupStrategy attribute of the Enable${store}Repositories annotation in case of Java config. Some strategies may not be supported for particular datastores.

  • CREATE 는 저장소에 맞는 쿼리를 쿼리메소드이름으로 부터 만들어내는 것을 시도합니다. 일반적인 접근은 메소드이름으로부터 잘 알려진 접두어를 제거하고 나머지 부분을 파싱하는 것입니다. 쿼리생성에서 이 부분에서 좀 더 읽어보세요.Query creation
    CREATE attempts to construct a store-specific query from the query method name. The general approach is to remove a given set of well-known prefixes from the method name and parse the rest of the method. Read more about query construction in Query creation.

  • USE_DECLARED_QUERY 는 선언된 쿼리를 찾는 것을 시도하고, 찾을 수 없을 때는 예외를 날립니다. 쿼리는 어노테이션이나 다른 방법으로 선언된 방식에 의해 정의될 수 있습니다. 특정 데이터 저장소의 문서를 참고하셔서 그 저장소에서 사용가능한 옵션들을 찾아보세요. 만약 리파지토리 인프라스트럭처가 부트스트랩 시간에 그 메소드를 위해 정의된 쿼리를 찾을 수 없으면 이것은 실패합니다.

    USE_DECLARED_QUERY tries to find a declared query and will throw an exception in case it can’t find one. The query can be defined by an annotation somewhere or declared by other means. Consult the documentation of the specific store to find available options for that store. If the repository infrastructure does not find a declared query for the method at bootstrap time, it fails.

  • CREATE_IF_NOT_FOUND (default) CREATEUSE_DECLARED_QUERY를 조합합니다. 이것은 처음에 정의된 쿼리를 찾아보고 발견되지 않으면, 커스텀 메소드네임 기반의 쿼리를 생성합니다. 이것은 기본 탐색전략이고, 당신이 아무것도 명시적으로 설정하지 않으면 기본 설정이 될 것입니다. 이것은 메소드이름으로 빠른 쿼리 정의를 가능케 하며, 필요에 따라 선언된 쿼리를 소개하면서 이러한 쿼리들의 커스텀 튜닝또한 가능하게 합니다.

    CREATE_IF_NOT_FOUND (default) combines CREATE and USE_DECLARED_QUERY. It looks up a declared query first, and if no declared query is found, it creates a custom method name-based query. This is the default lookup strategy and thus will be used if you do not configure anything explicitly. It allows quick query definition by method names but also custom-tuning of these queries by introducing declared queries as needed.

3.4.2. 쿼리 생성
3.4.2. Query creation

쿼리 빌더 메커니즘은 스프링 데이터 리파지토리 인프라스트럭쳐로 짜여져, 리파지토리의 엔티티들에 맞는 쿼리들을 만들어내는 데 유용합니다. 이 메커니즘은 find…By, read…By, query…By, count…By, 와 get…By같은 접두어들을 메소드에서 떼어내고 나머지부분을 파싱하기 시작합니다. 다음 소개하는 절은 Distinct(쿼리가 생성될지 결정하는 flag를 설정)같은 더 깊은 표현을 포함하고 있습니다. 그러나 처음의 By는 실제 크리테리아의 시작을 가리키는 구별자로 동작합니다. 기본레벨에서 당신은 엔티티의 속성을 결정할 조건을 정의할 수 있으며, 그것들을 AndOr로 연결할 수 있습니다.

The query builder mechanism built into Spring Data repository infrastructure is useful for building constraining queries over entities of the repository. The mechanism strips the prefixes find…By, read…By, query…By, count…By, and get…By from the method and starts parsing the rest of it. The introducing clause can contain further expressions such as a Distinct to set a distinct flag on the query to be created. However, the first By acts as delimiter to indicate the start of the actual criteria. At a very basic level you can define conditions on entity properties and concatenate them with And and Or.

Example 8.메소드네임으로 쿼리 생성하기
Example 8. Query creation from method names
public interface PersonRepository extends Repository<User, Long> {

  List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);

  // Enables the distinct flag for the query
  List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
  List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);

  // Enabling ignoring case for an individual property
  List<Person> findByLastnameIgnoreCase(String lastname);
  // Enabling ignoring case for all suitable properties
  List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);

  // Enabling static ORDER BY for a query
  List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
  List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}

메소드에 파싱된 실제 결과는 쿼리를 생성하는 데이터베이스에 따라 다르지만, 생각해볼만한 일반적인 것들이 있습니다.

The actual result of parsing the method depends on the persistence store for which you create the query. However, there are some general things to notice.

  • 이 표현은 보통 속성을 순회하며(영어 오타인듯?) 연결될 수 있는 연산자와 함께 조합됩니다. ANDOR같은 표현과 함께 속성을 조합할 수 있습니다. 당신은 또한 Between, LessThan, GreaterThan, Like같은 연산자의 지원을 받을 수 있습니다. 이러한 지원되는 연산자는 데이터스토어에 따라 다를 수 있으니, 해당 레퍼런스 문서에 적절한 부분을 참고해보세요.

    The expressions are usually property traversals combined with operators that can be concatenated. You can combine property expressions with AND and OR. You also get support for operators such as Between, LessThan, GreaterThan, Like for the property expressions. The supported operators can vary by datastore, so consult the appropriate part of your reference documentation.

  • 메소드파서는 code>IgnoreCase에 대한 독립적인 flag 속성설정을 지원하며 예를 들자면 findByLastnameIgnoreCase(…)같은 것이 되거나 findByLastnameAndFirstnameAllIgnoreCase(…)같이 모든 String인스턴스에서도 지원을 할 수 있습니다. 이 부분도 저장소에 따라 다를 수 있으니 해당 저장소 레퍼런스 문서를 참조하세요

    The method parser supports setting an IgnoreCase flag for individual properties (for example, findByLastnameIgnoreCase(…)) or for all properties of a type that support ignoring case (usually String instances, for example, findByLastnameAndFirstnameAllIgnoreCase(…)). Whether ignoring cases is supported may vary by store, so consult the relevant sections in the reference documentation for the store-specific query method.

  • 당신은 정적 순서를 OrderBy절을 추가하여 쿼리메소드의 정렬 방향을 정할 수 있습니다. AscDesc 로 말이죠.. 동적 정렬을 지원하는 쿼리를 만들기 위해 특별 파라미터 핸들링를 보시기 바랍니다.

    You can apply static ordering by appending an OrderBy clause to the query method that references a property and by providing a sorting direction (Asc or Desc). To create a query method that supports dynamic sorting, see Special parameter handling.

3.4.3. 속성 표현

속성표현은 이전 예제에서 나타난대로, 관리되는 엔티티의 직접적인 속성을 참조할 수 있습니다. 쿼리 생성 시간에 당신은 이미 파싱된 속성이 관리되는 도메인 클래스의 일부라는 것을 확실하게 하고 가야 합니다. 그러나 당신은 중첩속성에 대한 제약을 정의할 수 있습니다. PersonZipCode와 함께 있는 Address를 가지고 있다고 생각해봅시다. 이러한 경우 메소드 네임은

Property expressions can refer only to a direct property of the managed entity, as shown in the preceding example. At query creation time you already make sure that the parsed property is a property of the managed domain class. However, you can also define constraints by traversing nested properties. Assume a Person has an Address with a ZipCode. In that case a method name of

List<Person> findByAddressZipCode(ZipCode zipCode);

이렇게 되며, x.address.zipCode 이라는 속성에 접근하게 됩니다. 이렇게 해석하는 알고리즘은 전체 부분(AddressZipCode)을 속성으로 인터프리팅하면서 시작되어 속성에 맞는 도메인 클래스를 검사합니다. 만약 알고리즘이 성공하면, 이것은 저 속성을 사용할 것이고. 그렇지 않다면 알고리즘은 저 부분을 Camel case 부분의 오른쪽부터 왼쪽으로 그리고 꼬리부분으로 나눠보며, 맞는 속성을 찾으려 할 것입니다. 우리의 예제에서는 AddressZipCode일 것입니다. 만약 알고리즘이 앞에서 맞는 속성을 찾아내면, 거기서부터 뒷부분을 가지고서 트리다운을 만들기 시작합니다. (역주: 그 뒷부부분으로 변수를 만든다는 뜻인듯^^;) 이전에 설명한 방법대로 뒷부분을 잘라보면서 말입니다. 만약 처음 나누기가 매칭이 되지 않으면 알고리즘은 나누는 지점을 왼쪽으로 이동해보게 됩니다. (Address, ZipCode) 그리고 다시 시작합니다.

creates the property traversal x.address.zipCode. The resolution algorithm starts with interpreting the entire part (AddressZipCode) as the property and checks the domain class for a property with that name (uncapitalized). If the algorithm succeeds it uses that property. If not, the algorithm splits up the source at the camel case parts from the right side into a head and a tail and tries to find the corresponding property, in our example, AddressZip and Code. If the algorithm finds a property with that head it takes the tail and continue building the tree down from there, splitting the tail up in the way just described. If the first split does not match, the algorithm move the split point to the left (Address, ZipCode) and continues.

비록 이러한 작업이 대부분의 경우 잘 동작하지만, 잘못 동작할 경우가 있습니다. PersonaddressZip이라는 속성도 가지고 있다고 생각해봅시다. 이러한 알고리즘은 처음 나누면서 본질적으로 잘못된 뒷부분을 addressZip가지게 되어 아무런 속성도 아닌(code)것을 고르게 됩니다.

Although this should work for most cases, it is possible for the algorithm to select the wrong property. Suppose the Person class has an addressZip property as well. The algorithm would match in the first split round already and essentially choose the wrong property and finally fail (as the type of addressZip probably has no code property).

이러한 모호한 점을 해결하기 위해 당신은 _ 를 당신의 메소드 이름 내에 사용하여 수동적으로 횡단지점(tracersal이라고 나와있는데 나누는 지점이라 해야 맞을듯)을 정의할 수 있습니다. 그래서 메소드 이름은 다음과 같이 끝나게 됩니다.

To resolve this ambiguity you can use _ inside your method name to manually define traversal points. So our method name would end up like so:

List<Person> findByAddress_ZipCode(ZipCode zipCode);

우리가 _를 예약된 문자어로 썼기 때문에, 우리는 강력하게 표준 자바 작명 관습을 따르기를 권합니다 . (i.e. 카멜케이스CamelCase 쓰세요^^)

As we treat underscore as a reserved character we stongly advise to follow standard Java naming conventions (i.e. not using underscores in property names but camel case instead).

3.4.4. 특별 파라미터 핸들링
3.4.4. Special parameter handling

당신의 쿼리에서 파라미터를 다루기 위해, 당신은 간단히 메소드파라미터를 예전에 봤던 예제에서 처럼 정의할 수 있습니다. 게다가 인프라스트럭쳐가 Pageable, Sort같은 특정 타입을 인식하여 당신의 쿼리에서 페이징과 정렬을 동적으로 하게 해줄 것입니다.

To handle parameters in your query you simply define method parameters as already seen in the examples above. Besides that the infrastructure will recognize certain specific types like Pageable and Sort to apply pagination and sorting to your queries dynamically.

Example 9. 쿼리메소드에서 Pageable, Slice와 Sort를 사용하기
Example 9. Using Pageable, Slice and Sort in query methods
Page<User> findByLastname(String lastname, Pageable pageable);

Slice<User> findByLastname(String lastname, Pageable pageable);

List<User> findByLastname(String lastname, Sort sort);

List<User> findByLastname(String lastname, Pageable pageable);

첫번째 메소드는 org.springframework.data.domain.Pageable인스턴스를 쿼리메소드에 전달하여 정적으로 정의된 쿼리에 동적으로 페이징을 추가하게 해줍니다. Page는 전체적인 요소의 숫자와 가능한 페이지의 개수를 알 수 있습니다. 하부구조에서 숫자조회 쿼리를 발동하여 전체적인 숫자를 계산함으로써 그것이 가능해집니다. 이것은 사용하는 저장소에 따라서 비싼 작업이 될 수도 있습니다. Slice가 반환형으로 대신 리턴될 수도 있습니다. Slice는 오직 다음 Slice 가 가능한지만을 알고 있으며 많은 결과 셋을 가지고 작업할 때 쓰기 충분할 것입니다.

The first method allows you to pass an org.springframework.data.domain.Pageable instance to the query method to dynamically add paging to your statically defined query. A Page knows about the total number of elements and pages available. It does so by the infrastructure triggering a count query to calculate the overall number. As this might be expensive depending on the store used, Slice can be used as return instead. A Slice only knows about whether there’s a next Slice available which might be just sufficient when walking thought a larger result set.

정렬 옵션은 Pageable 인스턴스를 통해서도 다뤄지기도 합니다. 오직 정렬만을 필요로 한다면 org.springframework.data.domain.Sort 를 당신의 메소드에 파라미터로 전달해보세요. 당신이 보듯이, 단순히 List를 리턴하는 것이 가능할 것입니다. 이러한 경우 실제 Page인스턴스를 만들어내는 데 필요한 추가적인 메타데이터 생성되지 않을 것입니다 ( 여기서는 필요할지도 모르는 추가적인 카운트 쿼리가 발생되지 않는다는 것을 의미합니다 ) 대신에 단순히 쿼리를 오직 주어진 범위의 엔티티에서만으로 제한하는 것입니다.

Sorting options are handled through the Pageable instance too. If you only need sorting, simply add an org.springframework.data.domain.Sort parameter to your method. As you also can see, simply returning a List is possible as well. In this case the additional metadata required to build the actual Page instance will not be created (which in turn means that the additional count query that would have been necessary not being issued) but rather simply restricts the query to look up only the given range of entities.

당신이 쿼리에서 얼마나 많은 페이지를 얻을 수 있는지 알아보기 위해, 당신은 추가적인 count 쿼리를 동작시켜야 합니다. 기본적으로 이 쿼리는 당신이 실제적으로 동작하는 쿼리로부터 만들어집니다. (역주 : 말이 이상한 것같은데;; 그냥 페이징 하면서 카운트 쿼리가 같이 날아간다 그런 말인 듯하다.)
To find out how many pages you get for a query entirely you have to trigger an additional count query. By default this query will be derived from the query you actually trigger.

3.4.5. 쿼리 결과 Limit 하기
3.4.5. Limiting query results

쿼리메소드의 결과는 firsttop를 통해서 제한될 수 있으며 바꿔 쓸수 있습니다. 선택적인 숫자값이 top/first 로 추가되어 반환될 최대 결과 크기를 명시할 수 있습니다. 만약 숫자가 무시하면 1로 가정합니다.

The results of query methods can be limited via the keywords first or top, which can be used interchangeably. An optional numeric value can be appended to top/first to specify the maximum result size to be returned. If the number is left out, a result size of 1 is assumed.

예제 10. TopFirst로 결과 크기를 한계짓기 :
Example 10. Limiting the result size of a query with Top and First
User findFirstByOrderByLastnameAsc();

User findTopByOrderByAgeDesc();

Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);

Slice<User> findTop3ByLastname(String lastname, Pageable pageable);

List<User> findFirst10ByLastname(String lastname, Sort sort);

List<User> findTop10ByLastname(String lastname, Pageable pageable);

Limiting 표현은 또한 Distinct키워드를 지원합니다. 또한 결과 셋이 하나의 인스턴스로 제한된 쿼리들을 위해 결과를 Optional로 포장(wrapping)하는 것도 지원됩니다 .

The limiting expressions also support the Distinct keyword. Also, for the queries limiting the result set to one instance, wrapping the result into an Optional is supported.

만약 페이지네이션이나 슬라이싱이 제한된 쿼리 페이지네이션에 적용이 되면, (사용가능한 페이지숫자들에 적용이 되면) 이것은 제한된 결과내에서 적용됩니다.

If pagination or slicing is applied to a limiting query pagination (and the calculation of the number of pages available) then it is applied within the limited result.

Sort파라미터를 통한 동적 정렬과 결과값을 limiting 하는 것의 조합은 쿼리메소드를 나타내게 하는 것을 허용하게 해줍니다. ( 뒷부분에 나온 for문인데 잘 이해가 안된다. for the 'K' smallest as well as for the 'K' biggest elements.)
Note that limiting the results in combination with dynamic sorting via a Sort parameter allows to express query methods for the 'K' smallest as well as for the 'K' biggest elements.

3.4.6. 쿼리 결과 Streaming 하기
3.4.6. Streaming query results

쿼리 메소드의 결과값은 Java8의 Stream<T>를 이용하여 점차적으로 처리될 수 있습니다. 단순히 쿼리의 결과를 Stream 으로 포장( wrapping )하여, 데이터 스토어 특정 메소드들은 스트리밍을 하는데 사용됩니다.

The results of query methods can be processed incrementally by using a Java 8 Stream<T> as return type. Instead of simply wrapping the query results in a Stream data store specific methods are used to perform the streaming.

Example 11. 자바8 Stream<T>로 쿼리 결과 Stream하기 :
Example 11. Stream the result of a query with Java 8 Stream<T>
@Query("select u from User u")
Stream<User> findAllByCustomQueryAndStream();

Stream<User> readAllByFirstnameNotNull();

@Query("select u from User u")
Stream<User> streamAllPaged(Pageable pageable);
Stream은 잠재적으로 데이터스토어특정자원을 포장(wrap)하고 사용뒤에 반드시 닫혀져야 합니다. 당신은 직접 Streamclose()를 사용하여 닫아주거나 Java7의 try-with-resources블록을 사용할 수도 있습니다 .
A Stream potentially wraps underlying data store specific resources and must therefore be closed after usage. You can either manually close the Stream using the close() method or by using a Java 7 try-with-resources block.
예제 12. Stream<T> 와 try-with-resources 블록으로 작업해보기
Example 12. Working with a Stream<T> result in a try-with-resources block
try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) {
  stream.forEach(…);
}
모든 스프링 데이터 모듈이 현재 Stream<T>를 반환타입으로 제공하는 것은 아닙니다 .
Not all Spring Data modules currently support Stream<T> as a return type.

3.5. 리파지토리 인스턴스 생성하기
3.5. Creating repository instances

이 섹션에서는 당신은 리파지토리 인터페이스 정의를 위한 인스턴스들을 생성해보고 빈도 정의해봅니다.. 이것을 하는 한가지 방법은 스프링데이터모듈에 있는 Spring 네임 스페이스를 사용하는 것입니다. 비록 우리가 일반적으로 자바설정 스타일의 설정을 추천하지만, 스프링 데이터 모듈은 리파지토리 메커니즘을 지원합니다.

In this section you create instances and bean definitions for the repository interfaces defined. One way to do so is using the Spring namespace that is shipped with each Spring Data module that supports the repository mechanism although we generally recommend to use the Java-Config style configuration.

3.5.1. XML configuration

각각의 스프링 데이터 모듈은 repository 요소들을 포함하며 이 요소들은 단순하게 스프링이 스캔하는 base Package를 정의하게 해줍니다.

Each Spring Data module includes a repositories element that allows you to simply define a base package that Spring scans for you.

예제 13. XML을 통한 SPring data Repository 활성화
Example 13. Enabling Spring Data repositories via XML
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://www.springframework.org/schema/data/jpa"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/jpa
    http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

  <repositories base-package="com.acme.repositories" />

</beans:beans>

예전의 예제에서, 스프링은 Repository나 그 하위 인터페이스를 상속한 인터페이스들을 위해서 com.acme.repositories를 스캔을 하기로 되어있으며, 여기서의 모든 하위패키지들들을 스캔하기로 되어있습니다. 각각의 인터페이스가 발견되면 인프라스트럭쳐는 특정기술-영속 FactoryBean에 등록을 하여 적절한 프록시를 만들어 쿼리 메소드의 실행을 다루게 됩니다. 각각의 빈은 인터페이스로 추론된 빈 네임 아래 등록됩니다. 그래서 UserRepository 의 인터페이스는 userRepository로 등록이 될 것입니다. base-package속성은 와일드카드를 허용하여 당신은 스캔될 패키지로 패턴을 사용할 수도 있습니다 .

In the preceding example, Spring is instructed to scan com.acme.repositories and all its sub-packages for interfaces extending Repository or one of its sub-interfaces. For each interface found, the infrastructure registers the persistence technology-specific FactoryBean to create the appropriate proxies that handle invocations of the query methods. Each bean is registered under a bean name that is derived from the interface name, so an interface of UserRepository would be registered under userRepository. The base-package attribute allows wildcards, so that you can define a pattern of scanned packages.

Using filters

기본적으로 인프라스트럭쳐는 설정된 base package 밑의 각각의 특정영속기술 Repository하위 인터페이스를 골라서, 그것을 위한 빈 인스턴스를 생성합니다. 그러나 당신은 좀더 상세한 설정을 원할 수도 있습니다. 이를 위해서 당신은 <include-filter /><exclude-filter />를 요소를 <repositories /> 내부에 사용할 수도 있습니다. 이러한 문법은 스프링의 콘텍스트 네임스페이스와 정확히 동일합니다. 세부적인 사항은 스프링 레퍼런스 문서를 보시길 바랍니다. (역주: 번역문서라 스프링 공홈 변수 설정이 안됨ㅠ)

By default the infrastructure picks up every interface extending the persistence technology-specific Repository sub-interface located under the configured base package and creates a bean instance for it. However, you might want more fine-grained control over which interfaces bean instances get created for. To do this you use <include-filter /> and <exclude-filter /> elements inside <repositories />. The semantics are exactly equivalent to the elements in Spring’s context namespace. For details, see Spring reference documentation on these elements.

예를 들자면, 리파지토리에서 인스턴스화되는 특정 인터페이스를 제외하기 위해서 당신은 다음의 설정을 할 수도 있습니다 :

For example, to exclude certain interfaces from instantiation as repository, you could use the following configuration:

예제 14. exclude-filter 요소 사용해보기
Example 14. Using exclude-filter element
<repositories base-package="com.acme.repositories">
  <context:exclude-filter type="regex" expression=".*SomeRepository" />
</repositories>

이 예제는 SomeRepository 로 끝나는 모든 요소들이 인스턴스화되지 않게 합니다.

This example excludes all interfaces ending in SomeRepository from being instantiated.

3.5.2. JavaConfig

리파지토리 인프라스트럭쳐는 자바설정에서 특정 저장소 @Enable${store}Repositories 어노테이션을 사용하여 동작합니다. 스프링콘테이너에서 자바 기반의 설정에 대한 소개를 보기 위해 , 레퍼런스 문서를 보세요.[1]

The repository infrastructure can also be triggered using a store-specific @Enable${store}Repositories annotation on a JavaConfig class. For an introduction into Java-based configuration of the Spring container, see the reference documentation.[1]

스프링 데이터 리파지토리를 활성화시키는 간단한 설정입니다.

A sample configuration to enable Spring Data repositories looks something like this.

예제 15. 간단한 어노테이션 기반의 리파지토리 설정
Example 15. Sample annotation based repository configuration
@Configuration
@EnableJpaRepositories("com.acme.repositories")
class ApplicationConfiguration {

  @Bean
  public EntityManagerFactory entityManagerFactory() {
    // …
  }
}
예제에서는 JPA기반의 어노테이션을 사용하였지만 당신이 실제로 사용하는 저장소모듈에 따라 바꿔줄 수 있습니다. EntityManagerFactory빈의 정의에 똑같이 적용이 됩니다. 특정저장소 설정에 대해 다루는 섹션을 참조하세요
The sample uses the JPA-specific annotation, which you would change according to the store module you actually use. The same applies to the definition of the EntityManagerFactory bean. Consult the sections covering the store-specific configuration.

3.5.3. Standalone usage

당신은 또한 스프링 컨테이너의 바깥에서 repository infrastructure를 사용할 수 있습니다. 예를 들자면 CDI환경도 있습니다. 당신은 여전히 클래스 패스에 스프링 라이브러리가 필요할 것이지만, 일반적으로 계획에 따라서 리파지토리들을 설정할 수 있습니다. 리파지토리 지원을 제공하는 스프링 데이터 모듈은 당신이 사용할 수 있는 특정기술영속저장소 RepositoryFactory를 가지고 있을 수 있습니다 .

You can also use the repository infrastructure outside of a Spring container, e.g. in CDI environments. You still need some Spring libraries in your classpath, but generally you can set up repositories programmatically as well. The Spring Data modules that provide repository support ship a persistence technology-specific RepositoryFactory that you can use as follows.

예제 16. 리파지토리 팩토리의 독립적인 사용. Standalone usage of repository factory
Example 16. Standalone usage of repository factory
RepositoryFactorySupport factory = … // Instantiate factory here
UserRepository repository = factory.getRepository(UserRepository.class);

3.6. 스프링 데이터 리파지토리의 커스텀 구현
3.6. Custom implementations for Spring Data repositories

종종 적은 리파지토리 메소드를 위해 커스텀 구현을 제공해야할 필요가 있습니다. 스프링 데이터 리파지토리는 쉽게 커스텀 리파지토리 코드를 제공하고 , 이것을 일반적인 CRUD추상화와 쿼리 메소드 기능에 통합시키게 해줍니다.

Often it is necessary to provide a custom implementation for a few repository methods. Spring Data repositories easily allow you to provide custom repository code and integrate it with generic CRUD abstraction and query method functionality.

3.6.1. 단일 리파지토리에 커스텀 행동 추가해보기
3.6.1. Adding custom behavior to single repositories

리파지토리에 커스텀 기능을 좀 더 넣기 위해, 당신은 먼저 인터페이스를 정의하고, 그 커스텀 기능을 위한 구현체를 정의해야 합니다. repository 인터페이스를 사용하여 커스텀 인터페이스를 확장하세요.

To enrich a repository with custom functionality you first define an interface and an implementation for the custom functionality. Use the repository interface you provided to extend the custom interface.

예제 17. 커스텀 리파지토리 기능을 위한 인터페이스
Example 17. Interface for custom repository functionality
interface UserRepositoryCustom {
  public void someCustomMethod(User user);
}
예제 18. 커스텀 리파지토리 기능 구현
Example 18. Implementation of custom repository functionality
class UserRepositoryImpl implements UserRepositoryCustom {

  public void someCustomMethod(User user) {
    // Your custom implementation
  }
}
다른 핵심리파지토리 인터페이스와 비교해봤을 때, 클래스에서 가장 중요하게 발견되는 부분은 이름의 Impl 접미어입니다. (하단 참조 )
The most important bit for the class to be found is the Impl postfix of the name on it compared to the core repository interface (see below).

그 스스로의 구현은 스프링데이터에 의존하지 않으며, 정식 스프링빈이 될 수 있습니다. 그러므로 당신은 JDBCTemplate 같은 다른 빈처럼, 표준 의존성주입행동을 주입레퍼런스로써 사용할 수 있습니다. aspects에 참여시키는 것같은 일을 할 수가 있습니다.

The implementation itself does not depend on Spring Data and can be a regular Spring bean. So you can use standard dependency injection behavior to inject references to other beans like a JdbTemplate, take part in aspects, and so on.

예제 19. 당신의 기본적인 리파지토리 인터페이스 변경 Changes to the your basic repository interface
Example 19. Changes to the your basic repository interface
interface UserRepository extends CrudRepository<User, Long>, UserRepositoryCustom {

  // Declare query methods here
}

이제 당신의 표준 리파지토리 인터페이스를 커스텀버젼으로 확장해봅시다. CRUD와 커스텀 기능을 조합하여 클라이언트에게 사용가능하게 해봅시다.

Let your standard repository interface extend the custom one. Doing so combines the CRUD and custom functionality and makes it available to clients.

설정

만약 당신이 네임스페이스 설정을 사용한다면 리파지토리 인프라스트럭쳐는 자동으로 리파지토리를 발견한 곳에 있는 패키지에서 클래스를 스캔하여 커스텀 구현체를 찾을 것입니다. 이러한 클래스들은 네임스페이스 요소 속성 repository-impl-postfix을 따를 필요가 있습니다. 이러한 접미어는 기본적으로 Impl입니다.

If you use namespace configuration, the repository infrastructure tries to autodetect custom implementations by scanning for classes below the package we found a repository in. These classes need to follow the naming convention of appending the namespace element’s attribute repository-impl-postfix to the found repository interface name. This postfix defaults to Impl.

설정 20. 설정 예제Configuration example
Example 20. Configuration example
<repositories base-package="com.acme.repository" />

<repositories base-package="com.acme.repository" repository-impl-postfix="FooBar" />

첫번째 설정 예제에서는 com.acme.repository.UserRepositoryImpl 라는 클래스를 찾아 커스텀 리파지토리 구현체로 작동하게 할 것입니다. 반면에 두번째 예제 줄에서는 com.acme.repository.UserRepositoryFooBar를 찾을 것입니다.

The first configuration example will try to look up a class com.acme.repository.UserRepositoryImpl to act as custom repository implementation, whereas the second example will try to lookup com.acme.repository.UserRepositoryFooBar.

수동 와이어링 Manual wiring

만약 당신의 커스텀 구현체가 어노테이션 기반의 설정을 사용한다면 이러한 접근은 잘 동작할 것이며, 커스텀 구현체는 다른 스프링 빈으로 잘 다뤄질 것입니다. 만약 커스텀 구현체가 특별한 와이어링을 원한다면 당신은 단순히 빈을 선언하고 앞서 선언한 컨벤션에 따라 이름지으면 됩니다. 인프라스트럭쳐는 수동으로, 새로 빈을 만드는 대신에 이름으로 정의된 빈정의를 참조할 것입니다.

The approach just shown works well if your custom implementation uses annotation-based configuration and autowiring only, as it will be treated as any other Spring bean. If your custom implementation bean needs special wiring, you simply declare the bean and name it after the conventions just described. The infrastructure will then refer to the manually defined bean definition by name instead of creating one itself.

예제 21. 커스텀 구현체의 수동 와이어링 Manual wiring of custom implementations
Example 21. Manual wiring of custom implementations
<repositories base-package="com.acme.repository" />

<beans:bean id="userRepositoryImpl" class="…">
  <!-- further configuration -->
</beans:bean>

3.6.2. 모든 리파지토리들에 커스텀 행동 추가하기
3.6.2. Adding custom behavior to all repositories

만약 당신이 모든 리파지토리 인터페이스에 하나의 메소드를 추가하고 싶을 때 이전의 접근들은 실현가능하지가 않았습니다.

The preceding approach is not feasible when you want to add a single method to all your repository interfaces.

  1. 모든 리파지토리에서 커스텀 행동을 추가하기 위해, 먼저 공유행동으로 선언할 중계인터페이스를 추가합니다.
    To add custom behavior to all repositories, you first add an intermediate interface to declare the shared behavior
    예제 22. 커스텀 공유 행동 선언하는 인터페이스
    Example 22. An interface declaring custom shared behavior
    @NoRepositoryBean
    public interface MyRepository<T, ID extends Serializable>
      extends PagingAndSortingRepository<T, ID> {
    
      void sharedCustomMethod(ID id);
    }
  2. 이제 당신의 독립적인 리파지토리 인터페이스는 Repository대신에 이 중계인터페이스를 상속하고, 선언된 기능을 포함할 것입니다.
    Now your individual repository interfaces will extend this intermediate interface instead of the Repository interface to include the functionality declared.
  3. 다음으로, 영속기술특징 리파지토리 빈 클래스를 상속하는 중계인터페이스의 구현체를 생성합니다. 이 클래스는 리파지토리 프록시를 위한 커스텀 base 클래스로 동작할 것입니다 .
    Next, create an implementation of the intermediate interface that extends the persistence technology-specific repository base class. This class will then act as a custom base class for the repository proxies.
    예제 23. 커스텀 리파지토리 베이스 클래스 Custom repository base class
    Example 23. Custom repository base class
    public class MyRepositoryImpl<T, ID extends Serializable>
      extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID> {
    
      private final EntityManager entityManager;
    
      public MyRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
        super(domainClass, entityManager);
    
        // Keep the EntityManager around to used from the newly introduced methods.
        this.entityManager = entityManager;
      }
    
      public void sharedCustomMethod(ID id) {
        // implementation goes here
      }
    }
    spring의 <repositories /> 네임스페이스의 기본행동은 base-package아래에 있는 모든 인터페이스들에 대한 구현체를 제공하는 것입니다. 이것은 현재 상태로 남아있다면 구현체는 MyRepository의 인스턴스가 스프링에 의해 생성된다는 것을 의미합니다. 물론 이것은 Repository와 실제 엔티티를 위해 정의한 리파지토리 인터페이스간에서 중계자로 행동하기를 바라고 있는 와중에 의도한 것이 아닙니다. Repository를 상속하는 인터페이스를 제외시키기 위해 당신은 그것을 @NoRepositoryBean로 어노테이션하거나, base-package바깥으로 제외시켜줘야할 것입니다.
    The default behavior of the Spring <repositories /> namespace is to provide an implementation for all interfaces that fall under the base-package. This means that if left in its current state, an implementation instance of MyRepository will be created by Spring. This is of course not desired as it is just supposed to act as an intermediary between Repository and the actual repository interfaces you want to define for each entity. To exclude an interface that extends Repository from being instantiated as a repository instance, you can either annotate it with @NoRepositoryBean (as seen above) or move it outside of the configured base-package.
  4. 그러고 난 후에 커스텀 리파지토리 팩토리를 만들어 기본 RepositoryFactoryBean를 대체하여 커스텀 RepositoryFactory을 만들어봅시다. 그러면 새로운 리파지토리 팩토리는 당신에게 Repository인터페이스를 상속하는 어떤 인터페이스의 구현체로써 MyRepositoryImpl를 제공할 것이고, 이것은 SimpleJpaRepository 구현체를 대체할 것입니다.

    Then create a custom repository factory to replace the default RepositoryFactoryBean that will in turn produce a custom RepositoryFactory. The new repository factory will then provide your MyRepositoryImpl as the implementation of any interfaces that extend the Repository interface, replacing the SimpleJpaRepository implementation you just extended.

    예제 24. 커스텀 리파지토리 팩토리 빈 Custom repository factory bean
    Example 24. Custom repository factory bean
    public class MyRepositoryFactoryBean<R extends JpaRepository<T, I>, T,
      I extends Serializable> extends JpaRepositoryFactoryBean<R, T, I> {
    
      protected RepositoryFactorySupport createRepositoryFactory(EntityManager em) {
        return new MyRepositoryFactory(em);
      }
    
      private static class MyRepositoryFactory<T, I extends Serializable>
        extends JpaRepositoryFactory {
    
        private final EntityManager em;
    
        public MyRepositoryFactory(EntityManager em) {
    
          super(em);
          this.em = em;
        }
    
        protected Object getTargetRepository(RepositoryMetadata metadata) {
          return new MyRepositoryImpl<T, I>((Class<T>) metadata.getDomainClass(), em);
        }
    
        protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
          return MyRepositoryImpl.class;
        }
      }
    }
  5. 마침내, 커스텀 팩토리를 직접적으로 선언을 하거나, 스프링 네임스페이스의 factory-class 속성을 사용하거나 @Enable…어노테이션으로 repository 인프라스트럭쳐를 설정하여서, (마침내) 당신의 커스텀 팩토리 구현체를 사용하게 됩니다.
    Finally, either declare beans of the custom factory directly or use the factory-class attribute of the Spring namespace or @Enable… annotation to instruct the repository infrastructure to use your custom factory implementation.
    예제 25. 커스텀 팩토리와 네임스페이스 사용하기 Using the custom factory with the namespace
    Example 25. Using the custom factory with the namespace
    <repositories base-package="com.acme.repository"
      factory-class="com.acme.MyRepositoryFactoryBean" />
    예제 26. @Enable… 어노테이션과 함께 커스텀 팩토리 사용하기
    Example 26. Using the custom factory with the @Enable… annotation
    @EnableJpaRepositories(factoryClass = "com.acme.MyRepositoryFactoryBean")
    class Config {}

3.7. 스프링 데이터 익스텐션
3.7. Spring Data extensions

이 섹션문서는 스프링 데이터 익스텐션(확장)문서의 모음으로 스프링 데이터 익스텐션은 스프링 데이터의 사용을 다양한 context로써, 가능케 해줍니다. 현재는 대부분의 통합이 SpringMVC를 대상으로합니다.
This section documents a set of Spring Data extensions that enable Spring Data usage in a variety of contexts. Currently most of the integration is targeted towards Spring MVC.

3.7.1. Web support

이 섹션은 스프링 데이터 웹서포트를 위한 문서를 포함하며 스프링 데이터 웹서포트는 1.6 범위에서 스프링데이터 Commons로 구현되었습니다. 새롭게 소개된 지원이 꽤 많은 것들을 변화시켜서, 이전의 행동에 관한 문서를 우리는 레거시 웹 지원에 가지고 있습니다.
This section contains the documentation for the Spring Data web support as it is implemented as of Spring Data Commons in the 1.6 range. As it the newly introduced support changes quite a lot of things we kept the documentation of the former behavior in Legacy web support.

스프링 데이터 모듈은, 리파지토리 프로그래밍 모델을 지원하는 모듈이라면 다양한 방식의 웹지원을 가지고 있습니다. 웹관련 작업은 classpath의 SpringMVC JARs를 필요로 하며, 그들의 일부는 심지어 Spring Hateoas[2]를 제공하기도 합니다. 일반적으로 통합 지원은 자바설정클래스에서 @EnableSpringDataWebSupport 어노테이션을 사용함으로써 활성화됩니다.

Spring Data modules ships with a variety of web support if the module supports the repository programming model. The web related stuff requires Spring MVC JARs on the classpath, some of them even provide integration with Spring HATEOAS [2]. In general, the integration support is enabled by using the @EnableSpringDataWebSupport annotation in your JavaConfig configuration class.

예제 27. 스프링 데이터 웹지원 활성화 Enabling Spring Data web support
Example 27. Enabling Spring Data web support
@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
class WebConfiguration { }

@EnableSpringDataWebSupport 어노테이션은 우리가 뒤에 나올 새로운 컴포넌트를 등록시킵니다. 이것은 클래스패스의 Spring HATEOAS를 감지하여서, hateoas가 있다면 통합 컴포넌트로써 등록을 시킵니다.

The @EnableSpringDataWebSupport annotation registers a few components we will discuss in a bit. It will also detect Spring HATEOAS on the classpath and register integration components for it as well if present.

아니면 만약 당신이 XML설정을 쓴다면, SpringDataWebSupportHateoasAwareSpringDataWebSupport를 스프링 빈으로 등록합니다.
Alternatively, if you are using XML configuration, register either SpringDataWebSupport or HateoasAwareSpringDataWebSupport as Spring beans:

예제 28. XML에서의 스프링 데이터 웹서포트 활성화 Enabling Spring Data web support in XML
Example 28. Enabling Spring Data web support in XML
<bean class="org.springframework.data.web.config.SpringDataWebConfiguration" />

<!-- If you're using Spring HATEOAS as well register this one *instead* of the former -->
<bean class="org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration" />
기본 웹 지원
위에 보이는 설정 셋업은 몇가지 기초 컴포넌트들을 등록할 것입니다
The configuration setup shown above will register a few basic components:
  • DomainClassConverter 는 SpringMVC 를 활성화시켜서 요청파라미터나 경로변수로부터 리파지토리 managed도메인클래스를 resolve합니다.
    A DomainClassConverter to enable Spring MVC to resolve instances of repository managed domain classes from request parameters or path variables.

  • HandlerMethodArgumentResolver 는 스프링 MVC로 하여금 요청파라미터로부터 Pageable 과 Sort 인스턴스를 resolve하게 해줍니다.
    HandlerMethodArgumentResolver implementations to let Spring MVC resolve Pageable and Sort instances from request parameters.

도메인 클래스 콘버터 : DomainClassConverter
DomainClassConverter는 SpringMVC 컨트롤러 시그니처에서 직접적으로 도메인 타입을 사용하게 해줍니다. 그래서 당신은 수동적으로 리파지토리를 통해 인스턴스를 찾을 필요가 없습니다.
The DomainClassConverter allows you to use domain types in your Spring MVC controller method signatures directly, so that you don’t have to manually lookup the instances via the repository:
Example 29. 메소드 시그니처에서 도메인 타입을 사용하는 스프링 MVC 컨트롤러
Example 29. A Spring MVC controller using domain types in method signatures
@Controller
@RequestMapping("/users")
public class UserController {

  @RequestMapping("/{id}")
  public String showUserForm(@PathVariable("id") User user, Model model) {

    model.addAttribute("user", user);
    return "userForm";
  }
}
당신이 볼 수 있듯, 메소드가 유저 인스턴스를 직접적으로 받고 더 이상의 검색은 필요하지가 않습니다. 저 인스턴스는 SpringMVC가 경로변수를 도메인 클래스의 id타입으로 전환하게 하여, 결국 도메인 타입에 등록된 리파지토리에서 findOne(…)을 호출하게 합니다.
As you can see the method receives a User instance directly and no further lookup is necessary. The instance can be resolved by letting Spring MVC convert the path variable into the id type of the domain class first and eventually access the instance through calling findOne(…) on the repository instance registered for the domain type.
현재 저 리파지토리는 CrudRepository 를 구현을 하여서 전환을 위한 자격을 얻어야 합니다 .
Currently the repository has to implement CrudRepository to be eligible to be discovered for conversion.
페이지화와 정렬을 위한 HandlerMethodArgumentResolvers
HandlerMethodArgumentResolvers for Pageable and Sort

위의 소스설정 일부는 또한 PageableHandlerMethodArgumentResolver를 등록하고 SortHandlerMethodArgumentResolver의 인스턴스를 등록합니다. 이 등록은 PageableSort가 유효한 컨틀롤러 메소드 아규먼트가 되게 해줍니다 .

The configuration snippet above also registers a PageableHandlerMethodArgumentResolver as well as an instance of SortHandlerMethodArgumentResolver. The registration enables Pageable and Sort being valid controller method arguments

예제 30. Pageable을 컨트롤러 메소드 아규먼트로 사용하기. Using Pageable as controller method argument
Example 30. Using Pageable as controller method argument
@Controller
@RequestMapping("/users")
public class UserController {

  @Autowired UserRepository repository;

  @RequestMapping
  public String showUsers(Model model, Pageable pageable) {

    model.addAttribute("users", repository.findAll(pageable));
    return "users";
  }
}
이 메소드 시그니처는 다음의 설정을 사용하여 스프링 MVC가 요청 파라미터로부터 Pageable 인스턴스를 만들어내게 시도합니다.
This method signature will cause Spring MVC try to derive a Pageable instance from the request parameters using the following default configuration:
표 1. Pageable 인스턴스를 위해 평가된 요청파라미터 Request parameters evaluated for Pageable instances

page

얻기 원하는 페이지, 0 indexed and 기본은 0.

size

얻기 원하는 페이지 크기, 기본 20.

sort

다음의 형식으로 정렬될 형식 property,property(,ASC|DESC). 기본 정렬 방향은 오름차순(asc). 만약 방향을 바꾸고 싶은 여러개의sort 파라미터가 있다면 다음과 같이.., e.g. ?sort=firstname&sort=lastname,asc.

이러한 행동을 커스터마이징 하고 싶다면 @Enable-어노테이션을 사용하는 대신에 SpringDataWebConfiguration를 상속하거나 HATEOAS-활성화 같은 것을 하거나, pageableResolver()sortResolver()메소드를 오버라이드하고 당신의 커스터마이징된 설정파일을 임포트하세요.

To customize this behavior extend either SpringDataWebConfiguration or the HATEOAS-enabled equivalent and override the pageableResolver() or sortResolver() methods and import your customized configuration file instead of using the @Enable-annotation.

이러한 경우 당신은 여러개의 테이블을 위해서, 요청으로부터 여러개의 PageableSort 인스턴스가 resolved되기를 필요로 할지도 모릅니다. 예를 들자면 당신은 스프링의 @Qualifier어노테이션을 사용하여 다른 것들끼리 구별을 할 수도 있습니다. 요청파라미터는 그러면 ${qualifier}_로 prefixed됩니다. 그래서 메소드 시그니처가 다음과 같이 됩니다 :

In case you need multiple Pageable or Sort instances to be resolved from the request (for multiple tables, for example) you can use Spring’s @Qualifier annotation to distinguish one from another. The request parameters then have to be prefixed with ${qualifier}_. So for a method signature like this:

public String showUsers(Model model,
      @Qualifier("foo") Pageable first,
      @Qualifier("bar") Pageable second) { … }
당신은 foo_pagebar_page같은 것을 만들어낼 것입니다 .
you have to populate foo_page and bar_page etc.
메소드에 전해진 기본 Pageablenew PageRequest(0, 20)와 같습니다만, Pageable 파라미터에서 @PageableDefaults 어노테이션을 통해서 커스터마이징 될 수 있습니다.
The default Pageable handed into the method is equivalent to a new PageRequest(0, 20) but can be customized using the @PageableDefaults annotation on the Pageable parameter.
Hypermedia support for Pageables
Spring HATEOAS는 표현 모델 클래스 PagedResources를 가지고 있어서 Page인스턴스를, 필요한 Page메타데이터로 풍부하게 해줄 수 있을 뿐만 아니라, 링크가 클라이언트가 쉽게 페이지를 네비게이트하게 해줍니다. Page에서 PagedResources으로의 전환은 Spring HATEOAS ResourceAssembler 인터페이스, PagedResourcesAssembler를 구현함으로써 이뤄집니다. .
Spring HATEOAS ships with a representation model class PagedResources that allows enrichting the content of a Page instance with the necessary Page metadata as well as links to let the clients easily navigate the pages. The conversion of a Page to a PagedResources is done by an implementation of the Spring HATEOAS ResourceAssembler interface, the PagedResourcesAssembler.
예제 31.PagedResourcesAssembler를 컨트롤러 메소드 아규먼트로 사용하기. Using a PagedResourcesAssembler as controller method argument
Example 31. Using a PagedResourcesAssembler as controller method argument
@Controller
class PersonController {

  @Autowired PersonRepository repository;

  @RequestMapping(value = "/persons", method = RequestMethod.GET)
  HttpEntity<PagedResources<Person>> persons(Pageable pageable,
    PagedResourcesAssembler assembler) {

    Page<Person> persons = repository.findAll(pageable);
    return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK);
  }
}

위 처럼 설정을 활성화하는 것은 PagedResourcesAssembler가 컨트롤러 메소드 아규먼트로 사용되게 해줍니다. 여기서 toResources(…) 를 호출하는 것은 다음과 같은 것을 발생하게 해줍니다 :

Enabling the configuration as shown above allows the PagedResourcesAssembler to be used as controller method argument. Calling toResources(…) on it will cause the following:

  • Page의 내용이 PagedResources인스턴스의 내용(content)이 되게 해줍니다
    The content of the Page will become the content of the PagedResources instance.
  • PagedResources는, 동봉되어 채워진 정보와 함께 PageMetadata 인스턴스를 얻어서 Page와 기본 PageRequest를 형성합니다.

  • PagedResources는 같이 있는 페이지의 상태에 따라 prevnext 연결을 얻습니다.. 이 링크는 메소드실행이 연결된 URI를 가리키며, 그 메소드에 추가된 페이지네이션 파라미터는 PageableHandlerMethodArgumentResolver의 설정과 매칭이 되어 링크가 나중에 resolved되게 해줍니다.
  • The PagedResources will get a PageMetadata instance attached populated with information form the Page and the underlying PageRequest.

  • The PagedResources gets prev and next links attached depending on the page’s state. The links will point to the URI the method invoked is mapped to. The pagination parameters added to the method will match the setup of the PageableHandlerMethodArgumentResolver to make sure the links can be resolved later on.

우리가 30명의 인원 인스턴스를 데이터베이스에 가지고 있다고 해봅시다. 당신은 이제 GET http://localhost:8080/persons요청을 해볼 것이고, 이러한 결과를 보게 될 것입니다

Assume we have 30 Person instances in the database. You can now trigger a request GET http://localhost:8080/persons and you’ll see something similar to this:

{ "links" : [ { "rel" : "next",
                "href" : "http://localhost:8080/persons?page=1&size=20 }
  ],
  "content" : [
     … // 20 Person instances rendered here
  ],
  "pageMetadata" : {
    "size" : 20,
    "totalElements" : 30,
    "totalPages" : 2,
    "number" : 0
  }
}

당신은 정확한 URI를 만들어내고서, 기본 설정이 앞으로 들어오는 요청에 대해 파라미터를 Pageable로 resolve하는 것을 볼 수가 있습니다. 이것이 의미하는 바는 만약 당신이 저 설정을 바꾼다면 이 연결은 자동적으로 바뀔 것입니다. (역주 : 파라미터값이 바뀌면 page metadata도 바뀐다 이말인듯?) 기본적으로 assembler는 실행될 컨트롤러 메소드를 가리킵니다. 하지만 커스텀 Link를 제시하여 커스터마이징할 수 있고, PagedResourcesAssembler.toResource(…)의 메소드를 오버라이딩하여 페이지네이션 링크를 만드는 데 기반이 될 수 있습니다 .

You see that the assembler produced the correct URI and also picks up the default configuration present to resolve the parameters into a Pageable for an upcoming request. This means, if you change that configuration, the links will automatically adhere to the change. By default the assembler points to the controller method it was invoked in but that can be customized by handing in a custom Link to be used as base to build the pagination links to overloads of the PagedResourcesAssembler.toResource(…) method.

3.7.2. 레파지토리 생성자(popluators)
3.7.2. Repository populators

만약 당신이 Spring JDBC 모듈과 함께 작업한다면 당신은 아마 SQL 스크립트를 이용하는 DataSource를 생성하는데 익숙할 것입니다. 비슷한 추상화가 리파지토리 레벨에서 가능한데요. 비록 DDL로써 sql을 사용하지는 않습니다. 왜냐하면 sql은 저장소 의존적이기 때문이죠 . 그렇기 때문에 populator는 XML(스프링의 OXM추상화를 통해)과 JSON(Jackson을 통해)으로 repository에 생성할 데이터를 정의합니다.

If you work with the Spring JDBC module, you probably are familiar with the support to populate a DataSource using SQL scripts. A similar abstraction is available on the repositories level, although it does not use SQL as the data definition language because it must be store-independent. Thus the populators support XML (through Spring’s OXM abstraction) and JSON (through Jackson) to define data with which to populate the repositories.

당신이 data.json 파일을 다음과 같이 가지고 있다고 가정해봅시다 :

Assume you have a file data.json with the following content:

예제 32. JSON으로 정의된 데이터
Example 32. Data defined in JSON
[ { "_class" : "com.acme.Person",
 "firstname" : "Dave",
  "lastname" : "Matthews" },
  { "_class" : "com.acme.Person",
 "firstname" : "Carter",
  "lastname" : "Beauford" } ]

당신은 Spring Data Commons 에서 제공하는 리파지토리 네임스페이스의 populator 요소를 사용하여서 당신의 리파지토리를 쉽게 populate할 수 있습니다. 이전의 data를 당신의 리파지토리에 populate하기 위해 다음과 같이 해보세요 :

You can easily populate your repositories by using the populator elements of the repository namespace provided in Spring Data Commons. To populate the preceding data to your PersonRepository , do the following:

Example 33. Jackson Repository populator선언하기 Declaring a Jackson repository populator
Example 33. Declaring a Jackson repository populator
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:repository="http://www.springframework.org/schema/data/repository"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/repository
    http://www.springframework.org/schema/data/repository/spring-repository.xsd">

  <repository:jackson-populator locations="classpath:data.json" />

</beans>
이 선언은 data.json이 읽혀져서 Jackson ObjectMapper를 통해 역직렬화 되게 해줍니다 .
This declaration causes the data.json file to be read and deserialized via a Jackson ObjectMapper.

JSON객체가 언마샬될 타입은 JSON문서의 _class 속성을 검사하면서 결정됩니다. 인프라스트럭쳐가 결국 적절한 리파지토리를 선택해서 막 역직렬화된 객체를 다룰 것입니다.

The type to which the JSON object will be unmarshalled to will be determined by inspecting the _class attribute of the JSON document. The infrastructure will eventually select the appropriate repository to handle the object just deserialized.

XML을 사용하여 리파지토리들을 채울 데이터를 정의하고 싶다면, 당신은 unmarshaller-populator요소를 사용할 수 있습니다. 당신은 SPRING OXM 공급자가 제공하는 XML marshaller 옵션 중의 하나를 사용하여서 설정을 할 수가 있습니다. 스프링 레퍼런스 가이드 문서를 살펴보세요 (역주 : 번역문서라 링크가 깨짐.. { }뭐 이런 변수가 되있다보니;; ㅎㅎ )

To rather use XML to define the data the repositories shall be populated with, you can use the unmarshaller-populator element. You configure it to use one of the XML marshaller options Spring OXM provides you with. See the Spring reference documentation for details.

Example 34. 언마샬링 repository populator(JAXB를 사용한)을 선언하기
Example 34. Declaring an unmarshalling repository populator (using JAXB)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:repository="http://www.springframework.org/schema/data/repository"
  xmlns:oxm="http://www.springframework.org/schema/oxm"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/repository
    http://www.springframework.org/schema/data/repository/spring-repository.xsd
    http://www.springframework.org/schema/oxm
    http://www.springframework.org/schema/oxm/spring-oxm.xsd">

  <repository:unmarshaller-populator locations="classpath:data.json"
    unmarshaller-ref="unmarshaller" />

  <oxm:jaxb2-marshaller contextPath="com.acme" />

</beans>

3.7.3. 레거시 웹 지원
3.7.3. Legacy web support

스프링 MVC를 위한 도메인 클래스 웹 바인딩
Domain class web binding for Spring MVC

당신이 SPRING MVC웹 어플리케이션으로 개발중이고 일반적으로 URL에서 도메인 클래스 id를 URL에서 resolve해야할 것입니다. 기본적으로 할일은 그 리퀘스트 파라미터나 URL의 부분을 도메인 클래스로 전달하여 하위 레이어로 전달하고 엔티티들에 대한 직접적인 비즈니스 로직을 수행해야 할 것입니다. 이것은 다음과 같이 보일 것입니다 :
Given you are developing a Spring MVC web application you typically have to resolve domain class ids from URLs. By default your task is to transform that request parameter or URL part into the domain class to hand it to layers below then or execute business logic on the entities directly. This would look something like this:

@Controller
@RequestMapping("/users")
public class UserController {

  private final UserRepository userRepository;

  @Autowired
  public UserController(UserRepository userRepository) {
    Assert.notNull(repository, "Repository must not be null!");
    this.userRepository = userRepository;
  }

  @RequestMapping("/{id}")
  public String showUserForm(@PathVariable("id") Long id, Model model) {

    // Do null check for id
    User user = userRepository.findOne(id);
    // Do null check for user

    model.addAttribute("user", user);
    return "user";
  }
}

먼저 당신은 각각의 컨트롤러에 대한 각각의 의존선을 선언하여 컨트롤러나 repository에서 각자 엔티티들을 찾아야 합니다. 엔티티를 찾는 것은 또한 보일러플레이트한 작업이 될 수가 있으면 이것은 언제나 findOne(…) 을 호출합니다. 다행스럽게도 스프링은 String값을 무작위의 값으로 전환시킬수 있는 커스텀 컴포넌트를 등록하게 해주는 방법을 제공합니다.

First you declare a repository dependency for each controller to look up the entity managed by the controller or repository respectively. Looking up the entity is boilerplate as well, as it’s always a findOne(…) call. Fortunately Spring provides means to register custom components that allow conversion between a String value to an arbitrary type.

PropertyEditors

스프링 버젼 3.0 이전의 간단한 자바 PropertyEditors 가 사용되어졌었습니다. 이것과 통합하기 위해, 스프링 데이터는 DomainClassPropertyEditorRegistrar를 제공하며 이것은 ApplicationContext 에 등록된 모든 스프링 데이터 리파지토리들을 찾고, 관리되는 도메인 클래스들을 위해서 커스텀 PropertyEditor를 등록시킵니다.

For Spring versions before 3.0 simple Java PropertyEditors had to be used. To integrate with that, Spring Data offers a DomainClassPropertyEditorRegistrar, which looks up all Spring Data repositories registered in the ApplicationContext and registers a custom PropertyEditor for the managed domain class.

<bean class="….web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
  <property name="webBindingInitializer">
    <bean class="….web.bind.support.ConfigurableWebBindingInitializer">
      <property name="propertyEditorRegistrars">
        <bean class="org.springframework.data.repository.support.DomainClassPropertyEditorRegistrar" />
      </property>
    </bean>
  </property>
</bean>

만약 당신이 이전의 예제에서 SPRING MVC를 설정했다면, 당신은 당신의 컨트롤러를 다음과 같이 설정할 수 있으며, 많은 어수선한 부분과 보일러플레이트한 부분을 줄일 수가 있을 것입니다.

If you have configured Spring MVC as in the preceding example, you can configure your controller as follows, which reduces a lot of the clutter and boilerplate.

@Controller
@RequestMapping("/users")
public class UserController {

  @RequestMapping("/{id}")
  public String showUserForm(@PathVariable("id") User user, Model model) {

    model.addAttribute("user", user);
    return "userForm";
  }
}

레퍼런스 문서
Reference Documentation

4. JPA 리파지토리들
4. JPA Repositories

이 챕터는 JPA 리파지토리지원에 대한 특별한 점들을 얘기해볼까합니다. 이것은 Working with Spring Data Repositories에서 설명했던 핵심 리파지토리 지원에서 시작합니다(빌드를 의역). 그러니, 저기서 설명한 기본 개념을 충분히 이해하시고 시작하겠습니다.

This chapter will point out the specialties for repository support for JPA. This builds on the core repository support explained in Working with Spring Data Repositories. So make sure you’ve got a sound understanding of the basic concepts explained there.

4.1. 소개
4.1. Introduction

4.1.1. 스프링 네임스페이스
4.1.1. Spring namespace

스프링 데이터의 JPA모듈은 커스텀 네임스페이스를 가지고 있어서 리파지토리 빈을 정의하는 것을 허용합니다.이것은 또한 JPA에 좀 더 특별화되어있는 어떤 특징과, 요소 속성들을 포함하고 있습니다. 일반적으로 JPA리파지토리는 repositories 요소를 사용하여 설정가능합니다 :

The JPA module of Spring Data contains a custom namespace that allows defining repository beans. It also contains certain features and element attributes that are special to JPA. Generally the JPA repositories can be set up using the repositories element:

Example 35. 네임스페이스를 사용하여 JPA 리파지토리들 설정하기
Example 35. Setting up JPA repositories using the namespace
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:jpa="http://www.springframework.org/schema/data/jpa"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/jpa
    http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

  <jpa:repositories base-package="com.acme.repositories" />

</beans>

이 요소를 사용하여서, 리파지토리 인스턴스 생성하기에서 설명된 스프링 데이터 리파지토리들을 찾습니다. 그 뒤에서는(Beyond That) @Repository가 붙은 모든 빈에서는 영속 예외 전환이 활성화되어서 JPA 영속화 provider에 의해 던져지는 예외들은 스프링의 DataAccessException hierarchy로 전환되게 됩니다.

Using this element looks up Spring Data repositories as described in Creating repository instances. Beyond that it activates persistence exception translation for all beans annotated with @Repository to let exceptions being thrown by the JPA persistence providers be converted into Spring’s DataAccessException hierarchy.

커스텀 네임스페이스 속성
Custom namespace attributes

repositories 요소 다음에 , JPA 네임스페이스는 repositories의 준비를 하며 세부적인 control 을 얻는 추가적인 속성을 제공합니다. :

Beyond the default attributes of the repositories element the JPA namespace offers additional attributes to gain more detailed control over the setup of the repositories:

표2. 리파지토리 요소의 커스텀 JPA-특정 속성
Table 2. Custom JPA-specific attributes of the repositories element

entity-manager-factory-ref

명시적으로 EntityManagerFactory를 wire하면서, repositories요소에 의해 감지되는 리포지들과 함께 사용될 수 있습니다. 만약 여러개의 EntityManagerFactory빈이 어플리케이션이 사용될 때 보통 사용됩니다. 이것이 설정되어있지 않다면, 우리는 자동적으로 EntityManagerFactoryApplicationContext에서 entityManagerFactory의 이름으로 찾습니다.

Explicitly wire the EntityManagerFactory to be used with the repositories being detected by the repositories element. Usually used if multiple EntityManagerFactory beans are used within the application. If not configured we will automatically lookup the EntityManagerFactory bean with the name entityManagerFactory in the ApplicationContext.

transaction-manager-ref

repositories요소에 의해 감지되는 리파지도리들과 함께 사용되는 PlatformTransactionManager를 wire 합니다. 보통 여러개의 트랜잭션 매니저 와/아니면(and/or) EntityManagerFactoryq빈이 설정되었을 때 필요로 합니다. 기본값은 현재 ApplicationContext 안에 정의된 단일 PlatformTransactionManager입니다.

Explicitly wire the PlatformTransactionManager to be used with the repositories being detected by the repositories element. Usually only necessary if multiple transaction managers and/or EntityManagerFactory beans have been configured. Default to a single defined PlatformTransactionManager inside the current ApplicationContext.

만약 어떠한 명시적 transaction-manager-ref가 정의되지 않았다면, transactionManager 로 이름지어진 PlatformTransactionManager빈이 있어야한다는 것을 알아둡시다.

Note that we require a PlatformTransactionManager bean named transactionManager to be present if no explicit transaction-manager-ref is defined.

4.1.2. 어노테이션 기반 설정
4.1.2. Annotation based configuration

스프링 데이터 리파지토리는 XML네임스페이스뿐만이 아니라 어노테이션 기반의 자바설정을 통해서도 설정을 지원합니다.

The Spring Data JPA repositories support cannot only be activated through an XML namespace but also using an annotation through JavaConfig.

Example 36. 자바설정을 이용한 스프링 데이터 JPA리자피토리 설정
Example 36. Spring Data JPA repositories using JavaConfig
@Configuration
@EnableJpaRepositories
@EnableTransactionManagement
class ApplicationConfig {

  @Bean
  public DataSource dataSource() {

    EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
    return builder.setType(EmbeddedDatabaseType.HSQL).build();
  }

  @Bean
  public EntityManagerFactory entityManagerFactory() {

    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    vendorAdapter.setGenerateDdl(true);

    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    factory.setJpaVendorAdapter(vendorAdapter);
    factory.setPackagesToScan("com.acme.domain");
    factory.setDataSource(dataSource());
    factory.afterPropertiesSet();

    return factory.getObject();
  }

  @Bean
  public PlatformTransactionManager transactionManager() {

    JpaTransactionManager txManager = new JpaTransactionManager();
    txManager.setEntityManagerFactory(entityManagerFactory());
    return txManager;
  }
}

방금 보여드린 설정 클래스는 spring-jdbc 의 EmbeddedDatabaseBuilder API 를 사용하여 내장 HSQL 데이터베이스를 설정합니다. 우리는 그 후에 EntityManagerFactory를 설정하고 Hinbernate 를 샘플 영속 provider로 사용합니다. 마지막 인프라스트럭쳐 컴포넌트는 여기서 JpaTransactionManager로 선언되었습니다. 우리는 마침내 Spring data jpa 리파지토리들을, XML 네임스페이스가 필수적으로 가지고 있는 속성과 같은 @EnableJpaRepositories 어노테이션을 사용하여 활성화시켰습니다.

The just shown configuration class sets up an embedded HSQL database using the EmbeddedDatabaseBuilder API of spring-jdbc. We then set up a EntityManagerFactory and use Hibernate as sample persistence provider. The last infrastructure component declared here is the JpaTransactionManager. We finally activate Spring Data JPA repositories using the @EnableJpaRepositories annotation which essentially carries the same attributes as the XML namespace does. If no base package is configured it will use the one the configuration class resides in.

4.2. 엔티티들 영속화하기(Persisting)
4.2. Persisting entities

4.2.1. 엔티티들 저장하기(Saving)
4.2.1. Saving entities

엔티티를 저장하는 것은 CrudRepository.save(…)-메소드를 통해서 이뤄질 수 있습니다. 이것은 JPA EntityManager를 사용하여 주어진 엔티티를 persist하거나 merge할 것입니다. 만약에 엔티티가 아직 persisted되지 않았다면, 스프링 데이터 JPA는 entityManager.persist(…)메소드에 대한 호출을 통해, 엔티티를 저장할 것입니다. 그렇지 않으면 entityManager.merge(…)메소드가 호출될 것입니다.

Saving an entity can be performed via the CrudRepository.save(…)-Method. It will persist or merge the given entity using the underlying JPA EntityManager. If the entity has not been persisted yet Spring Data JPA will save the entity via a call to the entityManager.persist(…) method, otherwise the entityManager.merge(…) method will be called.

엔티티 상태 감지 전략
Entity state detection strategies

스프링 데이터 JPA는 다음 전략을 사용하여서 엔티티가 새로운 것인지 아닌지 감지합니다 :

Spring Data JPA offers the following strategies to detect whether an entity is new or not:

표3. 스프링 데이터 JPA 에서 엔티티가 새로운 것인지 아닌지 감지하는 것을 위한 옵션들
Table 3. Options for detection whether an entity is new in Spring Data JPA

Id-Property inspection (default)

기본적으로 스프링 데이터 JPA에서는 주어진 엔티티의 식별자 속성을 검사를 합니다.(역주 : ID겠지..?) 만약 식별자 속성이 null이라면, 엔티티는 new 라고 가정될 것이고 그렇지 않으면 new가 아닙니다 .

By default Spring Data JPA inspects the identifier property of the given entity. If the identifier property is null, then the entity will be assumed as new, otherwise as not new.

Implementing Persistable

만약 엔티티가 Persistable을 구현한다면, Spring Data JPA 는 엔티티의 isNew(…)메소드에게 새로운 감지를 위임할 것입니다.세부사항은 JavaDoc을 보시길 바랍니다..

If an entity implements Persistable, Spring Data JPA will delegate the new detection to the isNew(…) method of the entity. See the JavaDoc for details.

Implementing EntityInformation

당신은 JpaRepositoryFactory의 하위클래스를 생성하고 getEntityInformation(…)메소드를 오버라이딩함으로써, SimpleJpaRepository구현체에서 쓰이는 EntityInformation 추상화를 커스터마이징 할 수가 있습니다. 그리고 당신은 JpaRepositoryFactory의 커스텀 구현체를 스프링 빈으로 등록해야 합니다. 이것은 거의 필요로 하지 않는 다는 것을 알아둡시다. 더 많은 정보가 필요로 하면 JavaDoc을 봅시다.

You can customize the EntityInformation abstraction used in the SimpleJpaRepository implementation by creating a subclass of JpaRepositoryFactory and overriding the getEntityInformation(…) method accordingly. You then have to register the custom implementation of JpaRepositoryFactory as a Spring bean. Note that this should be rarely necessary. See the JavaDoc for details.

4.3. 쿼리 메소드들
4.3. Query methods

4.3.1. 쿼리 탐색 전략
4.3.1. Query lookup strategies

JPA 모듈은 쿼리를 수동으로 문자열이나, 메소드네임에서 유추하는 방식으로 정의하는 것을 지원합니다.

The JPA module supports defining a query manually as String or have it being derived from the method name.

쿼리들 선언
Declared queries

비록 메소드 네임으로 쿼리를 얻는 것이 편리하지만, 어떤 경우에는 메소드네임 파서가 사용하고자 하는 키워드를 지원하지 않거나, 메소드 네임이 불필요하게 보기 불편해질 때가 있을 것입니다. 그럴때 당신은 JPA named qeury 를 사용하거나(더 많은 정보는 Using JPA NamedQueries를 참조 ) 당신의 쿼리 메소드에 @Query어노테이션을 붙일 수도 있을 것입니다.(더 많은 정보 : Using @Query 참조)

Although getting a query derived from the method name is quite convenient, one might face the situation in which either the method name parser does not support the keyword one wants to use or the method name would get unnecessarily ugly. So you can either use JPA named queries through a naming convention (see Using JPA NamedQueries for more information) or rather annotate your query method with @Query (see Using @Query for details).

4.3.2. 쿼리 생성
4.3.2. Query creation

일반적으로 JPA 에서 동작하는 쿼리 생성 메커니즘은 쿼리메소드 - 여기에 설명되어있습니다. 여기 JPA 쿼리 메소드 변환이 이뤄지는 짧은 예제가 있습니다. :

Generally the query creation mechanism for JPA works as described in Query methods. Here’s a short example of what a JPA query method translates into:

예제 37. 메소드 이름으로 쿼리 생성
Example 37. Query creation from method names
public interface UserRepository extends Repository<User, Long> {

  List<User> findByEmailAddressAndLastname(String emailAddress, String lastname);
}

우리는 여기서 JPA 크리테리아 API를 이용하여서 쿼리를 생성할 것입니다. 하지만 기본적으로 이 전환은 다음 쿼리로 될 것입니다. select u from User u where u.emailAddress = ?1 and u.lastname = ?2. 스프링 데이터 JPA는 프로퍼티 검사를 할 것이며 Property expressions에 설명된 중첩 프로퍼티들을 횡단(역주: 횡단 검사한다. 필드를 짜맞춰본다 이런뜻인듯하다. )해볼 것입니다. 여기에 JPA를 위해 지원되는 키워드들에 대한 오버뷰와 기본적으로 전환되는 키워드들을 포함하고 있는 메소드에 대한 오버뷰가 나옵니다.

We will create a query using the JPA criteria API from this but essentially this translates into the following query: select u from User u where u.emailAddress = ?1 and u.lastname = ?2. Spring Data JPA will do a property check and traverse nested properties as described in Property expressions. Here’s an overview of the keywords supported for JPA and what a method containing that keyword essentially translates to.

표 4. 메소드 이름 안에서 지원되는 키워드들
Table 4. Supported keywords inside method names
Keyword Sample JPQL snippet

And

findByLastnameAndFirstname

… where x.lastname = ?1 and x.firstname = ?2

Or

findByLastnameOrFirstname

… where x.lastname = ?1 or x.firstname = ?2

Is,Equals

findByFirstname,findByFirstnameIs,findByFirstnameEquals

… where x.firstname = 1?

Between

findByStartDateBetween

… where x.startDate between 1? and ?2

LessThan

findByAgeLessThan

… where x.age < ?1

LessThanEqual

findByAgeLessThanEqual

… where x.age ⇐ ?1

GreaterThan

findByAgeGreaterThan

… where x.age > ?1

GreaterThanEqual

findByAgeGreaterThanEqual

… where x.age >= ?1

After

findByStartDateAfter

… where x.startDate > ?1

Before

findByStartDateBefore

… where x.startDate < ?1

IsNull

findByAgeIsNull

… where x.age is null

IsNotNull,NotNull

findByAge(Is)NotNull

… where x.age not null

Like

findByFirstnameLike

… where x.firstname like ?1

NotLike

findByFirstnameNotLike

… where x.firstname not like ?1

StartingWith

findByFirstnameStartingWith

… where x.firstname like ?1 (parameter bound with appended %)

EndingWith

findByFirstnameEndingWith

… where x.firstname like ?1 (parameter bound with prepended %)

Containing

findByFirstnameContaining

… where x.firstname like ?1 (parameter bound wrapped in %)

OrderBy

findByAgeOrderByLastnameDesc

… where x.age = ?1 order by x.lastname desc

Not

findByLastnameNot

… where x.lastname <> ?1

In

findByAgeIn(Collection<Age> ages)

… where x.age in ?1

NotIn

findByAgeNotIn(Collection<Age> age)

… where x.age not in ?1

True

findByActiveTrue()

… where x.active = true

False

findByActiveFalse()

… where x.active = false

IgnoreCase

findByFirstnameIgnoreCase

… where UPPER(x.firstame) = UPPER(?1)

In 이나 NotIn은 또한 Collection의 하위클래스를 받거나 배열, 가변인자들을 받을 수 있습니다. 같은 논리적 연산 검사에 대한 다른 문법적인 내용들은 Repository query keywords를 보시길 바랍니다..

In and NotIn also take any subclass of Collection as parameter as well as arrays or varargs. For other syntactical versions of the very same logical operator check Repository query keywords.

4.3.3. JPA NamedQuery들 사용하기
4.3.3. Using JPA NamedQueries

이 예제는 간단하게 <named-query />요소와 @NamedQuery어노테이션을 사용합니다. 이 설정 요소들을 위한 쿼리는 JPA 쿼리 언어로 정의되어야 합니다. 물론 당신은 <named-native-query />@NamedNativeQuery또한 사용할 수 있습니다. 이러한 요소들은 데이터베이스 플랫폼 독립성을 잃으면서 , native SQL 문법을 사용하게 합니다.

The examples use simple <named-query /> element and @NamedQuery annotation. The queries for these configuration elements have to be defined in JPA query language. Of course you can use <named-native-query /> or @NamedNativeQuery too. These elements allow you to define the query in native SQL by losing the database platform independence.

XML named query 정의
XML named query definition

XML 설정을 단순히 사용하기 위해서 필요한 <named-query /> 요소를 클래스패스의 META-INF내에 위치한 orm.xml JPA 설정 파일에 넣어주세요. named query들의 자동적인 실행이 이름 컨벤션에 의해서 활성화됩니다. 더 많은 정보는 다음과 같습니다.

To use XML configuration simply add the necessary <named-query /> element to the orm.xml JPA configuration file located in META-INF folder of your classpath. Automatic invocation of named queries is enabled by using some defined naming convention. For more details see below.

Example 38. XML 네임드 쿼리 설정
Example 38. XML named query configuration
<named-query name="User.findByLastname">
  <query>select u from User u where u.lastname = ?1</query>
</named-query>

당신이 보듯이 query 는 런타임에서 resolve 되는데 사용되는 특수한 이름을 가지고 있어야 합니다.

As you can see the query has a special name which will be used to resolve it at runtime.

어노테이션 설정
Annotation configuration

어노테이션 설정은 (아마 유지보수 비용이 줄어들 것같은) 다른 설정 파일이 필요하지 않다는 장점이 있습니다. 당신은 그러한 장점을 위해서, 각각의 새로운 쿼리 선언을 할 때마다 당신의 도메인 클래스를 재컴파일 해줄 필요가 있습니다.

Annotation configuration has the advantage of not needing another configuration file to be edited, probably lowering maintenance costs. You pay for that benefit by the need to recompile your domain class for every new query declaration.

예제 39. 어노테이션 기반의 named query 설정
Example 39. Annotation based named query configuration
@Entity
@NamedQuery(name = "User.findByEmailAddress",
  query = "select u from User u where u.emailAddress = ?1")
public class User {

}
인터페이스들 선언하기
Declaring interfaces

이러한 named query 를 실행하기 위해서는 당신이 해야할 것은 UserRepository에 다음과 같이 명시해주는 것입니다. :

To allow execution of these named queries all you need to do is to specify the UserRepository as follows:

Example 40. UserRepository 에 선언된 쿼리 메소드
Example 40. Query method declaration in UserRepository
public interface UserRepository extends JpaRepository<User, Long> {

  List<User> findByLastname(String lastname);

  User findByEmailAddress(String emailAddress);
}

스프링 데이터는 이러한 namedquery에 대한 메소드들의 호출을 해석할 것입니다. 설정된 도메인 클래스의 간단한 이름과 함께 시작하기 위해, 다음의 메소드 이름은 dot(.)으로 분리됩니다. 그래서 여기의 예제에서는 메소드 이름으로 쿼리를 만드는 대신에 위의 정의된 named query를 사용하였습니다.

Spring Data will try to resolve a call to these methods to a named query, starting with the simple name of the configured domain class, followed by the method name separated by a dot. So the example here would use the named queries defined above instead of trying to create a query from the method name.

4.3.4. @Query 사용하기
4.3.4. Using @Query

엔티티들을 위해서 쿼리들을 선언하는 named 쿼리를 사용하는 것은 적은 수의 쿼리들에 대해 잘 동작하는 유효한 접근입니다. 쿼리 그들 스스로가 그들을 실행하는 자바메소드로 묶이는데, 당신은 실제적으로 Spring data jpa의 @Query메소드들 통해서 그들을 도메인 클래스에 어노테이션 하지 않고 직접적으로 바인딩할수가 있습니다. 이것은 도메인 클래스에서 영속 특징을 가진 정보를 없애게 해주며, 쿼리를 리파지토리 인터페이스근처로 위치하게 해줍니다.

Using named queries to declare queries for entities is a valid approach and works fine for a small number of queries. As the queries themselves are tied to the Java method that executes them you actually can bind them directly using the Spring Data JPA @Query annotation rather than annotating them to the domain class. This will free the domain class from persistence specific information and co-locate the query to the repository interface.

query method에 어노테이션된 쿼리들은 @NamedQueryorm.xml에 선언된 namedquery 에 비해서 우선시(precendence)됩니다.

Queries annotated to the query method will take precedence over queries defined using @NamedQuery or named queries declared in orm.xml.

Example 41. @Query를 사용하여서 쿼리메소드에 쿼리 선언하기
Example 41. Declare query at the query method using @Query
public interface UserRepository extends JpaRepository<User, Long> {

  @Query("select u from User u where u.emailAddress = ?1")
  User findByEmailAddress(String emailAddress);
}

LIKE같이 advanced한 표현을 사용해볼까요. 수동적으로 @Query를 사용해 정의된 쿼리를 위한, 쿼리 실행 메커니즘은 쿼리 정의 내에서 LIKE 표현의 정의를 허용합니다. (역주 : 영어표현에 군살이 많은데 그걸 그대로 옮겨적으면서 하다보니 보일러 플레이트한^^; 표현이 많은 것같기도 한 느낌이 듭니다. 가끔 주어없는 문장도 나오고, 오타도 있고 뭐..번역하기 애매한.. 그런것도 많아요.. 그냥 번역하다가 궁시렁 궁시렁 해봅니다.)

Using advanced LIKE expressionsThe query execution mechanism for manually defined queries using @Query allow the definition of advanced LIKE expressions inside the query definition.

Example 42. @Query 내에서 like같은 고급 표현
Example 42. Advanced like-expressions in @Query
public interface UserRepository extends JpaRepository<User, Long> {

  @Query("select u from User u where u.firstname like %?1")
  List<User> findByFirstnameEndsWith(String firstname);
}

방금전에 본 LIKE 구별자 문자 %가 인식이 되면서, 유효한 JPQL쿼리(%를 삭제하면서)로 변형되는 쿼리가 되게 됩니다. 쿼리 실행동안 메소드 호출쪽에 전해지는, 파라미터가 전에 인식된 LIKE패턴에 아규먼트화됩니다. (역주 : 파라미터가 like쪽에 간다. 이말인듯 ;; )

In the just shown sample LIKE delimiter character % is recognized and the query transformed into a valid JPQL query (removing the %). Upon query execution the parameter handed into the method call gets augmented with the previously recognized LIKE pattern.

네이티브 쿼리들. @Query 어노테이션은 nativeQuery를 true로 설정해서 네이티브 쿼리의 실행을 허용합니다. 우리는 현재 네이티브 쿼리를 위한 페이지네이션과 동적 정렬을 지원하지 않는다는 것을 알아둡시다. (우리가 실제 선언된 쿼리를 만들어야하고, 우리는 Native SQL에 확실하게 이것-동적정렬, 페이징을 할수 없기 때문에 )

Native queriesThe @Query annotation allows to execute native queries by setting the nativeQuery flag to true. Note, that we currently don’t support execution of pagination or dynamic sorting for native queries as we’d have to manipulate the actual query declared and we cannot do this reliably for native SQL.

Example 43. @Query를 사용하여 쿼리 메소드에서 네이티브 쿼리 선언하기
Example 43. Declare a native query at the query method using @Query
public interface UserRepository extends JpaRepository<User, Long> {

  @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?0", nativeQuery = true)
  User findByEmailAddress(String emailAddress);
}

4.3.5. Using named parameters

기본적으로 Spring Data JPA 는 예전의 샘플에서도 설명했듯이, 파라미터 바인딩에 기반한 포지션을 사용합니다. 이것은 파라미터 포지션에 대해서 쿼리 메소드가 리팩토링해야할 작은 에러들을 만들기 쉽게 합니다. 이것을 해결하기 위해서 당신은 @Param 어노테이션을 이용하여서, 메소드 파라미터에 구체적인 이름을 주고 쿼리에 그 이름으로 바인딩시킬 수가 있습니다.

By default Spring Data JPA will use position based parameter binding as described in all the samples above. This makes query methods a little error prone to refactoring regarding the parameter position. To solve this issue you can use @Param annotation to give a method parameter a concrete name and bind the name in the query.

Example 44. named parameter 이용하기
Example 44. Using named parameters
public interface UserRepository extends JpaRepository<User, Long> {

  @Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")
  User findByLastnameOrFirstname(@Param("lastname") String lastname,
                                 @Param("firstname") String firstname);
}

정의된 쿼리의 발생에 따라 메소드 파라미터가 교환됩니다.

Note that the method parameters are switched according to the occurrence in the query defined.

4.3.6. SpEL 표현 사용하기
4.3.6. Using SpEL expressions

Spring data 1.4 버젼부터 우리는 제한된 SpEL템플릿 표현을 @Query를 통해서 수동으로 정해진 쿼리에, 사용할 수 있게 하였습니다. 쿼리가 실행되면서 이러한 표현들은 미리 정의된 변수들의 집합에 대하여 평가됩니다. 우리는 수동쿼리에서 사용될 다음 변수들의 리스트를 지원합니다.

As of Spring Data JPA release 1.4 we support the usage of restricted SpEL template expressions in manually defined queries via @Query. Upon query execution these expressions are evaluated against a predefined set of variables. We support the following list of variables to be used in a manual query.

Table 5. 쿼리 템플릿 기반에서 SpEL 안의 지원되는 변수들
Table 5. Supported variables inside SpEL based query templates
변수 사용 설명

entityName

select x from #{#entityName} x

주어진 repository에 관련된 도메인 타입의 entityName 를 삽입하세요. entityName는 다음과 같이 해석됩니다 : 만약 @Entity어노테이션에서 도메인 타입 이름을 정한다면 , 그것은 사용될 것입니다. 그렇지 않으면 도메인 타입의 간단한 class-name이 사용될 것입니다. (역주: 그냥 뒤의 예제들을 조금 살펴보자^^; )

Inserts the entityName of the domain type associated with the given Repository. The entityName is resolved as follows: If the domain type has set the name property on the @Entity annotation then it will be used. Otherwise the simple class-name of the domain type will be used.

다음 예제는 쿼리 문자열에서 #{#entityName} 표현의 사용 예제를 하나 보여줍니다. (일부러 짧은 표현 쓰다.) @Query어노테이션의 쿼리 문자열에서 실제적인 엔티티 이름을 기술하지 않기 위해서 #{#entityName}변수를 쓴 것입니다.

The following example demonstrates one use case for the #{#entityName} expression in a query string where you want to define a repository interface with a query method with a manually defined query. In order not to have to state the actual entity name in the query string of a @Query annotation one can use the #{#entityName} Variable.

entityName@Entity어노테이션을 통해 커스터마이징 됩니다. orm.xml를 통한 커스텀화는 SpEL표현에서 지원되지 않습니다.

The entityName can be customized via the @Entity annotation. Customizations via orm.xml are not supported for the SpEL expressions.

Example 45. entityName - 리파지토리 쿼리 메소드에서 SpEl표현을 사용하기
Example 45. Using SpEL expressions in repository query methods - entityName
@Entity
public class User {

  @Id
  @GeneratedValue
  Long id;

  String lastname;
}

public interface UserRepository extends JpaRepository<User,Long> {

  @Query("select u from #{#entityName} u where u.lastname = ?1")
  List<User> findByLastname(String lastname);
}

물론 당신은 쿼리 선언에서 직접적으로 User 를 직접적으로 사용했을 것입니다만 이것은 쿼리를 변경할 것을 요구합니다.(역주: 나중에 변경한다는 얘기인듯?).. #entityName는 미래에 잠재적으로, 다른 엔티티 이름을 가지는(예를 들자면 @Entity(name = "MyUser"를 사용하는) User클래스의 재매핑을 선택합니다.

Of course you could have just used User in the query declaration directly but that would require you to change the query as well. The reference to #entityName will pick up potential future remappings of the User class to a different entity name (e.g. by using @Entity(name = "MyUser").

쿼리 문자열에서 다른 #{#entityName} 표현의 유스케이스는, 만약 당신이 제너릭 리파지토리 인터페이스를 구체적인 도메인 타입을 가진 특별화된 리파지토리 인터페이스와 같이 사용하고 싶을 때입니다. 구체적인 인터페이스에서 커스텀 쿼리 메소드들의 반복적인 정의를 피하기 위해, 당신은 제너릭 리파지토리 인터페이스의 @Query어노테이션의, 쿼리문자열에서- 엔티티네임 표현을 사용할 수가 있습니다.

Another use case for the #{#entityName} expression in a query string is if you want to define a generic repository interface with specialized repository interfaces for a concrete domain type. In order not to have to repeat the definition of custom query methods on the concrete interfaces you can use the entity name expression in the query string of the @Query annotation in the generic repository interface.

Example 46. SpEL 표현을 리파지토리 쿼리 메소드에서 사용하기 - entityName with inheritance
Example 46. Using SpEL expressions in repository query methods - entityName with inheritance
@MappedSuperclass
public abstract class AbstractMappedType {
  …
  String attribute
}

@Entity
public class ConcreteType extends AbstractMappedType { … }

@NoRepositoryBean
public interface MappedTypeRepository<T extends AbstractMappedType>
  extends Repository<T, Long> {

  @Query("select t from #{#entityName} t where t.attribute = ?1")
  List<T> findAllByAttribute(String attribute);
}

public interface ConcreteRepository
  extends MappedTypeRepository<ConcreteType> { … }

이 예제에서 MappedTypeRepository 인터페이스는, AbstractMappedType를 상속하는 적은 도메인들을 위한 평범한 부모 인터페이스입니다. 이것은 또한 제너릭 메소드findAllByAttribute(…)를 정의하여 이것은 구체화된 리파지토리 인터페이스의 인스턴스에서 사용될 수 있습니다. 만약 당신이 ConcreteRepository에서의 findByAllAttribute(…)를 실행시킨다면, 실행되는 쿼리는 select t from ConcreteType t where t.attribute = ?1가 될 것입니다.

In the example the interface MappedTypeRepository is the common parent interface for a few domain types extending AbstractMappedType. It also defines the generic method findAllByAttribute(…) which can be used on instances of the specialized repository interfaces. If you now invoke findByAllAttribute(…) on ConcreteRepository the query being executed will be select t from ConcreteType t where t.attribute = ?1.

4.3.7. 쿼리 수정하기 - Modifying queries
4.3.7. Modifying queries

위의 모든 섹션들은 어떻게 주어진 엔티티나 엔티티들의 콜렉션에 접근하는 쿼리들을 선언하는 지를 설명합니다. 물론 당신은 Custom implementations for Spring Data repositories에 설명한 기능들을 이용하여서 커스텀 수정 행동을 추가할 수도 있습니다. 이러한 접근은 포괄적인 커스텀 기능에서 실현가능하지만, 당신은 실제로, 쿼리 메소드에 @Modifying로 어노테이션 하면서, 오직 파라미터 바인딩만을 필요로 하는 수정쿼리의 실행을 할 수 있습니다.

All the sections above describe how to declare queries to access a given entity or collection of entities. Of course you can add custom modifying behaviour by using facilities described in Custom implementations for Spring Data repositories. As this approach is feasible for comprehensive custom functionality, you can achieve the execution of modifying queries that actually only need parameter binding by annotating the query method with @Modifying:

Example 47. manipuling 쿼리들의 정의
Example 47. Declaring manipulating queries
@Modifying
@Query("update User u set u.firstname = ?1 where u.lastname = ?2")
int setFixedFirstnameFor(String firstname, String lastname);

이것은 하나를 선택하는 대신에 updating하는 쿼리를 동작시킵니다. EntityManager가 아마 수정쿼리의 실행한 이후에 오래된 엔티리를 가지고 있는 까닭에, 우리는 자동적으로 clear it 할 필요가 없습니다. (세부적인 사항을 보려면 자바Doc의 EntityManager.clear() 를 보세요). 이것은 EntityManager안에서 여전히 대기중인 모든 flush되지 않은 변화들을 효과적으로 drop할 것이기 때문입니다. 만약 당신이 EntityManager가 자동적으로 cleared되기를 원한다면, 당신은 @Modifying어노테이션의 clearAutomatically속성을 true로 하면 됩니다. (역주: 조회수 같은 것 올릴 때 이 속성을 걸고 할 때가 있었다. 테스트하면서 이상하게 안 걸리는 경우가 있었는데 명시적으로 이 옵션을 true까지 다 주니까 되었던 기억이 남;; )

This will trigger the query annotated to the method as updating query instead of a selecting one. As the EntityManager might contain outdated entities after the execution of the modifying query, we do not automatically clear it (see JavaDoc of EntityManager.clear() for details) since this will effectively drop all non-flushed changes still pending in the EntityManager. If you wish the EntityManager to be cleared automatically you can set @Modifying annotation’s clearAutomatically attribute to true.

4.3.8. 쿼리 힌트 적용
4.3.8. Applying query hints

당신의 리파지토리 인터페이스에 선언된 쿼리에 쿼리 힌트를 적용하기 위해, @QueryHints어노테이션을 사용할 수가 있습니다. 이것은 JPA @QueryHint 의 배열을 사용하고, 잠재적으로 힌트를 사용하지 않도록 하는 boolean flag가 있습니다. 페이지네이션을 적용할 때 발동되는 카운트 쿼리에 이 flag는 적용이 됩니다. (역주 : 맞는 번역인지 하는 고민)

To apply JPA query hints to the queries declared in your repository interface you can use the @QueryHints annotation. It takes an array of JPA @QueryHint annotations plus a boolean flag to potentially disable the hints applied to the addtional count query triggered when applying pagination.

Example 48. repository 메소드와 함께 QueryHints 사용하기
Example 48. Using QueryHints with a repository method
public interface UserRepository extends Repository<User, Long> {

  @QueryHints(value = { @QueryHint(name = "name", value = "value")},
              forCounting = false)
  Page<User> findByLastname(String lastname, Pageable pageable);
}

방금 보여진 선언은 실제 쿼리를 위하여, 설정된 @QueryHint에 적용될 수 있습니다. 하지만 전체 페이지 숫자를 계산하기 위해 발동되는 카운트쿼리는 생략될 수 있습니다. (역주: 확인필요)

The just shown declaration would apply the configured @QueryHint for that actually query but omit applying it to the count query triggered to calculate the total number of pages.

4.3.9. Fetch- and LoadGraphs 설정
4.3.9. Configuring Fetch- and LoadGraphs

JPA 2.1 스펙은 Fetch- 와 LoadGraphs에 대한 지원을 소개합니다. 우리는 @NamedEntityGraph 정의에 대한 참조를 허용하는 @EntityGraph어노테이션을 통해서 LoadGraph를 지원합니다. @NamedEntityGraph는 엔티에 어노테이션되어서 resulting쿼리에 대한 fetch plan을 설정하는데 사용됩니다. fetching의 타입 (Fetch/Load) 는 @EntityGraph어노테이션의 type 속성을 통해서 설정될 수 있습니다. 더 나은 레퍼런스를 위해서 JPA 2.1 Spec 3.7.4를 살펴보시길 바랍니다.

The JPA 2.1 specification introduced support for specifiying Fetch- and LoadGraphs that we also support via the @EntityGraph annotation which allows to reference a @NamedEntityGraph definition, that can be annotated on an entity, to be used to configure the fetch plan of the resulting query. The type (Fetch / Load) of the fetching can be configured via the type attribute on the @EntityGraph annotation. Please have a look at the JPA 2.1 Spec 3.7.4 for further reference.

Example 49. 엔티티에서 엔티티 그래프의 이름정의하기
Example 49. Defining a named entity graph on an entity.
@Entity
@NamedEntityGraph(name = "GroupInfo.detail",
  attributeNodes = @NamedAttributeNode("members"))
public class GroupInfo {

  // default fetch mode is lazy.
  @ManyToMany
  List<GroupMember> members = new ArrayList<GroupMember>();

  …
}
Example 50. repository 쿼리 메소드에서 이름지어진 엔티티 그래프 정의 참조하기
Example 50. Referencing a named entity graph definition on an repository query method.
@Repository
public interface GroupRepository extends CrudRepository<GroupInfo, String> {

  @EntityGraph(value = "GroupInfo.detail", type = EntityGraphType.LOAD)
  GroupInfo getByGroupName(String name);

}

4.4. 스토어 프로시져 - Stored procedures
4.4. Stored procedures

JPA 2.1 스펙은 JPA 크리테리아 API를 통한 스토어 프로시져 호출을 지원을 소개합니다. 우리는 리파지토리 메소드에서 저장된 프로시져 메타데이터를 선언하는 @Procedure어노테이션을 소개했었습니다.

The JPA 2.1 specification introduced support for calling stored procedures via the JPA criteria query API. We Introduced the @Procedure annotation for declaring stored procedure metadata on a repository method.

Example 51. HSQL DB 에서 pus1inout procedure 정의 하기
Example 51. The definition of the pus1inout procedure in HSQL DB.
/;
DROP procedure IF EXISTS plus1inout
/;
CREATE procedure plus1inout (IN arg int, OUT res int)
BEGIN ATOMIC
 set res = arg ` 1;
END
/;

스토어 프로시져를 위한 메타데이터는 엔티티 타입에 NamedStoredProcedureQuery어노테이션을 통해서 설정 될 수 있습니다.

Metadata for stored procedures can be configured via the NamedStoredProcedureQuery annotation on an entity type.

Example 52. 엔티티에서의 StoredProcedure 메타데이터 정의
Example 52. StoredProcedure metadata definitions on an entity.
@Entity
@NamedStoredProcedureQuery(name = "User.plus1", procedureName = "plus1inout", parameters = {
  @StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class),
  @StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class) })
public class User {}

스토어 프로시져는 다양한 방법으로 리파지토리 메소드에서 참조될 수 있습니다. 호출되는 stored procedure 는 value@ProcedureprocedureName속성을 통해 직접적으로 정의될 수 있기도하고, 아니면 name 속성을 통해 간접적으로 정의되기도 합니다. 어떠한 이름도 설정되지 않으면 리파지토리 메소드 이름이 fallback으로 사용됩니다.

Stored procedures can be referenced from a repository method in multiple ways. The stored procedure to be called can either be defined directly via the value or procedureName attribute of the @Procedure annotation or indirectly via the name attribute. If no name is configured the name of the repository method is used as a fallback.

Example 53. 데이터베이스에 명시적으로 매핑된 프로시져 "plus1inout" 참조
Example 53. Referencing explicitly mapped procedure with name "plus1inout" in database.
@Procedure("plus1inout")
Integer explicitlyNamedPlus1inout(Integer arg);
Example 54. 데이터베이스에 암시적으로 매핑된 프로시져 "plus1inout" 을 통해 procedureName alias.으로 참조
Example 54. Referencing implicitly mapped procedure with name "plus1inout" in database via procedureName alias.
@Procedure(procedureName = "plus1inout")
Integer plus1inout(Integer arg);
Example 55. 엔티티 매니저에서 명시적으로 매핑된 "User.plus1IO" stored procedure를 참조
Example 55. Referencing explicitly mapped named stored procedure "User.plus1IO" in EntityManager.
@Procedure(name = "User.plus1IO")
Integer entityAnnotatedCustomNamedProcedurePlus1IO(@Param("arg") Integer arg);
Example 56. 암시적으로 엔티티 매니저에 매핑된 stored procedure "User.plus1" 를 메소드 네임을 통해 참조
Example 56. Referencing implicitly mapped named stored procedure "User.plus1" in EntityManager via method-name.
@Procedure
Integer plus1(@Param("arg") Integer arg);

4.5. 스페시피케이션 - Specifications(원문 그대로 가자)
4.5. Specifications

JPA 2 는 프로그램적으로 쿼리를 만들어내는 크리테리아 API를 소개합니다. criteria를 쓰는 것은 당신이 실제로 도메인 클래스를 위해 쿼리의 where-구문을 정의한다는 것입니다. 이러한 크리테리아는 (JPA 크리테리아 API 제약에 의해 설명된)엔티티에 대해 predicate(서술어)로 여겨집니다.

JPA 2 introduces a criteria API that can be used to build queries programmatically. Writing a criteria you actually define the where-clause of a query for a domain class. Taking another step back these criteria can be regarded as predicate over the entity that is described by the JPA criteria API constraints.

스프링 데이터 JPA는 에릭 에반스의 책"Domain Driven Design"에서 specification의 개념을 따와서, 같은 문법을 따라, 크리테리아 API 를 사용하는 그러한 specification을 정의하는 API를 제공하였습니다. specifications를 지원하기 위해, 당신의 리파지토리 인터페이스를 JpaSpecificationExecutor인터페이스로 인터페이스와 함께 상속할 수 있습니다.

Spring Data JPA takes the concept of a specification from Eric Evans' book "Domain Driven Design", following the same semantics and providing an API to define such specifications using the JPA criteria API. To support specifications you can extend your repository interface with the JpaSpecificationExecutor interface:

public interface CustomerRepository extends CrudRepository<Customer, Long>, JpaSpecificationExecutor {
 …
}

추가적인 인터페이스는 당신이 specification 를 다양한 방법으로 하게 해주는 메소드를 가지고 있습니다. 예를 들면 findAll 메소드는 speficiation에 맞는 모든 엔티티를 반환하여 줍니다.

The additional interface carries methods that allow you to execute specifications in a variety of ways. For example, the findAll method will return all entities that match the specification:

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

Specification인터페이스는 다음과 같이 정의합니다. :

The Specification interface is defined as follows:

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

자, 그럼 뭐가 일반적인 유스케이스 일까요? Specification는 엔티티위에서 predicates들의 모음을 확장하게끔 하는 데 쉽게 사용될수 있어서, 매번 필요한 조합을 위한 쿼리를 선언할 필요없이 JpaRepository와 같이 사용되고 조합될 수 있습니다. 여기에 그 예제가 있습니다.

Okay, so what is the typical use case? Specifications can easily be used to build an extensible set of predicates on top of an entity that then can be combined and used with JpaRepository without the need to declare a query (method) for every needed combination. Here’s an example:

Okay, so what is the typical use case? Specifications can easily be used to build an extensible set of predicates on top of an entity that then can be combined and used with JpaRepository without the need to declare a query (method) for every needed combination. Here’s an example:

Example 57. Customer를 위한 Spcifications
Example 57. Specifications for a Customer
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(_Customer.createdAt), 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
      }
    };
  }
}

인정하건대, 상당한 양의 보일러플레이트가 더 나은 향상을 위해 남아있지만(자바8 클로저로 좀더 줄어들기를 바랍니다), 클라이언트사이드는 보시는 바와 같이 좀 더 나아졌습니다. _Customer타입은 JPA 메타모델 generator를 이용하여 생성된메타모델 타입입니다.(see the Hibernate implementation’s documentation for example을 보세요 ) 그래서 이 표현-_Customer.createdAtDatecreatedAt속성을 가지는 Customer를 가정합니다. 그 옆에는 우리는 비즈니스 요구사항 추상화 레벨에서 몇가지 크리테리아와 생성된 실행가능한 Specifications를 표현했습니다. 그럼 클라이언트는 다음과 같이 Specification를 사용할 것입니다. :

Admittedly the amount of boilerplate leaves room for improvement (that will hopefully be reduced by Java 8 closures) but the client side becomes much nicer as you will see below. The _Customer type is a metamodel type generated using the JPA Metamodel generator (see the Hibernate implementation’s documentation for example). So the expression _Customer.createdAt is asuming the Customer having a createdAt attribute of type Date. Besides that we have expressed some criteria on a business requirement abstraction level and created executable Specifications. So a client might use a Specification as follows:

Example 58.단순한 specification 사용하기
Example 58. Using a simple Specification
List<Customer> customers = customerRepository.findAll(isLongTermCustomer());

자, 왜 단순하게 이러한 종류의 데이터 접근을 위해 쿼리를 생성하지 않을까요? 당신이 맞습니다. 단순한 Specification을 사용하는 것은 단순한 평문 쿼리 선언에서는 많은 장점을 얻지 못합니다. specifications의 장점은 그들을 조합해서 새로운 Specification객체를 만들때 나타납니다. 당신은 Specificationshelper 클래스(우리가 다음과 같이 표현을 만들기 위해 제공하는)를 통해서 이러한 점을 달성할 수 있습니다. :

Okay, why not simply create a query for this kind of data access? You’re right. Using a single Specification does not gain a lot of benefit over a plain query declaration. The power of specifications really shines when you combine them to create new Specification objects. You can achieve this through the Specifications helper class we provide to build expressions like this:

Example 59. 조합된 Specifications
Example 59. Combined Specifications
MonetaryAmount amount = new MonetaryAmount(200.0, Currencies.DOLLAR);
List<Customer> customers = customerRepository.findAll(
  where(isLongTermCustomer()).or(hasSalesOfMoreThan(amount)));

당신이 보듯이 SpecificationsSpecification인스턴스를 체인하고 조합하기 위해 몇가지 glue-code 메소드를 제공합니다. 그러므로 당신의 데이터 접근 레이어를 확장하는 것은 단순히 새로운 Specification 구현체들을 만들어, 그들을 이미 존재하는 하나로 조합하는 일이 될 것입니다.

As you can see, Specifications offers some glue-code methods to chain and combine Specification instances. Thus extending your data access layer is just a matter of creating new Specification implementations and combining them with ones already existing.

4.6. Transactionality

리파지토리 인스턴스의 CRUD 메소드들은 기본적으로 transactional합니다. 읽기 작업을 위해서 트랜잭션 설정 readOnly flag가 true로 설정되고, 모든 다른 것들은 평문@Transactional로 설정되어 기본 트랜잭션이 적용되게 됩니다. 세부사항은 CrudRepository의 JavaDoc을 보시길 바랍니다. 만약 당신이 조금 다르게 리파지토리에 선언된 메소드들 중 하나의 트랜잭션 설정을 하고 싶다면, 단순히 다음과 같이 재선언해보세요 :

CRUD methods on repository instances are transactional by default. For reading operations the transaction configuration readOnly flag is set to true, all others are configured with a plain @Transactional so that default transaction configuration applies. For details see JavaDoc of CrudRepository. If you need to tweak transaction configuration for one of the methods declared in a repository simply redeclare the method in your repository interface as follows:

Example 60. CRUD 설정을 위한 커스텀 트랜잭션
Example 60. Custom transaction configuration for CRUD
public interface UserRepository extends CrudRepository<User, Long> {

  @Override
  @Transactional(timeout = 10)
  public List<User> findAll();

  // Further query method declarations
}

이것은 findAll()메소드가 readOnlyflag없이 10초간의 시간제한안에 실행되게 할 것입니다.

This will cause the findAll() method to be executed with a timeout of 10 seconds and without the readOnly flag.

트랜잭션 행동을 변경하는 다른 가능성은 facade나 일반적으로 하나이상의 리파지토리를 다루는 서비스구현체를 사용하는 것입니다. 그것의 목적은 non-CRUD 작업에 대해 트랜잭션 경계선을 정의하는 데 있습니다.

Another possibility to alter transactional behaviour is using a facade or service implementation that typically covers more than one repository. Its purpose is to define transactional boundaries for non-CRUD operations:

Example 61. 여러개의 리파지토리 호출을 위해 트랜잭션을 호출하는 facade 사용하기
Example 61. Using a facade to define transactions for multiple repository calls
@Service
class UserManagementImpl implements UserManagement {

  private final UserRepository userRepository;
  private final RoleRepository roleRepository;

  @Autowired
  public UserManagementImpl(UserRepository userRepository,
    RoleRepository roleRepository) {
    this.userRepository = userRepository;
    this.roleRepository = roleRepository;
  }

  @Transactional
  public void addRoleToAllUsers(String roleName) {

    Role role = roleRepository.findByName(roleName);

    for (User user : userRepository.findAll()) {
      user.addRole(role);
      userRepository.save(user);
    }
}

이것은 addRoleToAllUsers(…)에 대한 호출을 일으키고, 트랜잭션 내부에서 동작하게 할 것입니다. (특별히 존재하는 트랜잭션에 참여시키거나, 아무것도 실행되지 않는다면 새로운 것을 만들것입니다). 이 리파지토리의 트랜잭션 설정은 실제 사용되기로 결정한 다른 바깥 쪽의 트랜잭션 설정때문에 무시(neglected)될 것입니다. 당신은 <tx:annotation-driven />을 활성화시키거나 @EnableTransactionManagement 를 명시적으로 사용하여서 facades가 동작하는 어노테이션 기반의 설정 을 얻어야 합니다. 위의 예제는 당신이 컴포넌트 스캐닝을 한다고 가정합니다.

This will cause call to addRoleToAllUsers(…) to run inside a transaction (participating in an existing one or create a new one if none already running). The transaction configuration at the repositories will be neglected then as the outer transaction configuration determines the actual one used. Note that you will have to activate <tx:annotation-driven /> or use @EnableTransactionManagement explicitly to get annotation based configuration at facades working. The example above assumes you are using component scanning.

4.6.1. Transactional query methods

당신의 쿼리 메소드가 트랜잭션하기 위해 단순히 @Transactional을 레파지토리 인터페이스에 사용하세요.

To allow your query methods to be transactional simply use @Transactional at the repository interface you define.

Example 62. @Transactional 을 쿼리 메소드에 사용하기
Example 62. Using @Transactional at query methods
@Transactional(readOnly = true)
public interface UserRepository extends JpaRepository<User, Long> {

  List<User> findByLastname(String lastname);

  @Modifying
  @Transactional
  @Query("delete from User u where u.active = false")
  void deleteInactiveUsers();
}

일반적으로 대부분의 쿼리가 읽기 전용이기 때문에, 당신은 readOnly flag를 true로 설정하기를 원할 겁니다..이와는 대조적으로 deleteInactiveUsers()@Modifying어노테이션을 사용하여 트랜잭션 설정을 오버라이딩합니다. 그러므로 저 메소드는 readOnlyfalse인 상태로 설정되어서 실행됩니다

Typically you will want the readOnly flag set to true as most of the query methods will only read data. In contrast to that deleteInactiveUsers() makes use of the @Modifying annotation and overrides the transaction configuration. Thus the method will be executed with readOnly flag set to false.

읽기전용 쿼리를 위해 트랜잭션을 사용하는 것은 절대적으로 합리적입니다. 우리는 readOnlyflag 같은 것을 설정함으로써 그것들(읽기전용쿼리들)을 mark하는 것이 가능합니다. 하지만 당신이 manipulating하는 쿼리를 동작시키지 않았는지 체크하는 것은 되지가 않습니다. (비록 몇몇 데이터베이스가 읽기전용 트랜잭션 내의 INSERTUPDATE 문을 거절하지만) readOnly flag는 대신에 성능 최적화를 위해 JDBC 드라이버에게 hint를 전파시킵니다(propagated). 더군다나 스프링은 몇가지 JPA provider에 대핸 최적화를 제공합니다. 예를 들자면 하이버네이트를 사용할 때, flush 모드가 NEVER로 된 경우 당신은 트랜잭션을 readOnly로 설정하여 하이너네이트의 변경감지를 skip합니다.(large 객체 trees에서는 주목할만한 향상이 있습니다.)

It’s definitely reasonable to use transactions for read only queries and we can mark them as such by setting the readOnly flag. This will not, however, act as check that you do not trigger a manipulating query (although some databases reject INSERT and UPDATE statements inside a read only transaction). The readOnly flag instead is propagated as hint to the underlying JDBC driver for performance optimizations. Furthermore, Spring will perform some optimizations on the underlying JPA provider. E.g. when used with Hibernate the flush mode is set to NEVER when you configure a transaction as readOnly which causes Hibernate to skip dirty checks (a noticeable improvement on large object trees).

4.7. Locking

사용할 lock 모드를 명시하기 위해 @Lock어노테이션이 쿼리메소드에서 사용될 수 있습니다.

To specify the lock mode to be used the @Lock annotation can be used on query methods:

Example 63. 쿼리메소드에 락 메타데이터 정의하기
Example 63. Defining lock metadata on query methods
interface UserRepository extends Repository<User, Long> {

  // Plain query method
  @Lock(LockModeType.READ)
  List<User> findByLastname(String lastname);
}

이 메소드 선언은 동작될 쿼리가 LockModeType READ를 갖추게 할 것입니다. 당신은 또한 CRUD 메소드를 위한 locking을 리파지토리 인터페이스에서 재선언하고 @Lock어노테이션을 추가함으로써 정의할 수 있습니다.

This method declaration will cause the query being triggered to be equipped with the LockModeType READ. You can also define locking for CRUD methods by redeclaring them in your repository interface and adding the @Lock annotation:

Example 64. CRUD메소드들에 대해 락 메타데이터를 정의하기
Example 64. Defining lock metadata on CRUD methods
interface UserRepository extends Repository<User, Long> {

  // Redeclaration of a CRUD method
  @Lock(LockModeType.READ);
  List<User> findAll();
}

4.8. 감사 - Auditing

4.8.1. 기초 - Basics

스프링 데이터는 엔티티를 누가 만들었고 변경했는지, 발생한 시간 지점에 관한 것들을 투명하게 추적하는 세련된 지원을 제공합니다. 이러한 기능의 장점을 누리기 위해 당신은 엔티티 클래스에 어노테이션을 사용하거나 인터페이스를 구현하여서 정의되는 auditing 메타데이터를 넣을 수 있습니다.

Spring Data provides sophisticated support to transparently keep track of who created or changed an entity and the point in time this happened. To benefit from that functionality you have to equip your entity classes with auditing metadata that can be defined either using annotations or by implementing an interface.

어노테이션 기반 auditing 메타데이터
Annotation based auditing metadata

우리는@CreatedBy, @LastModifiedBy 를 제공하여 누가 엔티티를 생성하였고 수정하였는지 캡쳐할 뿐만 아니라 @CreatedDate and @LastModifiedDate를 사용하여 어떠한 시점에 발생하였는지 캡쳐합니다.

We provide @CreatedBy, @LastModifiedBy to capture the user who created or modified the entity as well as @CreatedDate and @LastModifiedDate to capture the point in time this happened.

Example 65. 감사된 엔티티 - An audited entity
Example 65. An audited entity
class Customer {

  @CreatedBy
  private User user;

  @CreatedDate
  private DateTime createdDate;

  // … further properties omitted
}

당신이 보듯이, 당신이 캡쳐하고 싶은 정보에 따라 선택적으로 어노테이션이 적용 가능합니다. 제 시간 지점을 캡쳐하는 어노테이션을 위해 다음의 타입들이 사용가능합니다 => JodaTimes DateTime, legacy Java Date and Calendar, JDK8 date/time types as well as long/Long

As you can see, the annotations can be applied selectively, depending on which information you’d like to capture. For the annotations capturing the points in time can be used on properties of type JodaTimes DateTime, legacy Java Date and Calendar, JDK8 date/time types as well as long/Long.

인터페이스 기반 감사 메타데이터
Interface-based auditing metadata

당신이 auditing메타데이터를 정의하지 않는 경우에, 당신은 도메인 클래스에 Auditable인터페이스를 구현할 수가 있습니다. 이것은 모든 auditing 속성을 위한 setter메소드를 노출시킵니다.

In case you don’t want to use annotations to define auditing metadata you can let your domain class implement the Auditable interface. It exposes setter methods for all of the auditing properties.

또한 AbstractAuditable 기반의 편리한 클래스가 있는데, 이것으로 상속을 할 수가 있어서, 수동적으로 인터페이스 메소드를 구현할 필요로를 피하게 합니다. 이것은 스프링 데이터에 대한 도메인클래스들에, 피하고 싶어할 지도 모르는 커플링을 증가시킨다는 것을 알아둡시다. 보통 auditing 메타데이터를 정의하는 어노테이션 기반ㅇ 방법은 좀 더 유연하고 덜 침투적이기 때문에 선호됩니다.

There’s also a convenience base class AbstractAuditable which you can extend to avoid the need to manually implement the interface methods. Be aware that this increases the coupling of your domain classes to Spring Data which might be something you want to avoid. Usually the annotation based way of defining auditing metadata is preferred as it is less invasive and more flexible.

AuditorAware

당신이 @CreatedBy@LastModifiedBy같은 것을 사용하고 싶은 경우에, auditing인프라스트럭처는 어떻게든 현재 printcipal 에 대해 알게될 필요가 있습니다. 그렇게 하기 위해 우리는 AuditorAware<T> SPI interface를 제공을 하는데, 이것은 누가 어플리케이션과 시스템 상호작용을 하는 지, 누가 현재 유저인지 인프라스트럭쳐에게 알려주기 위해 구현을 합니다. 제너릭 타입 T@CreatedBy@LastModifiedBy가 어노테이션되는 속성이 어떤 속성인지 정의합니다.

In case you use either @CreatedBy or @LastModifiedBy, the auditing infrastructure somehow needs to become aware of the current principal. To do so, we provide an AuditorAware<T> SPI interface that you have to implement to tell the infrastructure who the current user or system interacting with the application is. The generic type T defines of what type the properties annotated with @CreatedBy or @LastModifiedBy have to be.

여기에 Spring 시큐리티 Authentication객체를 사용하는 인터페이스 구현에 대한 예제가 있습니다

Here’s an example implementation of the interface using Spring Security’s Authentication object:

Example 66. 스프링 시큐리티 기반의 AuditorAware구현
Example 66. Implementation of AuditorAware based on Spring Security
class SpringSecurityAuditorAware implements AuditorAware<User> {

  public User getCurrentAuditor() {

    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

    if (authentication == null || !authentication.isAuthenticated()) {
      return null;
    }

    return ((MyUserDetails) authentication.getPrincipal()).getUser();
  }
}

저 구현은 스프링시큐리티에 의해 제공된 Authentication객체에 접근하고, 당신이 생성한 UserDetailsService구현에서 커스텀 UserDetails을 탐색합니다. 우리는 여기서 당신이 도메인user를 UserDetails구현을 통해 노출했다고 가정하지만, 당신은 또한 Authentication가 발견될 수 있는 어디에서건 검색을 할 수가 있습니다.

The implementation is accessing the Authentication object provided by Spring Security and looks up the custom UserDetails instance from it that you have created in your UserDetailsService implementation. We’re assuming here that you are exposing the domain user through that UserDetails implementation but you could also look it up from anywhere based on the Authentication found.

4.9. JPA Auditing

4.9.1. 일반적인 auditing 설정
4.9.1. General auditing configuration

스프링 데이터 JPA는 엔티티리스너를 가지고 있습니다. 엔티티리스너는 auditing 정보를 캡쳐하는 데 사용될 수가 있습니다. 먼저 당신은 orm.xml 내에 AuditingEntityListener를 등록을 해야 합니다. 당신의 영속성 컨텍스트에서 모든 엔티티가 사용할 수 있게 말이죠 .

Spring Data JPA ships with an entity listener that can be used to trigger capturing auditing information. So first you have to register the AuditingEntityListener inside your orm.xml to be used for all entities in your persistence contexts:

auditing 기능은 클래스패스에spring-aspects.jar 를 필요로 합니다.

Note that the auditing feature requires spring-aspects.jar to be on the classpath.

Example 67. Auditing 설정 orm.xml
Example 67. Auditing configuration orm.xml
<persistence-unit-metadata>
  <persistence-unit-defaults>
    <entity-listeners>
      <entity-listener class="….data.jpa.domain.support.AuditingEntityListener" />
    </entity-listeners>
  </persistence-unit-defaults>
</persistence-unit-metadata>

이제 auditing기능을 활성화시키는 것은 스프링 데이터 JPA의 auditing 네임스페이스 요소를 당신의 설정에 추가하면됩니다. :

Now activating auditing functionality is just a matter of adding the Spring Data JPA auditing namespace element to your configuration:

Example 68. XML 설정을 이용한 auditing 활성화
Example 68. Activating auditing using XML configuration
<jpa:auditing auditor-aware-ref="yourAuditorAwareBean" />

Spring data jpa 1.5 에서는 auditing 은 @EnableJpaAuditing설정이 클래스에 의해서도 활성화 될 수가 있습니다

As of Spring Data JPA 1.5, auditing can be enabled by annotating a configuration class with the @EnableJpaAuditing annotation.

Example 69. 자바 설정을 통해 auditing 활성화 시키기
Example 69. Activating auditing via Java configuration
@Configuration
@EnableJpaAuditing
class Config {

  @Bean
  public AuditorAware<AuditableUser> auditorProvider() {
    return new AuditorAwareImpl();
  }
}

만약 당신이 어플리케이션 콘텍스트에 AuditorAware의 빈타입을 노출시킨다면 auditing인프라스트럭쳐는 자동적으로 그것을 pick up해서 현재 user를 도메인 타입을 결정하는 데 그것을 사용할 것입니다. 만약 당신이 어플리케이션 내에 여러개의 구현체를 등록시켰다면, 당신은 명시적으로 @EnableJpaAuditing의 auditorAwareRef속성을 설정하여 그 중에 하나를 고를 수가 있습니다.

If you expose a bean of type AuditorAware to the ApplicationContext, the auditing infrastructure will pick it up automatically and use it to determine the current user to be set on domain types. If you have multiple implementations registered in the ApplicationContext, you can select the one to be used by explicitly setting the auditorAwareRef attribute of @EnableJpaAuditing.

5. 오해
5. Miscellaneous

5.1. persistence 유닛을 합병(merging)하기
5.1. Merging persistence units

스프링은 여러개의 영속 유닛을 가지는 것을 지원합니다. 하지만 때때로 당신은 어플리케이션을 모듈화하면서도, 여전히 이런 모듈들이 런타임 시에 단일 영속 유닛 내부에서 실행되기를 확인하고 싶을 것입니다. 이렇게 하기 위해 스프링데이터JPA는 PersistenceUnitManager구현체를 제공하여, 그들의 이름에 기반하여 영속 유닛들을 자동적으로 merge하게 해줍니다.

Spring supports having multiple persistence units out of the box. Sometimes, however, you might want to modularize your application but still make sure that all these modules run inside a single persistence unit at runtime. To do so Spring Data JPA offers a PersistenceUnitManager implementation that automatically merges persistence units based on their name.

Example 70. MergingPersistenceUnitmanager 사용
Example 70. Using MergingPersistenceUnitmanager
<bean class="….LocalContainerEntityManagerFactoryBean">
  <property name="persistenceUnitManager">
    <bean class="….MergingPersistenceUnitManager" />
  </property>
</bean>

5.1.1. JPA매핑파일과 @Entity 클래스들을 위한 클래스 패스 스캐닝
5.1.1. Classpath scanning for @Entity classes and JPA mapping files

평범한 JPA설정은 orm.xml에 리스트된 모든 어노테이션 매핑된 엔티티 클래스들을 필요로 합니다. 스프링데이터 JPA는 ClasspathScanningPersistenceUnitPostProcessor를 제공하는데, 이것은 base package 설정을 얻고, 선택적인 매핑 파일이름 패턴을 가집니다. 이것은 그후, 주어진 패키지에서 @Entity 나 @MappedSuperclass 로 어노테이션된 클래스들을 스캔하고, 파일이름패턴과 매칭되는 설정파일을 불러와 JPA설정에 그것들을 전달합니다. PostProcessor가 다음과 같이 설정되어야 합니다. :

A plain JPA setup requires all annotation mapped entity classes listed in orm.xml. Same applies to XML mapping files. Spring Data JPA provides a ClasspathScanningPersistenceUnitPostProcessor that gets a base package configured and optionally takes a mapping filename pattern. It will then scan the given package for classes annotated with @Entity or @MappedSuperclass and also loads the configuration files matching the filename pattern and hands them to the JPA configuration. The PostProcessor has to be configured like this:

Example 71. ClasspathScanningPersistenceUnitPostProcessor사용
Example 71. Using ClasspathScanningPersistenceUnitPostProcessor
<bean class="….LocalContainerEntityManagerFactoryBean">
  <property name="persistenceUnitPostProcessors">
    <list>
      <bean class="org.springframework.data.jpa.support.ClasspathScanningPersistenceUnitPostProcessor">
        <constructor-arg value="com.acme.domain" />
        <property name="mappingFileNamePattern" value="**/*Mapping.xml" />
      </bean>
    </list>
  </property>
</bean>

스프링 3.1 이후로, 스캔할 패키지는 LocalContainerEntityManagerFactoryBean에 직접적으로 설정되어 엔티티클래스르들을 위한 클래스패스 스캐닝을 활성화시킬수가 있습니다. 더 자세한 사항은 JavaDoc을 참고하세요

As of Spring 3.1 a package to scan can be configured on the LocalContainerEntityManagerFactoryBean directly to enable classpath scanning for entity classes. See the JavaDoc for details.

5.2. CDI 통합
5.2. CDI integration

repository 인터페이스의 인스턴스는 보통 컨테이너에 의해 생성됩니다. 이는 스프링데이터와 작업할 때 가장 자연스런 선택입니다. 리파지토리 인스턴스에 나왔던, 빈 인스턴스를 생성하는 방법을 스프링에서 쉽게 설정하는 세련된 방법이 있습니다. 스프링데이터 버젼 1.1.0 부터 CDI환경의 리파지토리 추상화를 사용하는 커스텀 CDI 확장(extension)을 가질 수가 있습니다. 이 extension은 Jar의 일부이며, 당신이 해야할 모든 것은 스프링 데이터 JPA jar 를 클래스패스에 두는 것입니다.

Instances of the repository interfaces are usually created by a container, which Spring is the most natural choice when working with Spring Data. There’s sophisticated support to easily set up Spring to create bean instances documented in Creating repository instances. As of version 1.1.0 Spring Data JPA ships with a custom CDI extension that allows using the repository abstraction in CDI environments. The extension is part of the JAR so all you need to do to activate it is dropping the Spring Data JPA JAR into your classpath.

Y당신은 그러면 EntityManagerFactoryEntityManager를 위한 CDI Producer를 구현함으로써 인프라스트럭쳐를 설정할 수가 있게 됩니다. :

You can now set up the infrastructure by implementing a CDI Producer for the EntityManagerFactory and EntityManager:

class EntityManagerFactoryProducer {

  @Produces
  @ApplicationScoped
  public EntityManagerFactory createEntityManagerFactory() {
    return Persistence.createEntityManagerFactory("my-presistence-unit");
  }

  public void close(@Disposes EntityManagerFactory entityManagerFactory) {
    entityManagerFactory.close();
  }

  @Produces
  @RequestScoped
  public EntityManager createEntityManager(EntityManagerFactory entityManagerFactory) {
    return entityManagerFactory.createEntityManager();
  }

  public void close(@Disposes EntityManager entityManager) {
    entityManager.close();
  }
}

필요한 설치는 당신이 운영하는 JAVA EE 환경에 따라 매우 다양해질 수가 있지만 또한 다음과 같이 CDI빈으로서 EntityManager를 재선언하는 것으로 충분할 수도 있습니다.

The necessary setup can vary depending on the JavaEE environment you run in. It might also just be enough to redeclare a EntityManager as CDI bean as follows:

class CdiConfig {

  @Produces
  @RequestScoped
  @PersistenceContext
  public EntityManager entityManager;
}

이 예제에서 컨테이너는 EntityManagers를 생성 할 수 있습니다. 여기서 설정이 하는 것은 CDI빈으로써 JPAEntityManager을 다시 export시키는 것입니다.

In this example, the container has to be capable of creating JPA EntityManagers itself. All the configuration does is re-exporting the JPA EntityManager as CDI bean.

스프링 데이터 JPA CDI 확장은 모든 EntityManagers 를 CDI빈으로 고를수 있게(pick up) 해주며, 리파지토리 타입의 빈이 컨테이너에 의해 요청될때마다 스프링 데이터 리파지토리를 위한 프록시를 생성합니다. 그러므로 스프링데이터 리파지토리의 인스턴스를 얻는 것은 @Injected속성을 선언하기만 하면 됩니다

The Spring Data JPA CDI extension will pick up all EntityManagers availables as CDI beans and create a proxy for a Spring Data repository whenever an bean of a repository type is requested by the container. Thus obtaining an instance of a Spring Data repository is a matter of declaring an @Injected property:

class RepositoryClient {

  @Inject
  PersonRepository repository;

  public void businessMethod() {
    List<Person> people = repository.findAll();
  }
}

부록

부록 A: 네임스페이스 레퍼런스
Appendix A: Namespace reference

The <repositories /> element

<repositories /> 요소는 스프링 데이터 리파지토리 인프라스트럭쳐의 설정을 동작시킵니다. 가장 중요한 속성은 base-package 로 이것은 스프링 데이터 리파지토리 인터페이스[3]를 찾을 패키지를 스캔하게 해줍니다.

The <repositories /> element triggers the setup of the Spring Data repository infrastructure. The most important attribute is base-package which defines the package to scan for Spring Data repository interfaces.[3]

Table 6. 속성들
Name Description

base-package

*Repository를 상속하는 리파지토리 인터페이스를 자동 감지 모드 아래 스캔하기 위해 사용됨. (실제 인터페이스는 스프링 데이터 모듈에 따라 결정된다.) 설정된 패키지의 아래 있는 모든 패키지는 스캔되며 와일드 카드 또한 가능하다.

Defines the package to be used to be scanned for repository interfaces extending *Repository (actual interface is determined by specific Spring Data module) in auto detection mode. All packages below the configured package will be scanned, too. Wildcards are allowed.

repository-impl-postfix

커스텀 리파지토리 구현체를 자동감지하기 위한 접미사를 설정한다. 설정된 접미사를 가진 클래스들은 그 후보로 고려되며 기본값은 Impl이다.

transDefines the postfix to autodetect custom repository implementations. Classes whose names end with the configured postfix will be considered as candidates. Defaults to Impl.

query-lookup-strategy

쿼리 검색자를 만드는데 사용되는 전략을 결정하기 위함. 쿼리 탐색 전략을 보시고 기본값은 create-if-not-found이다.

Determines the strategy to be used to create finder queries. See Query lookup strategies for details. Defaults to create-if-not-found.

named-queries-location

외부적으로 정의된 쿼리들을 포함하는 설정파일을 찾기 위한 위치를 정의함

Defines the location to look for a Properties file containing externally defined queries.

consider-nested-repositories

중첩(nested) 리파지토리 인터페이스 정의를 조정할지 고려함. 기본값은 false.

Controls whether nested repository interface definitions should be considered. Defaults to false.

부록 B : Populators 네임스페이스 레퍼런스
Appendix B: Populators namespace reference

The <populator /> element

<populator /> 요소는 저장소를 Spring data 리파지토리 인프라스트럭쳐[4]를 통해 채울 있게 해준다.
The <populator /> element allows to populate the a data store via the Spring Data repository infrastructure.[4]

Table 7. Attributes
Name Description

locations

어디서 저장소를 채울 객체를 읽어들일 파일들을 발견할지 정함.

Where to find the files to read the objects from the repository shall be populated with.

부록 C : 리파지토리 쿼리 키워드들
Appendix C: Repository query keywords

지원되는 쿼리 키워드들
Supported query keywords

다음의 테이블은 일반적으로 스프링데이터리파지토리 쿼리 추론 메커니즘에서 지원되는 키워드들을 나타낸다 그러나 특정 디비에서 정확히 지원되는 키워드를 위해 저장소 문서를 참조하기를 바란다. 왜냐하면 몇몇 리스트는 특정 데이터 저장소에서 지원이 되지 않기 때문이다.

The following table lists the keywords generally supported by the Spring Data repository query derivation mechanism. However, consult the store-specific documentation for the exact list of supported keywords, because some listed here might not be supported in a particular store.

Table 8. Query keywords
Logical keyword Keyword expressions

AND

And

OR

Or

AFTER

After, IsAfter

BEFORE

Before, IsBefore

CONTAINING

Containing, IsContaining, Contains

BETWEEN

Between, IsBetween

ENDING_WITH

EndingWith, IsEndingWith, EndsWith

EXISTS

Exists

FALSE

False, IsFalse

GREATER_THAN

GreaterThan, IsGreaterThan

GREATER_THAN_EQUALS

GreaterThanEqual, IsGreaterThanEqual

IN

In, IsIn

IS

Is, Equals, (or no keyword)

IS_NOT_NULL

NotNull, IsNotNull

IS_NULL

Null, IsNull

LESS_THAN

LessThan, IsLessThan

LESS_THAN_EQUAL

LessThanEqual, IsLessThanEqual

LIKE

Like, IsLike

NEAR

Near, IsNear

NOT

Not, IsNot

NOT_IN

NotIn, IsNotIn

NOT_LIKE

NotLike, IsNotLike

REGEX

Regex, MatchesRegex, Matches

STARTING_WITH

StartingWith, IsStartingWith, StartsWith

TRUE

True, IsTrue

WITHIN

Within, IsWithin

부록 D : 리파지토리 쿼리 리턴 타입
Appendix D: Repository query return types

지원되는 쿼리 리턴 타입
Supported query return types

다음의 테이블은 일반적으로 스프링 데이터 리파지토리에서 지원되는 리턴 타입들을 나타냅니다. 그러나 앞서 말씀드린 대로 데이터 저장소 문서를 참조하세요.
The following table lists the return types generally supported by Spring Data repositories. However, consult the store-specific documentation for the exact list of supported return types, because some listed here might not be supported in a particular store.

GeoResult, GeoResults, GeoPage와 같은 지역기반 타입 은 오직 지역기반 쿼리를 지원하는 데이터저장소에서만 사용가능합니다.
Geospatial types like (GeoResult, GeoResults, GeoPage) are only available for data stores that support geospatial queries.
표 6. 쿼리 리턴 타입
Table 6. Query return types
Return type Description

void

아무런 리턴 타입 없다.
Denotes no return value.

Primitives

자바 주요요소
Java primitives.

Wrapper types

자바 래퍼 타입
Java wrapper types.

T

유니크 엔티티. 대부분 쿼리 메소드가 하나의 결과를 돌려주기를 예상합니다. 어떠한 결과도 발견되지 않은 경우 null이 반환될 것입니다. 하나 이상의 결과가 나오게 되면 IncorrectResultSizeDataAccessException가 나올 것입니다.
An unique entity. Expects the query method to return one result at most. In case no result is found null is returned. More than one result will trigger an IncorrectResultSizeDataAccessException.

Iterator<T>

반복자.
An Iterator.

Collection<T>

A Collection.

List<T>

A List.

Optional<T>

자바8 이나 Guava Optional입니다. 쿼리메소드가 대부분 하나의 결과값을 리턴하기를 기대하며 어떠한 결과도 없으면 Optional.empty()/Optional.absent()가 반환됩니다. 하나 이상의 결과에는 IncorrectResultSizeDataAccessException가 동작할 것입니다.
A Java 8 or Guava Optional. Expects the query method to return one result at most. In case no result is found Optional.empty()/Optional.absent() is returned. More than one result will trigger an IncorrectResultSizeDataAccessException.

Stream<T>

A Java 8 Stream.

Slice

더 이상의 데이터가 가능한지에 대한 정보가 같이 있는 데이터의 덩어리입니다. Pageable를 메소드 파라미터로 필요로 합니다 .
A sized chunk of data with information whether there is more data available. Requires a Pageable method parameter.

Page<T>

Slice 와 추가적인 정보가 있는 (예를 들자면 결과의 총 개수) 타입입니다. Pageable 를 메소드 파라미터로 필요로 합니다.
A Slice with additional information, e.g. the total number of results. Requires a Pageable method parameter.

GeoResult<T>

추가적인 정보(참조 위치에 대한 거리)와 함께 있는 결과 엔트리
A result entry with additional information, e.g. distance to a reference location.

GeoResults<T>

추가적인 정보(참조 위치에 대한 평균 거리 )와 함께 있는 GeoResult<T>의 리스트
A list of GeoResult<T> with additional information, e.g. average distance to a reference location.

GeoPage<T>

PageGeoResult<T>,예를 들면 참조 위치와 평균 거리
A Page with GeoResult<T>, e.g. average distance to a reference location.

부록 E: 자주묻는 질문들
Appendix E: Frequently asked questions

흔한 질문
Common

  1. 저는 JpaRepository내에서 호출되는 메소드들에 대해 좀더 상세한 로깅을 얻고 싶은데 어떻게 할까요?

    당신으 스프링에서 제공되는 CustomizableTraceInterceptor 를 이용할 수가 있습니다 :

    I’d like to get more detailed logging information on what methods are called inside JpaRepository, e.g. How can I gain them?

    You can make use of CustomizableTraceInterceptor provided by Spring:

    <bean id="customizableTraceInterceptor" class="
      org.springframework.aop.interceptor.CustomizableTraceInterceptor">
      <property name="enterMessage" value="Entering $[methodName]($[arguments])"/>
      <property name="exitMessage" value="Leaving $[methodName](): $[returnValue]"/>
    </bean>
    
    <aop:config>
      <aop:advisor advice-ref="customizableTraceInterceptor"
        pointcut="execution(public * org.springframework.data.jpa.repository.JpaRepository+.*(..))"/>
    </aop:config>

인프라스트럭쳐
Infrastructure

  1. 현재 저는 HibernateDaoSupport에 기반하는 리파지토리 레이어를 구현했습니다. 저는 스프링의 AnnotationSessionFactoryBean를 이용하여서 SessionFactory를 만들었습니다. 어떻게 이런 환경에서 스프링 데이터 리파지토리들을 얻을 수 있을까요?

    Currently I have implemented a repository layer based on HibernateDaoSupport. I create a SessionFactory by using Spring’s AnnotationSessionFactoryBean. How do I get Spring Data repositories working in this environment?

    당신은 다음과 같이 AnnotationSessionFactoryBeanHibernateJpaSessionFactoryBean로 바꾸시면 됩니다. :

    You have to replace AnnotationSessionFactoryBean with the HibernateJpaSessionFactoryBean as follows:

    Example 72. SessionFactoryHibernateEntityManagerFactory로부터 SessionFactory탐색
    Example 72. Looking up a SessionFactory from a HibernateEntityManagerFactory
    <bean id="sessionFactory" class="org.springframework.orm.jpa.vendor.HibernateJpaSessionFactoryBean">
      <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

Auditing

  1. 저는 스프링 데이터 auditing 기능들을 사용하고 싶지만 제 데이터베이스는 이미 엔티티들에 수정,생성날짜를 설정해두었습니다. 어떻게 스프링데이터가 date를 programmatically하게 설정하는 것을 막아둘까요?

    네임스페이스 요소 auditingset-dates 속성을 false로 하세요

    I want to use Spring Data JPA auditing capabilities but have my database already set up to set modification and creation date on entities. How to prevent Spring Data from setting the date programmatically.

    Just use the set-dates attribute of the auditing namespace element to false.

부록 F: 용어집
Appendix F: Glossary

AOP

관점 지향 프로그래밍

Aspect oriented programming

Commons DBCP

Commons DataBase Connection Pools - 데이터 소스 인터페이스를 구현하는 pooling 구현체. 아파치 단체의 라이브러리

Commons DataBase Connection Pools - Library of the Apache foundation offering pooling implementations of the DataSource interface.

CRUD

만들고, 읽고, 수정하고, 삭제하는 기본적인 작업 Create, Read, Update, Delete - Basic persistence operations

Create, Read, Update, Delete - Basic persistence operations

DAO

데이터 접근 객체 - 영속화되는 객체로부터 분리된, 영속하는 logic

Data Access Object - Pattern to separate persisting logic from the object to be persisted

Dependency Injection

컴포넌트의 의존성을 바깥에 있는 컴포넌트로 전달하는 패턴. 자신이 스스로 의존성을 탐색해야되는 것에서 자유롭게 합니다. 더 나은 정보는 http://en.wikipedia.org/wiki/Dependency_Injection

Pattern to hand a component’s dependency to the component from outside, freeing the component to lookup the dependant itself. For more information see http://en.wikipedia.org/wiki/Dependency_Injection.

EclipseLink

JPA를 구현하는 객체관계매퍼 - http://www.eclipselink.org

Object relational mapper implementing JPA - http://www.eclipselink.org

Hibernate

JPA를 구현하는 객체관계매퍼 - http://www.hibernate.org

Object relational mapper implementing JPA - http://www.hibernate.org

JPA

자바 영속 API

Java Persistence API

Spring

자바 어플리케이션 프레임 워크 - http://projects.spring.io/spring-framework

Java application framework - http://projects.spring.io/spring-framework

댓글


1. JavaConfig in the Spring reference documentation
2. Spring HATEOAS - https://github.com/SpringSource/spring-hateoas
3. see XML configuration
4. see XML configuration