#199 - Add documentation for Connection Factory Initialization.

This commit is contained in:
Mark Paluch
2019-09-27 16:04:48 +02:00
parent 53121b49a4
commit fbb3f3ac69

View File

@@ -0,0 +1,102 @@
[[r2dbc.init]]
= Initializing a `ConnectionFactory`
The `org.springframework.data.r2dbc.connectionfactory.init` package provides support for initializing an existing `ConnectionFactory`.
You may sometimes need to initialize an instance that runs on a server somewhere or an embedded database.
== Initializing a Database by Using @Bean methods
If you want to initialize a database and you can provide a reference to a `ConnectionFactory` bean, you can use the
.Using `ConnectionFactoryInitializer` to initialize a `ConnectionFactory`
====
[source,java]
----
@Configuration
public class InitializerConfiguration {
@Bean
public ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) {
ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
initializer.setConnectionFactory(connectionFactory);
CompositeDatabasePopulator populator = new CompositeDatabasePopulator();
populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("com/foo/sql/db-schema.sql")));
populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("com/foo/sql/test-data1.sql")));
initializer.setDatabasePopulator(populator);
return initializer;
}
}
----
====
The preceding example runs the two specified scripts against the database.
The first script creates a schema, and the second populates tables with a test data set.
The default behavior of the database initializer is to unconditionally run the provided scripts.
This may not always be what you wantfor instance, if you run the scripts against a database that already has test data in it.
The likelihood of accidentally deleting data is reduced by following the common pattern (shown earlier) of creating the tables first and then inserting the data.
The first step fails if the tables already exist.
However, to gain more control over the creation and deletion of existing data, `ConnectionFactoryInitializer` and `ResourceDatabasePopulator` support various switches such as switching the initialization on and off.
Each statement should be separated by `;` or a new line if the `;` character is not present at all in the script. You can control that globally or script by script, as the following example shows:
.Customizing statement separators
====
[source,java]
----
@Configuration
public class InitializerConfiguration {
@Bean
public ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) {
ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
initializer.setConnectionFactory(connectionFactory);
ResourceDatabasePopulator populator = new ResourceDatabasePopulator(new ClassPathResource("com/foo/sql/db-schema.sql"));
populator.setSeparator("@@"); <1>
initializer.setDatabasePopulator(populator);
return initializer;
}
}
----
<1> Set the separator scripts to `@@`.
====
In this example, the schema scripts uses `@@` as statement separator.
=== Initialization of Other Components that Depend on the Database
A large class of applications (those that do not use the database until after the Spring context has started) can use the database initializer with no further complications.
If your application is not one of those, you might need to read the rest of this section.
The database initializer depends on a `ConnectionFactory` instance and runs the scripts provided in its initialization callback (analogous to an `init-method` in an XML bean definition, a `@PostConstruct` method in a component, or the `afterPropertiesSet()` method in a component that implements `InitializingBean`).
If other beans depend on the same data source and use the data source in an initialization callback, there might be a problem because the data has not yet been initialized.
A common example of this is a cache that initializes eagerly and loads data from the database on application startup.
To get around this issue, you have two options:
1. change your cache initialization strategy to a later phase or
2. ensure that the database initializer is initialized first
Changing your cache initialization strategy might be easy if the application is in your control and not otherwise. Some suggestions for how to implement this include:
* Make the cache initialize lazily on first usage, which improves application startup time.
* Have your cache or a separate component that initializes the cache implement Lifecycle or SmartLifecycle.
When the application context starts, you can automatically start a `SmartLifecycle` by setting its `autoStartup` flag, and you can manually start a Lifecycle by calling `ConfigurableApplicationContext.start()` on the enclosing context.
* Use a Spring `ApplicationEvent` or similar custom observer mechanism to trigger the cache initialization.
`ContextRefreshedEvent` is always published by the context when it is ready for use (after all beans have been initialized), so that is often a useful hook (this is how the `SmartLifecycle` works by default).
Ensuring that the database initializer is initialized first can also be easy.
Some suggestions on how to implement this include:
* Rely on the default behavior of the Spring `BeanFactory`, which is that beans are initialized in registration order.
You can easily arrange that by adopting the common practice of a set of `@Import` configuration that order your application modules and ensuring that the database and database initialization are listed first.
* Separate the `ConnectionFactory` and the business components that use it and control their startup order by putting them in separate `ApplicationContext` instances (for example, the parent context contains the `ConnectionFactory`, and the child context contains the business components).