0
# R2DBC Connectivity
1
2
Reactive database connectivity using R2DBC for non-blocking database operations. Provides factory methods and configuration options for reactive programming patterns with Oracle databases.
3
4
## Capabilities
5
6
### R2DBC Container Wrapper
7
8
Wraps Oracle XE containers to provide R2DBC connectivity with automatic connection factory configuration and lifecycle management delegation.
9
10
```java { .api }
11
public class OracleR2DBCDatabaseContainer implements R2DBCDatabaseContainer {
12
public OracleR2DBCDatabaseContainer(OracleContainer container);
13
public static ConnectionFactoryOptions getOptions(OracleContainer container);
14
public ConnectionFactoryOptions configure(ConnectionFactoryOptions options);
15
}
16
```
17
18
**Parameters:**
19
- `container (OracleContainer)`: Underlying Oracle container instance
20
21
**Returns:**
22
- `ConnectionFactoryOptions`: Configured R2DBC connection options
23
- `OracleR2DBCDatabaseContainer`: R2DBC wrapper instance
24
25
**Usage Example:**
26
```java
27
// Create Oracle container
28
OracleContainer oracleContainer = new OracleContainer("gvenzl/oracle-xe:18.4.0-slim")
29
.withDatabaseName("r2dbc_test")
30
.withUsername("r2dbcuser")
31
.withPassword("r2dbcpass");
32
33
// Wrap with R2DBC container
34
OracleR2DBCDatabaseContainer r2dbcContainer =
35
new OracleR2DBCDatabaseContainer(oracleContainer);
36
37
// Start container (delegated to underlying Oracle container)
38
r2dbcContainer.start();
39
40
// Get connection factory options
41
ConnectionFactoryOptions options = OracleR2DBCDatabaseContainer.getOptions(oracleContainer);
42
```
43
44
### Connection Factory Configuration
45
46
Automatically configures R2DBC connection factory options with Oracle-specific driver settings, host/port mapping, and authentication credentials.
47
48
```java { .api }
49
public ConnectionFactoryOptions configure(ConnectionFactoryOptions options);
50
```
51
52
**Parameters:**
53
- `options (ConnectionFactoryOptions)`: Base connection factory options
54
55
**Returns:**
56
- `ConnectionFactoryOptions`: Configured options with Oracle container details
57
58
**Usage Example:**
59
```java
60
OracleContainer container = new OracleContainer("gvenzl/oracle-xe:18.4.0-slim");
61
container.start();
62
63
OracleR2DBCDatabaseContainer r2dbcContainer = new OracleR2DBCDatabaseContainer(container);
64
65
// Configure connection factory options
66
ConnectionFactoryOptions baseOptions = ConnectionFactoryOptions.builder()
67
.option(ConnectionFactoryOptions.DRIVER, "oracle")
68
.build();
69
70
ConnectionFactoryOptions configuredOptions = r2dbcContainer.configure(baseOptions);
71
72
// Result includes:
73
// - HOST: container.getHost()
74
// - PORT: container.getMappedPort(OracleContainer.ORACLE_PORT)
75
// - DATABASE: container.getDatabaseName()
76
// - USER: container.getUsername()
77
// - PASSWORD: container.getPassword()
78
```
79
80
### R2DBC Container Provider
81
82
Service Provider Interface (SPI) implementation for automatic R2DBC container discovery and creation with connection factory metadata support.
83
84
```java { .api }
85
public class OracleR2DBCDatabaseContainerProvider implements R2DBCDatabaseContainerProvider {
86
static final String DRIVER = "oracle";
87
88
public boolean supports(ConnectionFactoryOptions options);
89
public R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options);
90
public ConnectionFactoryMetadata getMetadata(ConnectionFactoryOptions options);
91
}
92
```
93
94
**Parameters:**
95
- `options (ConnectionFactoryOptions)`: R2DBC connection factory options
96
97
**Returns:**
98
- `boolean`: Whether provider supports the driver type
99
- `R2DBCDatabaseContainer`: New R2DBC container instance
100
- `ConnectionFactoryMetadata`: Connection metadata with defaults
101
102
**Usage Example:**
103
```java
104
// Create connection factory options
105
ConnectionFactoryOptions options = ConnectionFactoryOptions.builder()
106
.option(ConnectionFactoryOptions.DRIVER, "oracle")
107
.option(ConnectionFactoryOptions.DATABASE, "testdb")
108
.option(IMAGE_TAG_OPTION, "18.4.0-slim")
109
.option(REUSABLE_OPTION, true)
110
.build();
111
112
// Use provider
113
OracleR2DBCDatabaseContainerProvider provider = new OracleR2DBCDatabaseContainerProvider();
114
115
// Check support
116
boolean supports = provider.supports(options); // true for oracle driver
117
118
// Create container
119
R2DBCDatabaseContainer r2dbcContainer = provider.createContainer(options);
120
121
// Get metadata with defaults
122
ConnectionFactoryMetadata metadata = provider.getMetadata(options);
123
// Automatically adds default USER and PASSWORD if not specified
124
```
125
126
### Lifecycle Management
127
128
R2DBC containers delegate lifecycle operations to the underlying Oracle container while implementing the Startable interface for seamless integration.
129
130
```java { .api }
131
// Delegated methods from Startable interface
132
public void start();
133
public void stop();
134
public boolean isRunning();
135
```
136
137
**Usage Example:**
138
```java
139
OracleContainer oracleContainer = new OracleContainer("gvenzl/oracle-xe:18.4.0-slim");
140
OracleR2DBCDatabaseContainer r2dbcContainer = new OracleR2DBCDatabaseContainer(oracleContainer);
141
142
// Lifecycle operations (delegated to Oracle container)
143
r2dbcContainer.start();
144
boolean running = r2dbcContainer.isRunning();
145
r2dbcContainer.stop();
146
147
// Container state is managed by underlying Oracle container
148
assertEquals(oracleContainer.isRunning(), r2dbcContainer.isRunning());
149
```
150
151
## Integration Patterns
152
153
### Spring WebFlux Integration
154
155
```java
156
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
157
@Testcontainers
158
class ReactiveOracleTest {
159
160
@Container
161
static OracleContainer oracle = new OracleContainer("gvenzl/oracle-xe:18.4.0-slim")
162
.withDatabaseName("reactive_test");
163
164
@DynamicPropertySource
165
static void configureProperties(DynamicPropertyRegistry registry) {
166
ConnectionFactoryOptions options = OracleR2DBCDatabaseContainer.getOptions(oracle);
167
168
registry.add("spring.r2dbc.url", () ->
169
"r2dbc:oracle://" + options.getRequiredValue(ConnectionFactoryOptions.HOST) +
170
":" + options.getRequiredValue(ConnectionFactoryOptions.PORT) +
171
"/" + options.getRequiredValue(ConnectionFactoryOptions.DATABASE)
172
);
173
registry.add("spring.r2dbc.username", oracle::getUsername);
174
registry.add("spring.r2dbc.password", oracle::getPassword);
175
}
176
177
@Autowired
178
private R2dbcEntityTemplate template;
179
180
@Test
181
void testReactiveConnection() {
182
StepVerifier.create(
183
template.getDatabaseClient()
184
.sql("SELECT 1 FROM DUAL")
185
.map(row -> row.get(0, Integer.class))
186
.one()
187
)
188
.expectNext(1)
189
.verifyComplete();
190
}
191
}
192
```
193
194
### Direct R2DBC Usage
195
196
```java
197
@Test
198
void testDirectR2DBCUsage() {
199
OracleContainer container = new OracleContainer("gvenzl/oracle-xe:18.4.0-slim");
200
container.start();
201
202
// Get connection factory options
203
ConnectionFactoryOptions options = OracleR2DBCDatabaseContainer.getOptions(container);
204
205
// Create connection factory
206
ConnectionFactory connectionFactory = ConnectionFactories.get(options);
207
208
// Use reactive connection
209
Mono.from(connectionFactory.create())
210
.flatMap(connection ->
211
Mono.from(connection.createStatement("SELECT SYSDATE FROM DUAL")
212
.execute())
213
.flatMap(result ->
214
Mono.from(result.map((row, metadata) ->
215
row.get(0, LocalDateTime.class))))
216
.doFinally(signal -> connection.close())
217
)
218
.as(StepVerifier::create)
219
.expectNextMatches(timestamp -> timestamp != null)
220
.verifyComplete();
221
}
222
```
223
224
### Reactive Repository Pattern
225
226
```java
227
@Repository
228
public class ReactiveUserRepository {
229
230
private final R2dbcEntityTemplate template;
231
232
public ReactiveUserRepository(R2dbcEntityTemplate template) {
233
this.template = template;
234
}
235
236
public Flux<User> findAllUsers() {
237
return template.select(User.class)
238
.from("users")
239
.all();
240
}
241
242
public Mono<User> saveUser(User user) {
243
return template.insert(User.class)
244
.using(user);
245
}
246
247
public Mono<User> findUserById(Long id) {
248
return template.select(User.class)
249
.from("users")
250
.matching(query(where("id").is(id)))
251
.one();
252
}
253
}
254
255
@Test
256
void testReactiveRepository() {
257
// Container setup
258
OracleContainer container = new OracleContainer("gvenzl/oracle-xe:18.4.0-slim")
259
.withInitScript("classpath:schema.sql");
260
container.start();
261
262
// Configure R2DBC
263
ConnectionFactoryOptions options = OracleR2DBCDatabaseContainer.getOptions(container);
264
ConnectionFactory connectionFactory = ConnectionFactories.get(options);
265
R2dbcEntityTemplate template = new R2dbcEntityTemplate(connectionFactory);
266
267
ReactiveUserRepository repository = new ReactiveUserRepository(template);
268
269
// Test reactive operations
270
User newUser = new User("Alice", "alice@example.com");
271
272
StepVerifier.create(
273
repository.saveUser(newUser)
274
.then(repository.findAllUsers().collectList())
275
)
276
.expectNextMatches(users -> users.size() == 1)
277
.verifyComplete();
278
}
279
```
280
281
## Connection Factory Options
282
283
The R2DBC Oracle connection factory supports the following standard options:
284
285
```java { .api }
286
// Standard R2DBC options
287
ConnectionFactoryOptions.DRIVER = "oracle"
288
ConnectionFactoryOptions.HOST = container.getHost()
289
ConnectionFactoryOptions.PORT = container.getMappedPort(OracleContainer.ORACLE_PORT)
290
ConnectionFactoryOptions.DATABASE = container.getDatabaseName()
291
ConnectionFactoryOptions.USER = container.getUsername()
292
ConnectionFactoryOptions.PASSWORD = container.getPassword()
293
294
// Testcontainers-specific options
295
IMAGE_TAG_OPTION = "18.4.0-slim" // Docker image tag
296
REUSABLE_OPTION = true // Container reuse flag
297
```
298
299
## Default Credentials
300
301
When connection factory options don't specify user credentials, the R2DBC provider automatically applies default values:
302
303
- **Default User**: "test" (OracleContainer.APP_USER)
304
- **Default Password**: "test" (OracleContainer.APP_USER_PASSWORD)
305
306
This ensures seamless integration even when credentials are not explicitly configured in the connection factory options.