© 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.프로젝트 메타데이터
-
Version control - http://github.com/spring-projects/spring-data-jpa
-
Bugtracker - https://jira.spring.io/browse/DATAJPA
-
Release repository - https://repo.spring.io/libs-release
-
Milestone repository - https://repo.spring.io/libs-milestone
-
Snapshot repository - https://repo.spring.io/libs-snapshot
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:
<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.
<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.
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. |
우리는 또한
JpaRepository 나 MongoRepository 같은 기술특징적인 추상화를 제공합니다. 이러한 인터페이스는 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:
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.
public interface UserRepository extends CrudRepository<User, Long> {
Long countByLastname(String lastname);
}
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:
-
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> { … }
-
그 인터페이스에 대해서 쿼리메소드를 선언합니다.Declare query methods on the interface.
interface PersonRepository extends Repository<User, Long> { List<Person> findByLastname(String lastname); }
-
스프링을 설정하여 이러한 인터페이스들을 위한 프록시 인스턴스를 생성하게 해줍니다. 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 {}
or via XML configuration:
<?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.
-
리파지토리 인스턴스를 주입시키고 사용하세요
Get the repository instance injected and use it.
public class SomeClient { @Autowired private PersonRepository repository; public void doSomething() { List<Person> persons = repository.findByLastname("Matthews"); } }
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.
|
@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 creationCREATE
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)CREATE
와USE_DECLARED_QUERY
를 조합합니다. 이것은 처음에 정의된 쿼리를 찾아보고 발견되지 않으면, 커스텀 메소드네임 기반의 쿼리를 생성합니다. 이것은 기본 탐색전략이고, 당신이 아무것도 명시적으로 설정하지 않으면 기본 설정이 될 것입니다. 이것은 메소드이름으로 빠른 쿼리 정의를 가능케 하며, 필요에 따라 선언된 쿼리를 소개하면서 이러한 쿼리들의 커스텀 튜닝또한 가능하게 합니다.CREATE_IF_NOT_FOUND
(default) combinesCREATE
andUSE_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
는 실제 크리테리아의 시작을 가리키는 구별자로 동작합니다. 기본레벨에서 당신은 엔티티의 속성을 결정할 조건을 정의할 수 있으며, 그것들을 And
와 Or
로 연결할 수 있습니다.
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
.
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.
-
이 표현은 보통 속성을 순회하며(영어 오타인듯?) 연결될 수 있는 연산자와 함께 조합됩니다.
AND
나OR
같은 표현과 함께 속성을 조합할 수 있습니다. 당신은 또한Between
,LessThan
,GreaterThan
,Like
같은 연산자의 지원을 받을 수 있습니다. 이러한 지원되는 연산자는 데이터스토어에 따라 다를 수 있으니, 해당 레퍼런스 문서에 적절한 부분을 참고해보세요.The expressions are usually property traversals combined with operators that can be concatenated. You can combine property expressions with
AND
andOR
. You also get support for operators such asBetween
,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 (usuallyString
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
절을 추가하여 쿼리메소드의 정렬 방향을 정할 수 있습니다.Asc
나Desc
로 말이죠.. 동적 정렬을 지원하는 쿼리를 만들기 위해 특별 파라미터 핸들링를 보시기 바랍니다.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
orDesc
). To create a query method that supports dynamic sorting, see Special parameter handling.
3.4.3. 속성 표현
속성표현은 이전 예제에서 나타난대로, 관리되는 엔티티의 직접적인 속성을 참조할 수 있습니다. 쿼리 생성 시간에 당신은 이미 파싱된 속성이 관리되는 도메인 클래스의 일부라는 것을 확실하게 하고 가야 합니다. 그러나 당신은 중첩속성에 대한 제약을 정의할 수 있습니다. Person
이 ZipCode
와 함께 있는 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 부분의 오른쪽부터 왼쪽으로 그리고 꼬리부분으로 나눠보며, 맞는 속성을 찾으려 할 것입니다. 우리의 예제에서는 AddressZip
와 Code
일 것입니다. 만약 알고리즘이 앞에서 맞는 속성을 찾아내면, 거기서부터 뒷부분을 가지고서 트리다운을 만들기 시작합니다. (역주: 그 뒷부부분으로 변수를 만든다는 뜻인듯^^;) 이전에 설명한 방법대로 뒷부분을 잘라보면서 말입니다. 만약 처음 나누기가 매칭이 되지 않으면 알고리즘은 나누는 지점을 왼쪽으로 이동해보게 됩니다. (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.
비록 이러한 작업이 대부분의 경우 잘 동작하지만, 잘못 동작할 경우가 있습니다. Person
가 addressZip
이라는 속성도 가지고 있다고 생각해봅시다. 이러한 알고리즘은 처음 나누면서 본질적으로 잘못된 뒷부분을 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.
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
쿼리메소드의 결과는 first
나 top
를 통해서 제한될 수 있으며 바꿔 쓸수 있습니다. 선택적인 숫자값이 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.
Top
과 First
로 결과 크기를 한계짓기 :
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.
Stream<T>
로 쿼리 결과 Stream하기 :
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)하고 사용뒤에 반드시 닫혀져야 합니다. 당신은 직접 Stream 를 close() 를 사용하여 닫아주거나 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.
|
Stream<T>
와 try-with-resources 블록으로 작업해보기
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.
<?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:
<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.
@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.
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.
interface UserRepositoryCustom {
public void someCustomMethod(User user);
}
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.
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
.
<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.
<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.
-
모든 리파지토리에서 커스텀 행동을 추가하기 위해, 먼저 공유행동으로 선언할 중계인터페이스를 추가합니다.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); }
-
이제 당신의 독립적인 리파지토리 인터페이스는
Repository
대신에 이 중계인터페이스를 상속하고, 선언된 기능을 포함할 것입니다.Now your individual repository interfaces will extend this intermediate interface instead of theRepository
interface to include the functionality declared. -
다음으로, 영속기술특징 리파지토리 빈 클래스를 상속하는 중계인터페이스의 구현체를 생성합니다. 이 클래스는 리파지토리 프록시를 위한 커스텀 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 classExample 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 thebase-package
. This means that if left in its current state, an implementation instance ofMyRepository
will be created by Spring. This is of course not desired as it is just supposed to act as an intermediary betweenRepository
and the actual repository interfaces you want to define for each entity. To exclude an interface that extendsRepository
from being instantiated as a repository instance, you can either annotate it with@NoRepositoryBean
(as seen above) or move it outside of the configuredbase-package
. -
그러고 난 후에 커스텀 리파지토리 팩토리를 만들어 기본
RepositoryFactoryBean
를 대체하여 커스텀RepositoryFactory
을 만들어봅시다. 그러면 새로운 리파지토리 팩토리는 당신에게Repository
인터페이스를 상속하는 어떤 인터페이스의 구현체로써MyRepositoryImpl
를 제공할 것이고, 이것은SimpleJpaRepository
구현체를 대체할 것입니다.Then create a custom repository factory to replace the default
RepositoryFactoryBean
that will in turn produce a customRepositoryFactory
. The new repository factory will then provide yourMyRepositoryImpl
as the implementation of any interfaces that extend theRepository
interface, replacing theSimpleJpaRepository
implementation you just extended.예제 24. 커스텀 리파지토리 팩토리 빈 Custom repository factory beanExample 24. Custom repository factory beanpublic 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; } } }
-
마침내, 커스텀 팩토리를 직접적으로 선언을 하거나, 스프링 네임스페이스의
factory-class
속성을 사용하거나@Enable…
어노테이션으로 repository 인프라스트럭쳐를 설정하여서, (마침내) 당신의 커스텀 팩토리 구현체를 사용하게 됩니다.Finally, either declare beans of the custom factory directly or use thefactory-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 namespaceExample 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
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.
@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.
SpringDataWebSupport
나 HateoasAwareSpringDataWebSupport
를 스프링 빈으로 등록합니다.
SpringDataWebSupport
or HateoasAwareSpringDataWebSupport
as Spring beans:
<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" />
기본 웹 지원
-
DomainClassConverter
는 SpringMVC 를 활성화시켜서 요청파라미터나 경로변수로부터 리파지토리 managed도메인클래스를 resolve합니다.ADomainClassConverter
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 컨트롤러 시그니처에서 직접적으로 도메인 타입을 사용하게 해줍니다. 그래서 당신은 수동적으로 리파지토리를 통해 인스턴스를 찾을 필요가 없습니다.
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:
@Controller
@RequestMapping("/users")
public class UserController {
@RequestMapping("/{id}")
public String showUserForm(@PathVariable("id") User user, Model model) {
model.addAttribute("user", user);
return "userForm";
}
}
findOne(…)
을 호출하게 합니다.
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
의 인스턴스를 등록합니다. 이 등록은 Pageable
과 Sort
가 유효한 컨틀롤러 메소드 아규먼트가 되게 해줍니다 .
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
@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";
}
}
|
얻기 원하는 페이지, 0 indexed and 기본은 0. |
|
얻기 원하는 페이지 크기, 기본 20. |
|
다음의 형식으로 정렬될 형식 |
이러한 행동을 커스터마이징 하고 싶다면 @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.
이러한 경우 당신은 여러개의 테이블을 위해서, 요청으로부터 여러개의 Pageable
나 Sort
인스턴스가 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_page
나 bar_page
같은 것을 만들어낼 것입니다 .
foo_page
and bar_page
etc.
Pageable
는 new PageRequest(0, 20)
와 같습니다만, Pageable
파라미터에서 @PageableDefaults
어노테이션을 통해서 커스터마이징 될 수 있습니다.
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
PagedResources
를 가지고 있어서 Page
인스턴스를, 필요한 Page
메타데이터로 풍부하게 해줄 수 있을 뿐만 아니라, 링크가 클라이언트가 쉽게 페이지를 네비게이트하게 해줍니다. Page에서 PagedResources
으로의 전환은 Spring HATEOAS ResourceAssembler
인터페이스, PagedResourcesAssembler
를 구현함으로써 이뤄집니다.
.
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
.
@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 thePage
will become the content of thePagedResources
instance. -
PagedResources
는, 동봉되어 채워진 정보와 함께PageMetadata
인스턴스를 얻어서Page
와 기본PageRequest
를 형성합니다. -
PagedResources
는 같이 있는 페이지의 상태에 따라prev
와next
연결을 얻습니다.. 이 링크는 메소드실행이 연결된 URI를 가리키며, 그 메소드에 추가된 페이지네이션 파라미터는PageableHandlerMethodArgumentResolver
의 설정과 매칭이 되어 링크가 나중에 resolved되게 해줍니다. -
The
PagedResources
will get aPageMetadata
instance attached populated with information form thePage
and the underlyingPageRequest
. -
The
PagedResources
getsprev
andnext
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 thePageableHandlerMethodArgumentResolver
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:
[ { "_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:
<?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
를 통해 역직렬화 되게 해줍니다 .
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.
<?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
@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:
<?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:
|
명시적으로
Explicitly wire the |
|
Explicitly wire the |
만약 어떠한 명시적 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.
@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:
Id-Property inspection (default) |
기본적으로 스프링 데이터 JPA에서는 주어진 엔티티의 식별자 속성을 검사를 합니다.(역주 : ID겠지..?) 만약 식별자 속성이 By default Spring Data JPA inspects the identifier property of the given entity. If the identifier property is |
Implementing |
만약 엔티티가 If an entity implements |
Implementing |
당신은 You can customize the |
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:
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.
Keyword | Sample | JPQL snippet |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4.3.3. JPA NamedQuery들 사용하기
4.3.3. Using JPA NamedQueries
이 예제는 간단하게 The examples use simple |
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.
<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.
@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:
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에 어노테이션된 쿼리들은 @NamedQuery
나 orm.xml
에 선언된 namedquery 에 비해서 우선시(precendence)됩니다.
Queries annotated to the query method will take precedence over queries defined using @NamedQuery
or named queries declared in orm.xml
.
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.
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.
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.
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.
변수 | 사용 | 설명 |
---|---|---|
|
|
주어진 repository에 관련된 도메인 타입의 Inserts the |
다음 예제는 쿼리 문자열에서 #{#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.
The |
@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.
@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
:
@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.
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.
@Entity
@NamedEntityGraph(name = "GroupInfo.detail",
attributeNodes = @NamedAttributeNode("members"))
public class GroupInfo {
// default fetch mode is lazy.
@ManyToMany
List<GroupMember> members = new ArrayList<GroupMember>();
…
}
@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.
/;
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.
@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
나 @Procedure
의 procedureName
속성을 통해 직접적으로 정의될 수 있기도하고, 아니면 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.
@Procedure("plus1inout")
Integer explicitlyNamedPlus1inout(Integer arg);
procedureName
alias.으로 참조
procedureName
alias.
@Procedure(procedureName = "plus1inout")
Integer plus1inout(Integer arg);
@Procedure(name = "User.plus1IO")
Integer entityAnnotatedCustomNamedProcedurePlus1IO(@Param("arg") Integer arg);
@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:
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.createdAt
은 Date
의createdAt
속성을 가지는 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:
List<Customer> customers = customerRepository.findAll(isLongTermCustomer());
자, 왜 단순하게 이러한 종류의 데이터 접근을 위해 쿼리를 생성하지 않을까요? 당신이 맞습니다. 단순한 Specification
을 사용하는 것은 단순한 평문 쿼리 선언에서는 많은 장점을 얻지 못합니다. specifications의 장점은 그들을 조합해서 새로운 Specification
객체를 만들때 나타납니다. 당신은 Specifications
helper 클래스(우리가 다음과 같이 표현을 만들기 위해 제공하는)를 통해서 이러한 점을 달성할 수 있습니다. :
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:
MonetaryAmount amount = new MonetaryAmount(200.0, Currencies.DOLLAR);
List<Customer> customers = customerRepository.findAll(
where(isLongTermCustomer()).or(hasSalesOfMoreThan(amount)));
당신이 보듯이 Specifications
는 Specification
인스턴스를 체인하고 조합하기 위해 몇가지 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:
public interface UserRepository extends CrudRepository<User, Long> {
@Override
@Transactional(timeout = 10)
public List<User> findAll();
// Further query method declarations
}
이것은 findAll()
메소드가 readOnly
flag없이 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:
@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.
@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
어노테이션을 사용하여 트랜잭션 설정을 오버라이딩합니다. 그러므로 저 메소드는 readOnly
가 false
인 상태로 설정되어서 실행됩니다
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
.
읽기전용 쿼리를 위해 트랜잭션을 사용하는 것은 절대적으로 합리적입니다. 우리는 It’s definitely reasonable to use transactions for read only queries and we can mark them as such by setting the |
4.7. Locking
사용할 lock 모드를 명시하기 위해 @Lock
어노테이션이 쿼리메소드에서 사용될 수 있습니다.
To specify the lock mode to be used the @Lock
annotation can be used 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:
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.
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:
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.
<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:
<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.
@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.
<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:
<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당신은 그러면 EntityManagerFactory
와 EntityManager
를 위한 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]
Name | Description |
---|---|
|
*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. |
|
커스텀 리파지토리 구현체를 자동감지하기 위한 접미사를 설정한다. 설정된 접미사를 가진 클래스들은 그 후보로 고려되며 기본값은
transDefines the postfix to autodetect custom repository implementations. Classes whose names end with the configured postfix will be considered as candidates. Defaults to |
|
쿼리 검색자를 만드는데 사용되는 전략을 결정하기 위함. 쿼리 탐색 전략을 보시고 기본값은
Determines the strategy to be used to create finder queries. See Query lookup strategies for details. Defaults to |
|
외부적으로 정의된 쿼리들을 포함하는 설정파일을 찾기 위한 위치를 정의함
Defines the location to look for a Properties file containing externally defined queries.
|
|
중첩(nested) 리파지토리 인터페이스 정의를 조정할지 고려함. 기본값은
Controls whether nested repository interface definitions should be considered. Defaults to |
부록 B : Populators 네임스페이스 레퍼런스
Appendix B: Populators namespace reference
The <populator /> element
<populator />
요소는 저장소를 Spring data 리파지토리 인프라스트럭쳐[4]를 통해 채울 있게 해준다.
<populator />
element allows to populate the a data store via the Spring Data repository infrastructure.[4]
Name | Description |
---|---|
|
어디서 저장소를 채울 객체를 읽어들일 파일들을 발견할지 정함. 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.
Logical keyword | Keyword expressions |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
부록 D : 리파지토리 쿼리 리턴 타입
Appendix D: Repository query return types
지원되는 쿼리 리턴 타입
Supported query return types
GeoResult , GeoResults , GeoPage 와 같은 지역기반 타입 은 오직 지역기반 쿼리를 지원하는 데이터저장소에서만 사용가능합니다.
Geospatial types like (
GeoResult , GeoResults , GeoPage ) are only available for data stores that support geospatial queries.
|
Return type | Description |
---|---|
|
아무런 리턴 타입 없다.
Denotes no return value.
|
Primitives |
자바 주요요소
Java primitives.
|
Wrapper types |
자바 래퍼 타입
Java wrapper types.
|
|
유니크 엔티티. 대부분 쿼리 메소드가 하나의 결과를 돌려주기를 예상합니다. 어떠한 결과도 발견되지 않은 경우
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 .
|
|
반복자 .
An
Iterator .
|
|
A |
|
A |
|
자바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 .
|
|
A Java 8 |
|
더 이상의 데이터가 가능한지에 대한 정보가 같이 있는 데이터의 덩어리입니다.
Pageable 를 메소드 파라미터로 필요로 합니다 .
A sized chunk of data with information whether there is more data available. Requires a
Pageable method parameter.
|
|
Slice 와 추가적인 정보가 있는 (예를 들자면 결과의 총 개수) 타입입니다. Pageable 를 메소드 파라미터로 필요로 합니다.
A
Slice with additional information, e.g. the total number of results. Requires a Pageable method parameter.
|
|
추가적인 정보(참조 위치에 대한 거리)와 함께 있는 결과 엔트리
A result entry with additional information, e.g. distance to a reference location.
|
|
추가적인 정보(참조 위치에 대한 평균 거리 )와 함께 있는
GeoResult<T> 의 리스트
A list of
GeoResult<T> with additional information, e.g. average distance to a reference location.
|
|
Page 와 GeoResult<T> ,예를 들면 참조 위치와 평균 거리
A
Page with GeoResult<T> , e.g. average distance to a reference location.
|
부록 E: 자주묻는 질문들
Appendix E: Frequently asked questions
흔한 질문
Common
-
저는
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
-
현재 저는
HibernateDaoSupport
에 기반하는 리파지토리 레이어를 구현했습니다. 저는 스프링의AnnotationSessionFactoryBean
를 이용하여서SessionFactory
를 만들었습니다. 어떻게 이런 환경에서 스프링 데이터 리파지토리들을 얻을 수 있을까요?Currently I have implemented a repository layer based on
HibernateDaoSupport
. I create aSessionFactory
by using Spring’sAnnotationSessionFactoryBean
. How do I get Spring Data repositories working in this environment?당신은 다음과 같이
AnnotationSessionFactoryBean
을HibernateJpaSessionFactoryBean
로 바꾸시면 됩니다. :You have to replace
AnnotationSessionFactoryBean
with theHibernateJpaSessionFactoryBean
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
-
저는 스프링 데이터 auditing 기능들을 사용하고 싶지만 제 데이터베이스는 이미 엔티티들에 수정,생성날짜를 설정해두었습니다. 어떻게 스프링데이터가 date를 programmatically하게 설정하는 것을 막아둘까요?
네임스페이스 요소
auditing
의set-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 theauditing
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