Loading...
Spring Framework Reference Documentation 7.0.2의 Data Access with R2DBC의 한국어 번역본입니다.
아래의 경우에 피드백에서 신고해주신다면 반영하겠습니다.
감사합니다 :)
R2DBC ("Reactive Relational Database Connectivity")는 리액티브 패턴을 사용하여 SQL 데이터베이스에 접근하는 것을 표준화하기 위한 커뮤니티 주도 명세 작업입니다.
Spring Framework의 R2DBC 추상화 프레임워크는 두 개의 서로 다른 패키지로 구성됩니다:
core: org.springframework.r2dbc.core 패키지는 DatabaseClient 클래스와 다양한 관련 클래스를 포함합니다. 자세한 내용은 Using the R2DBC Core Classes to Control Basic R2DBC Processing and Error Handling를 참고하십시오.
connection: org.springframework.r2dbc.connection 패키지는 쉬운 ConnectionFactory 접근을 위한 유틸리티 클래스와 테스트 및 수정되지 않은 R2DBC를 실행하는 데 사용할 수 있는 다양한 단순 ConnectionFactory 구현을 포함합니다. 자세한 내용은 Controlling Database Connections를 참고하십시오.
이 섹션에서는 오류 처리를 포함하여 기본적인 R2DBC 처리를 제어하기 위해 R2DBC 코어 클래스를 사용하는 방법을 다룹니다. 다음 주제를 포함합니다:
DatabaseClientSELECT)INSERT, UPDATE, and DELETE) with DatabaseClientDatabaseClientDatabaseClient는 R2DBC 코어 패키지의 중심 클래스입니다. 이는 리소스의 생성과 해제를 처리하여 커넥션을 닫는 것을 잊는 것과 같은 일반적인 오류를 피하는 데 도움을 줍니다. 이는 코어 R2DBC 워크플로의 기본 작업(예를 들어 스테이트먼트 생성 및 실행)을 수행하고, 애플리케이션 코드는 SQL 제공 및 결과 추출을 담당하도록 합니다. DatabaseClient 클래스는 다음을 수행합니다:
Result 인스턴스에 대한 반복 수행org.springframework.dao 패키지에 정의된 일반적이고 더 유익한 예외 계층 구조로 변환합니다. (자세한 내용은 Consistent Exception Hierarchy를 참고하십시오.)클라이언트는 선언적인 합성을 위한 리액티브 타입을 사용하는 함수형이고 유창한 API를 가집니다.
코드에서 DatabaseClient를 사용할 때는 java.util.function 인터페이스만 구현하면 되며, 명확하게 정의된 계약을 갖게 됩니다. DatabaseClient 클래스가 제공하는 Connection을 기준으로 Function 콜백이 Publisher를 생성합니다. Row 결과를 추출하는 매핑 함수도 마찬가지입니다.
DatabaseClient는 DAO 구현 내에서 ConnectionFactory 참조를 사용한 직접 인스턴스화로 사용할 수도 있고, Spring IoC 컨테이너에 이를 구성한 다음 빈 참조로 DAO에 제공할 수도 있습니다.
DatabaseClient 객체를 생성하는 가장 간단한 방법은 다음과 같이 정적 팩터리 메서드를 사용하는 것입니다:
1DatabaseClient client = DatabaseClient.create(connectionFactory);
1val client = DatabaseClient.create(connectionFactory)
ConnectionFactory는 항상 Spring IoC 컨테이너에 빈으로 구성되어야 합니다.
앞의 메서드는 기본 설정으로 DatabaseClient를 생성합니다.
또한 DatabaseClient.builder()로부터 Builder 인스턴스를 얻을 수도 있습니다. 다음 메서드를 호출하여 클라이언트를 커스터마이징할 수 있습니다:
….bindMarkers(…): 이름 있는 파라미터를 데이터베이스 바인드 마커 변환으로 구성하기 위해 특정 BindMarkersFactory를 제공합니다.….executeFunction(…): Statement 객체가 어떻게 실행되는지에 대한 ExecuteFunction을 설정합니다.….namedParameters(false): 이름 있는 파라미터 확장을 비활성화합니다. 기본값은 활성화입니다.방언은 일반적으로
ConnectionFactoryMetadata를 검사하여ConnectionFactory로부터BindMarkersFactoryResolver가 해결합니다.org.springframework.r2dbc.core.binding.BindMarkersFactoryResolver$BindMarkerFactoryProvider를 구현하는 클래스를META-INF/spring.factories를 통해 등록하여 Spring이BindMarkersFactory를 자동 검색하도록 할 수 있습니다.BindMarkersFactoryResolver는 Spring의SpringFactoriesLoader를 사용하여 클래스패스에서 바인드 마커 프로바이더 구현을 발견합니다.
현재 지원되는 데이터베이스는 다음과 같습니다:
이 클래스에 의해 발행되는 모든 SQL은 클라이언트 인스턴스의 완전 수식 클래스 이름(일반적으로 DefaultDatabaseClient)에 해당하는 카테고리 아래에서 DEBUG 레벨로 로깅됩니다. 추가로, 각 실행은 디버깅을 돕기 위해 리액티브 시퀀스에 체크포인트를 등록합니다.
다음 섹션에서는 DatabaseClient 사용 예제를 제공합니다. 이 예제는 DatabaseClient가 노출하는 모든 기능의 전체 목록은 아닙니다. 자세한 내용은 관련 javadoc을 참고하십시오.
DatabaseClient는 스테이트먼트 실행의 기본 기능을 제공합니다. 다음 예제는 새로운 테이블을 생성하는 완전하게 동작하는 최소한의 코드에 포함해야 할 내용을 보여줍니다:
1Mono<Void> completion = client.sql("CREATE TABLE person (id VARCHAR(255) PRIMARY KEY, name VARCHAR(255), age INTEGER);") 2 .then();
1client.sql("CREATE TABLE person (id VARCHAR(255) PRIMARY KEY, name VARCHAR(255), age INTEGER);") 2 .await()
DatabaseClient는 편리하고 유창한 사용을 위해 설계되었습니다. 이는 실행 명세의 각 단계에서 중간, 연속, 터미널 메서드를 노출합니다.
위의 예제는 쿼리(SQL 쿼리에 여러 스테이트먼트가 포함된 경우 여러 쿼리)가 완료되자마자 완료되는 완료 Publisher를 반환하기 위해 then()을 사용합니다.
execute(…)는 SQL 쿼리 문자열 또는 실제 쿼리 생성을 실행 시점까지 지연시키기 위한 쿼리Supplier<String>을 받아들입니다.
SELECT)SQL 쿼리는 Row 객체를 통해 값 또는 영향을 받은 행 수를 반환할 수 있습니다. DatabaseClient는 발행된 쿼리에 따라 업데이트된 행 수 또는 행 자체를 반환할 수 있습니다.
다음 쿼리는 테이블에서 id와 name 컬럼을 가져옵니다:
1Mono<Map<String, Object>> first = client.sql("SELECT id, name FROM person") 2 .fetch().first();
1val first = client.sql("SELECT id, name FROM person") 2 .fetch().awaitSingle()
다음 쿼리는 바인드 변수를 사용합니다:
1Mono<Map<String, Object>> first = client.sql("SELECT id, name FROM person WHERE first_name = :fn") 2 .bind("fn", "Joe") 3 .fetch().first();
1val first = client.sql("SELECT id, name FROM person WHERE first_name = :fn") 2 .bind("fn", "Joe") 3 .fetch().awaitSingle()
위의 예제에서 fetch()의 사용을 눈치챘을 수 있습니다. fetch()는 소비하려는 데이터의 양을 지정할 수 있게 해주는 연속 연산자입니다.
first()를 호출하면 결과에서 첫 번째 행을 반환하고 나머지 행은 버립니다. 다음 연산자를 사용하여 데이터를 소비할 수 있습니다:
first()는 전체 결과의 첫 번째 행을 반환합니다. Kotlin Coroutine 변형은 널이 될 수 없는 반환 값을 위한 awaitSingle(), 값이 선택적인 경우 awaitSingleOrNull()이라는 이름을 가집니다.one()은 정확히 하나의 결과를 반환하며, 결과에 더 많은 행이 포함되어 있으면 실패합니다. Kotlin Coroutines를 사용할 때는 정확히 하나의 값을 위한 awaitOne() 또는 값이 null일 수 있는 경우 awaitOneOrNull()을 사용합니다.all()은 결과의 모든 행을 반환합니다. Kotlin Coroutines를 사용할 때는 flow()를 사용합니다.rowsUpdated()는 영향을 받은 행 수(INSERT/UPDATE/DELETE 카운트)를 반환합니다. Kotlin Coroutine 변형은 awaitRowsUpdated()라는 이름을 가집니다.추가적인 매핑 세부 정보를 지정하지 않으면 쿼리는 컬럼 이름이 해당 컬럼 값에 매핑되는, 키가 대소문자를 구분하지 않는 컬럼 이름인 Map으로 테이블 형식 결과를 반환합니다.
각 Row에 대해 호출되어 임의의 값(단일 값, 컬렉션 및 맵, 객체)을 반환할 수 있는 Function<Row, T>를 제공함으로써 결과 매핑을 제어할 수 있습니다.
다음 예제는 name 컬럼을 추출하고 해당 값을 방출합니다:
1Flux<String> names = client.sql("SELECT name FROM person") 2 .map(row -> row.get("name", String.class)) 3 .all();
1val names = client.sql("SELECT name FROM person") 2 .map { row: Row -> row.get("name", String.class) } 3 .flow()
또는 단일 값으로 매핑하기 위한 단축키가 있습니다:
1Flux<String> names = client.sql("SELECT name FROM person") 2 .mapValue(String.class) 3 .all();
또는 빈 프로퍼티나 레코드 컴포넌트를 가진 결과 객체로 매핑할 수 있습니다:
1// assuming a name property on Person 2Flux<Person> persons = client.sql("SELECT name FROM person") 3 .mapProperties(Person.class) 4 .all();
null은 어떻게 될까요?
관계형 데이터베이스 결과는 null 값을 포함할 수 있습니다. 리액티브 스트림 명세는 null 값의 방출을 금지합니다. 이 요구사항은 추출기 함수에서 적절한 null 처리를 요구합니다. Row로부터 null 값을 얻을 수 있지만, null 값을 방출해서는 안 됩니다. 추출기 함수가 직접적으로 null 값을 반환하지 않도록 null 값을 객체(예를 들어 단일 값의 경우 Optional)로 래핑해야 합니다.
INSERT, UPDATE, and DELETE) with DatabaseClient수정 스테이트먼트의 유일한 차이점은 이러한 스테이트먼트는 일반적으로 테이블 형식 데이터를 반환하지 않으므로 결과를 소비하기 위해 rowsUpdated()를 사용한다는 점입니다.
다음 예제는 업데이트된 행 수를 반환하는 UPDATE 스테이트먼트를 보여줍니다:
1Mono<Integer> affectedRows = client.sql("UPDATE person SET first_name = :fn") 2 .bind("fn", "Joe") 3 .fetch().rowsUpdated();
1val affectedRows = client.sql("UPDATE person SET first_name = :fn") 2 .bind("fn", "Joe") 3 .fetch().awaitRowsUpdated()
일반적인 애플리케이션은 일부 입력에 따라 행을 선택하거나 업데이트하기 위해 파라미터화된 SQL 스테이트먼트를 필요로 합니다. 이는 일반적으로 WHERE 절로 제한되는 SELECT 스테이트먼트 또는 입력 파라미터를 받는 INSERT 및 UPDATE 스테이트먼트입니다. 파라미터화된 스테이트먼트는 파라미터가 적절하게 이스케이프되지 않으면 SQL 인젝션의 위험을 수반합니다. DatabaseClient는 쿼리 파라미터에 대한 SQL 인젝션 위험을 제거하기 위해 R2DBC의 bind API를 활용합니다. execute(…) 연산자로 파라미터화된 SQL 스테이트먼트를 제공하고 실제 Statement에 파라미터를 바인드할 수 있습니다. 그런 다음 R2DBC 드라이버는 준비된 스테이트먼트와 파라미터 치환을 사용하여 스테이트먼트를 실행합니다.
파라미터 바인딩은 두 가지 바인딩 전략을 지원합니다:
다음 예제는 쿼리에 대한 파라미터 바인딩을 보여줍니다:
1db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)") 2 .bind("id", "joe") 3 .bind("name", "Joe") 4 .bind("age", 34);
또는 이름과 값을 가진 맵을 전달할 수 있습니다:
1Map<String, Object> params = new LinkedHashMap<>(); 2params.put("id", "joe"); 3params.put("name", "Joe"); 4params.put("age", 34); 5db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)") 6 .bindValues(params);
또는 빈 프로퍼티나 레코드 컴포넌트를 가진 파라미터 객체를 전달할 수 있습니다:
1// assuming id, name, age properties on Person 2db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)") 3 .bindProperties(new Person("joe", "Joe", 34));
또는 스테이트먼트에 값을 바인딩하기 위해 위치 기반 파라미터를 사용할 수 있습니다. 인덱스는 0 기반입니다.
1db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)") 2 .bind(0, "joe") 3 .bind(1, "Joe") 4 .bind(2, 34);
애플리케이션이 많은 파라미터에 바인딩하는 경우, 동일한 작업을 단일 호출로 수행할 수 있습니다:
1List<?> values = List.of("joe", "Joe", 34); 2db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)") 3 .bindValues(values);
R2DBC Native Bind Markers
R2DBC는 실제 데이터베이스 벤더에 따라 달라지는 데이터베이스 네이티브 바인드 마커를 사용합니다. 예로, Postgres는 $1, $2, $n과 같은 인덱스 마커를 사용합니다. 또 다른 예로는 @ 프리픽스가 붙은 이름 있는 바인드 마커를 사용하는 SQL Server가 있습니다.
이는 바인드 마커로 ?를 요구하는 JDBC와는 다릅니다. JDBC에서는 실제 드라이버가 스테이트먼트 실행의 일부로 ? 바인드 마커를 데이터베이스 네이티브 마커로 변환합니다.
Spring Framework의 R2DBC 지원은 네이티브 바인드 마커 또는 :name 구문을 사용하는 이름 있는 바인드 마커를 사용할 수 있게 해줍니다.
이름 있는 파라미터 지원은 쿼리 실행 시점에 이름 있는 파라미터를 네이티브 바인드 마커로 확장하기 위해 BindMarkersFactory 인스턴스를 활용하며, 이를 통해 다양한 데이터베이스 벤더 간에 일정 수준의 쿼리 이식성을 제공합니다.
쿼리 전처리기는 인자 수에 기반한 동적 쿼리 생성을 제거하기 위해 이름 있는 Collection 파라미터를 일련의 바인드 마커로 언롤합니다. 중첩 객체 배열은 (예를 들어) 셀렉트 리스트 사용을 허용하기 위해 확장됩니다.
다음 쿼리를 고려해 보십시오:
1SELECT id, name, state FROM table WHERE (name, age) IN (('John', 35), ('Ann', 50))
앞의 쿼리는 다음과 같이 파라미터화되어 실행될 수 있습니다:
1List<Object[]> tuples = new ArrayList<>(); 2tuples.add(new Object[] {"John", 35}); 3tuples.add(new Object[] {"Ann", 50}); 4 5client.sql("SELECT id, name, state FROM table WHERE (name, age) IN (:tuples)") 6 .bind("tuples", tuples);
1val tuples: MutableList<Array<Any>> = ArrayList() 2tuples.add(arrayOf("John", 35)) 3tuples.add(arrayOf("Ann", 50)) 4 5client.sql("SELECT id, name, state FROM table WHERE (name, age) IN (:tuples)") 6 .bind("tuples", tuples)
셀렉트 리스트 사용은 벤더에 따라 다릅니다.
다음 예제는 IN 술어를 사용하는 더 간단한 변형을 보여줍니다:
1client.sql("SELECT id, name, state FROM table WHERE age IN (:ages)") 2 .bind("ages", Arrays.asList(35, 50));
1client.sql("SELECT id, name, state FROM table WHERE age IN (:ages)") 2 .bind("ages", arrayOf(35, 50))
R2DBC 자체는 Collection과 유사한 값을 지원하지 않습니다. 그럼에도 불구하고, 위 예제에서 주어진
List를 확장하는 것은 Spring의 R2DBC 지원에서 이름 있는 파라미터에 대해 동작하며, 예를 들어 위에서 보여준 것처럼IN절에서 사용하기에 적합합니다. 그러나 배열 타입 컬럼(예: Postgres)을 insert하거나 update하려면 기본 R2DBC 드라이버에서 지원하는 배열 타입이 필요합니다. 일반적으로text[]컬럼을 update하기 위한String[]와 같은 Java 배열입니다. 배열 파라미터로Collection<String>또는 이와 유사한 것을 전달하지 마십시오.
때로는 실제 Statement가 실행되기 전에 옵션을 미세 조정해야 할 필요가 있습니다. 이를 위해 DatabaseClient에 Statement 필터 (StatementFilterFunction)를 등록하여 스테이트먼트를 가로채고 수정할 수 있습니다. 다음 예제와 같습니다:
1client.sql("INSERT INTO table (name, state) VALUES(:name, :state)") 2 .filter((s, next) -> next.execute(s.returnGeneratedValues("id"))) 3 .bind("name", …) 4 .bind("state", …);
1client.sql("INSERT INTO table (name, state) VALUES(:name, :state)") 2 .filter { s: Statement, next: ExecuteFunction -> next.execute(s.returnGeneratedValues("id")) } 3 .bind("name", …) 4 .bind("state", …)
DatabaseClient는 또한 Function<Statement, Statement>를 받는 단순화된 filter(…) 오버로드를 노출합니다:
1client.sql("INSERT INTO table (name, state) VALUES(:name, :state)") 2 .filter(statement -> statement.returnGeneratedValues("id")); 3 4client.sql("SELECT id, name, state FROM table") 5 .filter(statement -> statement.fetchSize(25));
1client.sql("INSERT INTO table (name, state) VALUES(:name, :state)") 2 .filter { statement -> statement.returnGeneratedValues("id") } 3 4client.sql("SELECT id, name, state FROM table") 5 .filter { statement -> statement.fetchSize(25) }
StatementFilterFunction 구현은 Statement 필터링과 Result 객체 필터링을 모두 허용합니다.
DatabaseClient Best PracticesDatabaseClient 클래스의 인스턴스는 한 번 구성되면 스레드 세이프합니다. 이는 하나의 DatabaseClient 인스턴스를 구성한 다음 이 공유 참조를 여러 DAO(또는 리포지토리)에 안전하게 주입할 수 있다는 점에서 중요합니다. DatabaseClient는 ConnectionFactory에 대한 참조를 유지한다는 점에서 상태를 가지지만, 이 상태는 대화형 상태가 아닙니다.
DatabaseClient 클래스를 사용할 때 일반적인 관행은 Spring 설정 파일에 ConnectionFactory를 구성한 다음 해당 공유 ConnectionFactory 빈을 DAO 클래스에 의존성 주입하는 것입니다. DatabaseClient는 ConnectionFactory의 세터에서 생성됩니다.
이는 다음과 같은 DAO로 이어집니다:
1public class R2dbcCorporateEventDao implements CorporateEventDao { 2 3 private DatabaseClient databaseClient; 4 5 public void setConnectionFactory(ConnectionFactory connectionFactory) { 6 this.databaseClient = DatabaseClient.create(connectionFactory); 7 } 8 9 // R2DBC-backed implementations of the methods on the CorporateEventDao follow... 10}
1class R2dbcCorporateEventDao(connectionFactory: ConnectionFactory) : CorporateEventDao { 2 3 private val databaseClient = DatabaseClient.create(connectionFactory) 4 5 // R2DBC-backed implementations of the methods on the CorporateEventDao follow... 6}
명시적 구성을 대체하는 방법은 의존성 주입을 위한 컴포넌트 스캐닝 및 어노테이션 지원을 사용하는 것입니다. 이 경우 클래스를 @Component로 어노테이션(이를 컴포넌트 스캐닝의 후보로 만듦)하고 ConnectionFactory 세터 메서드를 @Autowired로 어노테이션할 수 있습니다.
다음 예제는 이를 수행하는 방법을 보여줍니다:
1@Component (1) 2public class R2dbcCorporateEventDao implements CorporateEventDao { 3 4 private DatabaseClient databaseClient; 5 6 @Autowired (2) 7 public void setConnectionFactory(ConnectionFactory connectionFactory) { 8 this.databaseClient = DatabaseClient.create(connectionFactory); (3) 9 } 10 11 // R2DBC-backed implementations of the methods on the CorporateEventDao follow... 12}
| 1 | 클래스를 @Component로 어노테이션합니다. |
| 2 | ConnectionFactory 세터 메서드를 @Autowired로 어노테이션합니다. |
| 3 | ConnectionFactory로 새로운 DatabaseClient를 생성합니다. |
1@Component (1) 2class R2dbcCorporateEventDao(connectionFactory: ConnectionFactory) : CorporateEventDao { (2) 3 4 private val databaseClient = DatabaseClient(connectionFactory) (3) 5 6 // R2DBC-backed implementations of the methods on the CorporateEventDao follow... 7}
| 1 | 클래스를 @Component로 어노테이션합니다. |
| 2 | ConnectionFactory의 생성자 주입. |
| 3 | ConnectionFactory로 새로운 DatabaseClient를 생성합니다. |
위의 템플릿 초기화 스타일 중 어떤 것을 사용하든(또는 사용하지 않든) SQL을 실행할 때마다 매번 새로운 DatabaseClient 클래스 인스턴스를 생성해야 할 필요는 거의 없습니다. 한 번 구성되면 DatabaseClient 인스턴스는 스레드 세이프입니다.
애플리케이션이 여러 데이터베이스에 접근하는 경우, 여러 ConnectionFactory와 그에 따라 여러 개의 다르게 구성된 DatabaseClient 인스턴스가 필요하므로 여러 DatabaseClient 인스턴스가 필요할 수 있습니다.
INSERT 스테이트먼트는 자동 증가 또는 아이덴티티 컬럼을 정의하는 테이블에 행을 insert할 때 키를 생성할 수 있습니다. 생성할 컬럼 이름을 완전히 제어하려면 원하는 컬럼에 대해 생성된 키를 요청하는 StatementFilterFunction을 등록하면 됩니다.
1Mono<Integer> generatedId = client.sql("INSERT INTO table (name, state) VALUES(:name, :state)") 2 .filter(statement -> statement.returnGeneratedValues("id")) 3 .map(row -> row.get("id", Integer.class)) 4 .first(); 5 6// generatedId emits the generated key once the INSERT statement has finished
1val generatedId = client.sql("INSERT INTO table (name, state) VALUES(:name, :state)") 2 .filter { statement -> statement.returnGeneratedValues("id") } 3 .map { row -> row.get("id", Integer.class) } 4 .awaitOne() 5 6// generatedId emits the generated key once the INSERT statement has finished
이 섹션에서는 다음을 다룹니다:
ConnectionFactoryConnectionFactoryUtilsSingleConnectionFactoryTransactionAwareConnectionFactoryProxyR2dbcTransactionManagerConnectionFactorySpring은 ConnectionFactory를 통해 데이터베이스에 대한 R2DBC 커넥션을 얻습니다. ConnectionFactory는 R2DBC 명세의 일부이며 드라이버를 위한 공통 진입점입니다. 이는 컨테이너 또는 프레임워크가 애플리케이션 코드로부터 커넥션 풀링 및 트랜잭션 관리 이슈를 숨길 수 있게 해줍니다.
개발자로서 데이터베이스에 연결하는 방법에 대한 세부 사항을 알 필요는 없습니다. 이는 ConnectionFactory를 설정하는 관리자 책임입니다. 개발 및 테스트 코드를 작성하는 동안 두 역할을 모두 수행하는 경우가 많지만, 프로덕션 데이터 소스가 어떻게 구성되는지 반드시 알 필요는 없습니다.
Spring의 R2DBC 레이어를 사용할 때는 서드파티가 제공하는 커넥션 풀 구현으로 직접 구성할 수 있습니다. 널리 사용되는 구현은 R2DBC Pool(r2dbc-pool)입니다. Spring 배포판의 구현은 테스트 목적에만 사용되며 풀링을 제공하지 않습니다.
ConnectionFactory를 구성하려면:
ConnectionFactory를 얻는 것처럼 ConnectionFactory로 커넥션을 얻습니다.다음 예제는 ConnectionFactory를 구성하는 방법을 보여줍니다:
1ConnectionFactory factory = ConnectionFactories.get("r2dbc:h2:mem:///test?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
1val factory = ConnectionFactories.get("r2dbc:h2:mem:///test?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
ConnectionFactoryUtilsConnectionFactoryUtils 클래스는 ConnectionFactory로부터 커넥션을 얻고(필요한 경우) 커넥션을 닫기 위한 static 메서드를 제공하는 편리하고 강력한 헬퍼 클래스입니다.
이는 예를 들어 R2dbcTransactionManager와 함께 구독자 Context에 바인딩된 커넥션을 지원합니다.
SingleConnectionFactorySingleConnectionFactory 클래스는 각 사용 후에 닫히지 않는 단일 Connection을 래핑하는 DelegatingConnectionFactory 인터페이스의 구현입니다.
클라이언트 코드가(예: 퍼시스턴스 도구를 사용할 때처럼) 풀링된 커넥션을 가정하고 close를 호출하는 경우, suppressClose 프로퍼티를 true로 설정해야 합니다. 이 설정은 물리적 커넥션을 래핑하는 close-suppressing 프록시를 반환합니다. 이 경우 더 이상 이를 네이티브 Connection 또는 유사한 객체로 캐스팅할 수 없습니다.
SingleConnectionFactory는 주로 테스트 클래스이며, R2DBC 드라이버가 이를 허용하는 경우 파이프라이닝과 같은 특정 요구사항에 사용할 수 있습니다. 풀링된 ConnectionFactory와는 달리, 항상 동일한 커넥션을 재사용하여 물리적 커넥션의 과도한 생성을 피합니다.
TransactionAwareConnectionFactoryProxyTransactionAwareConnectionFactoryProxy는 target ConnectionFactory에 대한 프록시입니다. 이 프록시는 해당 target ConnectionFactory를 래핑하여 Spring-managed 트랜잭션에 대한 인식을 추가합니다.
이 클래스를 사용하는 것은 Spring의 R2DBC 지원과 별도로 통합되지 않은 R2DBC 클라이언트를 사용하는 경우에 필요합니다. 이 경우에도 이 클라이언트를 계속 사용할 수 있으며 동시에 이 클라이언트가 Spring-managed 트랜잭션에 참여하도록 할 수 있습니다. 일반적으로는 리소스 관리를 위해
ConnectionFactoryUtils에 적절히 접근할 수 있도록 R2DBC 클라이언트를 통합하는 것이 바람직합니다.
자세한 내용은 TransactionAwareConnectionFactoryProxy javadoc을 참고하십시오.
R2dbcTransactionManagerR2dbcTransactionManager 클래스는 단일 R2DBC ConnectionFactory를 위한 ReactiveTransactionManager 구현입니다. 이는 지정된 ConnectionFactory로부터 R2DBC Connection을 구독자 Context에 바인딩하여, 잠재적으로 각 ConnectionFactory마다 하나의 구독자 Connection을 허용합니다.
애플리케이션 코드는 R2DBC 표준 ConnectionFactory.create() 대신 ConnectionFactoryUtils.getConnection(ConnectionFactory)를 통해 R2DBC Connection을 조회해야 합니다. 모든 프레임워크 클래스(예: DatabaseClient)는 이 전략을 암묵적으로 사용합니다. 트랜잭션 매니저와 함께 사용되지 않는 경우 조회 전략은 정확히 ConnectionFactory.create()처럼 동작하므로 어떤 경우에도 사용할 수 있습니다.
Initializing a DataSource
Object Relational Mapping (ORM) Data Access