Skip to content

Commit

Permalink
Release v0.2.1 | Better organised config annotations, decoupling from…
Browse files Browse the repository at this point in the history
… JPARepositoy, Javax etc. (#21)

* Better Organise DataSourceConfig to segregate Primary and Secondary data sources as per Spring Convention.

* Allow @TargetSecondaryDataSource to be placed on any Repository, not just JpaRepository.

* Adaptive EntityManagerFactory for TransactionManager based on LocalContainerEntityManagerFactoryBean generated. Decoupled from Javax/Jakarta.

* CHANGELOG.MD written for 0.2.1 Release

* CHANGELOG.MD written for 0.2.1 Release

* CHANGELOG.MD written for 0.2.1 Release

* CHANGELOG.MD written for 0.2.1 Release

* Some formatting changes

* Some dependency changes

* Allow possibility for overriding different JPA properties for each data source

* Fix some documentation

* Ready for v0.2.1 release.

* Ready for v0.2.1 release.
  • Loading branch information
Dhi13man committed Jan 15, 2024
1 parent cc1d0fc commit f1f61ee
Show file tree
Hide file tree
Showing 15 changed files with 431 additions and 248 deletions.
57 changes: 57 additions & 0 deletions CHANGELOG.MD
@@ -1,5 +1,62 @@
# Releases

## [0.2.1] - 10th December 2023

- BREAKING CHANGE: Better organised DataSourceConfig to segregate Primary and Secondary data sources
as per Spring Convention. Also renamed `@TargetDataSource` to `@TargetSecondaryDataSource` to
avoid confusion.

Before:

```java
@Configuration
@EnableMultiDataSourceConfig(
repositoryPackages = {
"com.sample"
},
exactEntityPackages = {
"com.sample.project.sample_service.entities.mysql"
},
dataSourceConfigs = {
@DataSourceConfig(dataSourceName = "master", isPrimary = true),
@DataSourceConfig(dataSourceName = "replica-2"),
@DataSourceConfig(dataSourceName = "read-replica")
}
)
public class ServiceConfig {
}
```

After:

```java
@Configuration
@EnableMultiDataSourceConfig(
repositoryPackages = {
"com.sample"
},
exactEntityPackages = {
"com.sample.project.sample_service.entities.mysql"
},
primaryDataSourceConfig = @DataSourceConfig(dataSourceName = "master"),
secondaryDataSourceConfigs = {
@DataSourceConfig(dataSourceName = "replica-2"),
@DataSourceConfig(dataSourceName = "read-replica")
}
)
public class ServiceConfig {
}
```

- Allow `@TargetSecondaryDataSource` to be placed on any Repository, not just JpaRepository.

- Adaptive `EntityManagerFactory` for `TransactionManager` will be generated based on
`LocalContainerEntityManagerFactoryBean`. Decoupled from Javax/Jakarta implementations.

- Allow possibility for overriding different JPA properties for each data source by adding
`@DataSourceConfig.overridingJpaPropertiesPath` field. By default, it will take the default
`spring.jpa.properties` path.

## [0.1.2] - 6th October 2023

- HOTFIX: Fix `@EnableJpaRepositories.basePackages` being empty for secondary data sources
Expand Down
42 changes: 21 additions & 21 deletions README.md
Expand Up @@ -33,7 +33,7 @@ down to this library in the future.
* [Annotations Provided](#annotations-provided)
* [@EnableMultiDataSourceConfig](#enablemultidatasourceconfig)
* [@EnableMultiDataSourceConfig.DataSourceConfig](#enablemultidatasourceconfigdatasourceconfig)
* [@TargetDataSource](#targetdatasource)
* [@TargetSecondaryDataSource](#targetsecondarydatasource)
* [Usage](#usage)
* [Building from Source (Maven)](#building-from-source-maven)
* [Removing Dependency on spring-multi-data-source without Losing Functionality](#removing-dependency-on-spring-multi-data-source-without-losing-functionality)
Expand Down Expand Up @@ -86,8 +86,13 @@ for configuring multi-data source configurations for a service. Let's break down
naming format. If this is not specified, the generated repositories will be placed in the
same package as the class where this annotation is applied, followed by
`.generated.repositories` and then `.<data_source_name>`.
- `dataSourceConfigs`: An array of `@DataSourceConfig` annotations. Each annotation represents
a data source and its configuration.
- `primaryDataSourceConfig`: A `@DataSourceConfig` annotation. This annotation represents
the primary data source and its configuration. The primary data source will be able
to access every repository other than the repositories generated for the secondary data
sources.
- `secondaryDataSourceConfigs`: An array of `@DataSourceConfig` annotations. Each annotation
represents a data source and its configuration. The secondary data sources will only be able
to access the repositories generated for them.

#### @EnableMultiDataSourceConfig.DataSourceConfig

Expand All @@ -99,19 +104,14 @@ for configuring multi-data source configurations for a service. Let's break down
- `dataSourceName`: The name of the data source. It is used to generate the data source
beans and to name the generated classes, packages, and property paths for the data
source properties.
- `isPrimary`: Whether the data source is the primary data source. If this is set to
`true`, the generated beans for this data source will be annotated with `@Primary`. Hence
this should be set to `true` for only one data source. The primary data source will be able
to access every repository other than the repositories generated for the secondary data
sources.
- `dataSourceClassPropertiesPath`: The path of the data source class properties in the
application properties. Eg. `spring.datasource.hikari` for Hikari data sources.
- `hibernateBeanContainerPropertyPath`: The path of the Hibernate bean container property in
the application properties. This is needed to manually set the hibernate bean container to
the spring bean container to ensure that the hibernate beans like attribute converters are
managed by spring.
- `dataSourceClassPropertiesPath`:The application properties key/path of the data source class'
properties. Eg. `spring.datasource.hikari` for Hikari data sources.
- `overridingPropertiesPath`: The application properties key/path under which the JPA
properties to override for this data source are located. This allows overriding of the JPA
properties for each data source. By default, it will take the default `spring.jpa.properties`
path.

### @TargetDataSource
### @TargetSecondaryDataSource

- This annotation is used to create copies of repositories in relevant packages and
autoconfigure them to use the relevant data sources.
Expand Down Expand Up @@ -152,8 +152,8 @@ intended to be used for generating code for configuring data sources during the
exactEntityPackages = {
"com.sample.project.sample_service.entities.mysql"
},
dataSourceConfigs = {
@DataSourceConfig(dataSourceName = "master", isPrimary = true),
primaryDataSourceConfig = @DataSourceConfig(dataSourceName = "master"),
secondaryDataSourceConfigs = {
@DataSourceConfig(dataSourceName = "replica-2"),
@DataSourceConfig(dataSourceName = "read-replica")
}
Expand All @@ -162,20 +162,20 @@ intended to be used for generating code for configuring data sources during the
}
```

3. Add the `@TargetDataSource` annotation to the repository methods that need to be
3. Add the `@TargetSecondaryDataSource` annotation to the repository methods that need to be
configured for a specific data source, and specify the data source name.

```java
@Repository
public interface ServiceRepository extends JpaRepository<ServiceEntity, Long> {

@TargetDataSource("read-replica")
@TargetSecondaryDataSource("read-replica")
ServiceEntity findByCustomIdAndDate(String id, Date date);

// To override the default JpaRepository methods in the generated repository
// All base methods that have not been overridden along with this annotation will throw an
// UnsupportedOperationException.
@TargetDataSource("read-replica")
@TargetSecondaryDataSource("read-replica")
@Override
ServiceEntity getById(Long id);
}
Expand Down Expand Up @@ -252,7 +252,7 @@ longer wish to be tied down to this library.
project from the `target/generated-sources/annotations` directory.
2. Remove `implements IMultiDataSourceConfig` from the generated `@Configuration` classes.
3. Remove the `@EnableMultiDataSourceConfig` annotation from your configuration class.
4. Remove the `@TargetDataSource` annotation from your repository methods.
4. Remove the `@TargetSecondaryDataSource` annotation from your repository methods.
5. Remove the `spring-multi-data-source` dependency from your project pom.

And that's all you have to do! You are no longer tied down to this library and have the freedom to
Expand Down
12 changes: 3 additions & 9 deletions pom.xml
Expand Up @@ -133,7 +133,7 @@
</exclusions>
<groupId>org.springframework.data</groupId>
<scope>provided</scope>
<version>2.6.10</version>
<version>${spring.boot.version}</version>
</dependency>

<dependency>
Expand Down Expand Up @@ -163,12 +163,6 @@
<version>1.0.1</version>
</dependency>

<dependency>
<artifactId>javax.persistence-api</artifactId>
<groupId>javax.persistence</groupId>
<version>2.2</version>
</dependency>

<dependency>
<artifactId>junit-jupiter</artifactId>
<groupId>org.junit.jupiter</groupId>
Expand Down Expand Up @@ -229,7 +223,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<sonar.language>java</sonar.language>
<spring.boot.version>2.6.15</spring.boot.version>
<spring.boot.version>2.7.18</spring.boot.version>
</properties>
<scm>
<connection>scm:git:git://github.com/Dhi13man/spring-multi-data-source.git</connection>
Expand All @@ -239,5 +233,5 @@
</scm>

<url>http://www.github.com/dhi13man/spring-multi-data-source</url>
<version>0.1.2</version>
<version>0.2.1</version>
</project>
Expand Up @@ -18,8 +18,8 @@
* This must be an exact package name, and not a prefix as custom entity managers can not
* recursively scan entities inside nested packages.
* <p>
* If {@link TargetDataSource} is used on any repository, the package of the entity that the
* repository is associated with will also be scanned for entities.
* If {@link TargetSecondaryDataSource} is used on any repository, the package of the entity that
* the repository is associated with will also be scanned for entities.
*
* @return the array of exact packages to scan for entities.
*/
Expand Down Expand Up @@ -74,15 +74,23 @@
String generatedRepositoryPackagePrefix() default "";

/**
* The array of {@link DataSourceConfig} annotations which contain the configuration for each data
* source.
* The config for the primary data source.
* <p>
* The primary data source is the data source which will be used by default for all repositories
* which do not have the {@link TargetSecondaryDataSource} annotation.
*
* @return the {@link DataSourceConfig} annotation for the primary data source.
*/
DataSourceConfig primaryDataSourceConfig() default @DataSourceConfig(dataSourceName = "master");

/**
* The array of {@link DataSourceConfig} annotations which contain the configuration for each
* secondary data source.
*
* @return the array of {@link DataSourceConfig} annotations.
* @see DataSourceConfig
*/
DataSourceConfig[] dataSourceConfigs() default {
@DataSourceConfig(dataSourceName = "master", isPrimary = true)
};
DataSourceConfig[] secondaryDataSourceConfigs() default {};

@Retention(RetentionPolicy.RUNTIME)
@Target({})
Expand All @@ -105,29 +113,21 @@ DataSourceConfig[] dataSourceConfigs() default {
String dataSourceName() default "";

/**
* Whether this data source is the primary data source.
* <p>
* There must be exactly one primary data source.
*
* @return whether this data source is the primary data source.
*/
boolean isPrimary() default false;

/**
* The key of the data source class properties in the application properties file.
* The application properties key/path of the data source class properties.
*
* @return the prefix of the data source class properties in the application properties file.
*/
String dataSourceClassPropertiesPath() default "spring.datasource.hikari";

/**
* The path of the hibernate bean container property in the application properties.
* The application properties key/path under which the JPA properties to override for this data
* source are located.
* <p>
* This is needed to manually set the hibernate bean container to the spring bean container to
* ensure that the hibernate beans like attribute converters are managed by spring.
* This allows overriding of the JPA properties for each data source. The properties under this
* key will be merged with the usual properties under the spring.jpa.properties key.
*
* @return the prefix of the hibernate bean container properties in the application properties
* @return the key under which the JPA properties to override are located.
*/
String hibernateBeanContainerPropertyPath() default "hibernate.resource.beans.container";
String overridingJpaPropertiesPath() default "spring.jpa.properties";
}
}
Expand Up @@ -9,20 +9,20 @@

/**
* Annotation to create copies of the repositories in the relevant packages, and autoconfigure them
* to use the relevant data sources.
* to use the relevant secondary data sources.
* <p>
* Will generate all relevant boilerplate code and beans.
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
@Repeatable(TargetDataSources.class)
public @interface TargetDataSource {
@Repeatable(TargetSecondaryDataSources.class)
public @interface TargetSecondaryDataSource {

/**
* Alias for dataSourceName, the name of the data source to use for the repository.
* <p>
* To use a data source other than the primary, it must have been configured in the
* {@link EnableMultiDataSourceConfig#dataSourceConfigs()} annotations.
* {@link EnableMultiDataSourceConfig#secondaryDataSourceConfigs()} annotations.
*
* @return the data source to use for the repository.
* @see EnableMultiDataSourceConfig.DataSourceConfig#dataSourceName()
Expand Down
Expand Up @@ -7,18 +7,18 @@

/**
* Annotation to create copies of the repositories in the relevant packages, and autoconfigure them
* to use the relevant data sources.
* to use the relevant secondary data sources.
* <p>
* Will generate all relevant boilerplate code and beans.
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface TargetDataSources {
public @interface TargetSecondaryDataSources {

/**
* The array of {@link TargetDataSource} annotations.
* The array of {@link TargetSecondaryDataSource} annotations.
*
* @return the array of {@link TargetDataSource} annotations
* @return the array of {@link TargetSecondaryDataSource} annotations
*/
TargetDataSource[] value();
TargetSecondaryDataSource[] value();
}
Expand Up @@ -5,7 +5,7 @@

package io.github.dhi13man.spring.datasource.config;

import javax.persistence.EntityManagerFactory;
import java.util.Properties;
import javax.sql.DataSource;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
Expand All @@ -25,6 +25,16 @@ public interface IMultiDataSourceConfig {
*/
DataSourceProperties dataSourceProperties();

/**
* Get the JPA properties to override for the data source to be used.
* <p>
* This allows overriding of the JPA properties for each data source. These properties will be
* merged with the usual properties under the spring.jpa.properties key.
*
* @return The JPA properties.
*/
Properties overridingJpaProperties();

/**
* Get the {@link DataSource} instance for the data source to be used.
* <p>
Expand All @@ -38,22 +48,29 @@ public interface IMultiDataSourceConfig {
/**
* Get the entity manager factory to be used to interface with the data source.
*
* @param builder The entity manager factory builder, used to build the entity manager
* @param beanFactory The bean factory, used to create the entity manager factory bean
* @param dataSource The data source, used to create the entity manager factory bean
* @param builder The entity manager factory builder, used to build the entity
* manager
* @param beanFactory The bean factory, used to create the entity manager factory bean
* @param dataSource The data source, used to create the entity manager factory bean
* @param overrideJpaProperties The JPA properties, used to create the entity manager factory
* bean
* @return The entity manager factory bean.
*/
LocalContainerEntityManagerFactoryBean entityManagerFactory(
Properties overrideJpaProperties,
DataSource dataSource,
EntityManagerFactoryBuilder builder,
ConfigurableListableBeanFactory beanFactory,
DataSource dataSource
ConfigurableListableBeanFactory beanFactory
);

/**
* Get the transaction manager to be used for the data source.
*
* @param entityManagerFactory The entity manager factory, used to create the transaction manager
* @param entityManagerFactoryBean The entity manager factory bean, used to create the transaction
* manager
* @return The transaction manager.
*/
PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory);
PlatformTransactionManager transactionManager(
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean
);
}

0 comments on commit f1f61ee

Please sign in to comment.