How to set initial connections of a dynamic data source - spring-boot

I have a multi-tenant application with multiple datasources. The exact url which its connecting is set by runtime, depending on a request parameter. When I make a request at default 10 database-connections are created. With multiple datasources this fast sums up to a lot of connections. So I want to lower that number and also close the connections after a certain time.
My Configuration Class:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
basePackages = "com.example.demo",
entityManagerFactoryRef = "multiEntityManager",
transactionManagerRef = "multiTransactionManager"
)
public class PersistenceConfiguration extends WebMvcConfigurerAdapter {
private final String PACKAGE_SCAN = "com.example.demo";
//datasources
#Primary
#Bean(name = "COMP1")
public DataSource company1DS() {
return DataSourceBuilder.create()
.username("root")
.password("password")
.url("jdbc:mysql://81.123.456.789:3306/company1")
.build();
}
#Bean(name = "COMP2")
public DataSource company2DS() {
return DataSourceBuilder.create()
.username("root")
.password("password")
.url("jdbc:mysql://81.123.456.789:3306/company2")
.build();
}
#Bean(name = "multiRoutingDataSource")
public DataSource multiRoutingDataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("COMP1", company1DS());
targetDataSources.put("COMP2", company2DS());
MultiRoutingDataSource multiRoutingDataSource = new MultiRoutingDataSource();
multiRoutingDataSource.setDefaultTargetDataSource(company1DS());
multiRoutingDataSource.setTargetDataSources(targetDataSources);
return multiRoutingDataSource;
}
#Bean(name = "multiEntityManager")
public LocalContainerEntityManagerFactoryBean multiEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(multiRoutingDataSource());
em.setPackagesToScan(PACKAGE_SCAN);
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(hibernateProperties());
return em;
}
#Bean(name = "multiTransactionManager")
public PlatformTransactionManager multiTransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
multiEntityManager().getObject());
return transactionManager;
}
#Primary
#Bean(name = "dbSessionFactory")
public LocalSessionFactoryBean dbSessionFactory() {
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(multiRoutingDataSource());
sessionFactoryBean.setPackagesToScan(PACKAGE_SCAN);
sessionFactoryBean.setHibernateProperties(hibernateProperties());
return sessionFactoryBean;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
return properties;
}
}
What I already tried:
I tried to change the values in the application.properties file like so:
spring.datasource.maxActive=2
spring.datasource.minIdle=2
spring.datasource.maxIdle=5
spring.datasource.initialSize=2
I tried to change the properties the dynamic way (PersistenceConfiguration.hibernateProperties()):
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("spring.datasource.maxActive",2);
properties.put("spring.datasource.minIdle",2);
properties.put("spring.datasource.maxIdle",5);
properties.put("spring.datasource.initialSize",2);
return properties;
}
But it seems that no properties get applied. When I show SHOW GLOBAL STATUS LIKE 'Connections'; in the MySQL workbench, I always get 10 more connections after I made a request from a new datasource.
If there is any important information missing, please let me know and I will add it.
I really appreciate your effort,
Greetings Alexander
EDIT
Since spring.datasource.max-active=2 is not valid, I also tried all the following in application-properties, as well as in the hibernateProperties();
spring.datasource.hikari.maximum-pool-size=2
spring.datasource.tomcat.initial-size=2
spring.datasource.tomcat.max-idle=2
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
properties.put("spring.datasource.hikari.maximum-pool-size",2);
properties.put("spring.datasource.tomcat.initial-size",2);
properties.put("spring.datasource.tomcat.max-idle",2);
properties.setProperty("spring.datasource.hikari.maximum-pool-size","2");
properties.setProperty("spring.datasource.tomcat.initial-size","2");
properties.setProperty("spring.datasource.tomcat.max-idle","2");
return properties;
}
Nothing changed a thing

Related

Resetting database url string dynamically

I am working on a spring boot app that has two databases lets call them A and B. Database A has the list of clients (registered users) and database info and severs as the #Primary database, and Database B holds all client specific data. Database B is configured dynamically based on the user that logs in. The First page of the app shows a list of clients from which a user selects a list item, the db url string is passed to the bean configuring the datasource, and then the user is prompted to login. How can you achieve this in spring boot?
I tried the AbstractRoutingDataSource but that requires all the databases to be hard coded (or known before hand), but since new databse is created ever time a new user registers, it is possible that there will be new databases will be left out. So the database needs to be set at run time from a dynamic list, hence the need for a separate database A. How does one intialize a database from a list and make it available for the user until the user logs out?
This is the code for the primary database configuration (Database A):
#Configuration
#EnableJpaRepositories(
basePackages = "com.domu.nyomba",
entityManagerFactoryRef = "nyombaEntityManager",
transactionManagerRef = "nyombaTransactionManager")
public class NyombaDBConfig {
#Primary
#Bean
#ConfigurationProperties(prefix="spring.datasource")
public DataSource nyombaDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean nyombaEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(nyombaDataSource());
em.setPackagesToScan(new String[] { "com.domu.nyomba"});
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
Map<String, Object> properties = new HashMap<>();
em.setJpaPropertyMap(properties);
return em;
}
#Bean
#Primary
public PlatformTransactionManager nyombaTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(nyombaEntityManager().getObject());
return transactionManager;
}
}
This is the code for the client configuration (Database B):
#Configuration
#EnableJpaRepositories(
basePackages = "com.domu.activist",
entityManagerFactoryRef = "activistEntityManager",
transactionManagerRef = "activistTransactionManager"
)
public class ActivistDBConfig {
#Bean
public DataSource activistDataSource() {
return DataSourceBuilder.create().url("put the db url here as a variable/parameter")
.password("root")
.username("root")
.build();
}
#Bean
public LocalContainerEntityManagerFactoryBean activistEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(activistDataSource());
em.setPackagesToScan(new String[] { "com.domu.activist" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
Map<String, Object> properties = new HashMap<>();
em.setJpaPropertyMap(properties);
return em;
}
#Bean
public PlatformTransactionManager activistTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(activistEntityManager().getObject());
return transactionManager;
}
}
I need assistance with how to pass a variable that has the value of the database URL to the activist DataSource above, and how to initialize the datasource, point it to the right database and make it available unit the user logs out.
Please use an example to explain.
Thanks. DMuts

How to reconfigure LocalContainerEntityManagerFactoryBean in test

If I have a programmatically managed LocalContainerEntityManagerFactoryBean, like with a #Bean-annotated method:
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "myEntityManager",
transactionManagerRef = "myTransactionManager",
basePackages = {"com.mycompany.my.repository"}
)
public class MyDbConfig {
#Bean
public LocalContainerEntityManagerFactoryBean mcEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
//some production config here
return em;
}
}
How do I add this little JPA property only to be used for integration testing, ideally via something like a listener below (in the test classpath)
public class AfterAllBeansCreatedListener {
#Autowired
private LocalContainerEntityManagerFactoryBean em
#PostConstruct
private void reconfigure() {
HashMap<String, Object> properties = em.getJpaPropertyMap()
properties.put("hibernate.hbm2ddl.auto", "create");
em.setJpaPropertyMap(properties);
}
}
Currently, I am using another #Bean-annotated method producing LocalContainerEntityManagerFactoryBean in a nested #TestConfiguration, but this means that I need to duplicate all the other creation logic for that bean, which is of course pure evil.
The quick and simple solution is to create a Map of properties within the mcEntityManager() method like so
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "myEntityManager",
transactionManagerRef = "myTransactionManager",
basePackages = {"com.mycompany.my.repository"}
)
public class MyDbConfig {
#Bean
public LocalContainerEntityManagerFactoryBean mcEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
HashMap<String, Object> properties = em.getJpaPropertyMap()
properties.put("hibernate.hbm2ddl.auto", "create");
em.setJpaPropertyMap(properties);
return em;
}
}
If you need different JpaProperty map values per env, you'd move the property map creatation to it's own Profile specific methods, like
#Bean
#Profile("dev)
public Map<String,Object> getMyJpaPropertyMap() {
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", "create");
properties.put("hibernate.temp.use_jdbc_metadata_defaults",false);
properties.put("hibernate.jdbc.lob.non_contextual_creation",true);
}
#Bean
#Profile("prod)
public Map<String,Object> getMyJpaPropertyMap() {
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", "update");
properties.put("hibernate.temp.use_jdbc_metadata_defaults",true);
}
and the mcEntityManager() method is updated to
#Bean
public LocalContainerEntityManagerFactoryBean mcEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setJpaPropertyMap(getMyJpaPropertyMap());
return em;
}

Spring Boot and Database Initialization not working properly

The scripts in schema.sql gets executes but scripts from data.sql are not executing,
not sure what I am missing?
I am using Spring Boot with two data source my data base configuration is as follows
#PropertySource({ "classpath:application.properties" })
#Configuration
#EnableJpaRepositories(
basePackages = "com.projectx.mysql",
entityManagerFactoryRef = "userEntityManager",
transactionManagerRef = "userTransactionManager"
)
public class DataBaseConfig {
#Autowired
Environment env;
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean userEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(primaryDataSource());
em.setPackagesToScan(new String[] { "com.projectx.mysql" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.hbm2ddl.auto", env.getProperty("spring.jpa.hibernate.ddl-auto_mysql"));
properties.put("hibernate.dialect", env.getProperty("spring.jpa.properties.hibernate.dialect_mysql"));
properties.put("hibernate.show_sql", env.getProperty("spring.jpa.show-sql"));
em.setJpaPropertyMap(properties);
return em;
}
#Bean
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean
public PlatformTransactionManager userTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(userEntityManager().getObject());
return transactionManager;
}
}
and .properties file configuration as follows
spring.datasource.initialize=true
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.jpa.show-sql: true
spring.jpa.hibernate.ddl-auto_mysql=update
spring.jpa.properties.hibernate.dialect_mysql=org.hibernate.dialect.MySQL5Dialect
For those of you who stumble upon this question in a SpringBoot 2.1+ world.
First, what I think is the full class name of the important class (the object of the "publishEvent"... "org.springframework.boot.autoconfigure.jdbc.DataSourceInitializedEvent"
For future readers, this class seems to have disappeared between these two versions :
Below does exist:
https://docs.spring.io/spring-boot/docs/2.0.0.M3/api/org/springframework/boot/autoconfigure/jdbc/DataSourceInitializedEvent.html
Below no longer exists:
https://docs.spring.io/spring-boot/docs/2.1.0.M1/api/org/springframework/boot/autoconfigure/jdbc/DataSourceInitializedEvent.html
Here is how I "coded up" the seed data ("data.sql") .. when I had a Java-Config heavy class.
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
#Bean
public DataSource getDataSource() {
//not shown
}
#Bean
public DataSourceInitializer dataSourceInitializer(DataSource ds) {
ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator();
resourceDatabasePopulator.addScript(new ClassPathResource("/data.sql"));
DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
dataSourceInitializer.setDataSource(ds);
dataSourceInitializer.setDatabasePopulator(resourceDatabasePopulator);
return dataSourceInitializer;
}
Debug tip. You may want to use a file name that is NOT a magic file name ("data.sql" is a magic name) to purposely avoid spring boot magic. Especially since spring boot 2.5.
The issue was with datasource initialization when we see content of org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer class that takes care of Database initialization through *.sql files.It has post construct method as follows
#PostConstruct
public void init() {
if (!this.properties.isInitialize()) {
logger.debug("Initialization disabled (not running DDL scripts)");
return;
}
if (this.applicationContext.getBeanNamesForType(DataSource.class, false,
false).length > 0) {
this.dataSource = this.applicationContext.getBean(DataSource.class);
}
if (this.dataSource == null) {
logger.debug("No DataSource found so not initializing");
return;
}
runSchemaScripts();
}
The runSchemaScripts() method will initialize the data before hibernate schema creation and update operation is perfomed so if database schema is not generated then these method will create schema if you provide that in SQL script, but I want to perform operation after the schema is created/updated, for that class contains
#Override
public void onApplicationEvent(DataSourceInitializedEvent event) {
if (!this.properties.isInitialize()) {
logger.debug("Initialization disabled (not running data scripts)");
return;
}
// NOTE the event can happen more than once and
// the event datasource is not used here
if (!this.initialized) {
runDataScripts();
this.initialized = true;
}
}
this is called if after the hibernate schema creation/updation operation when we have spring boots default Datasource creation mechanism.
But as I was creating Datasource by myself,so it was not creating DataSourceInitializedEvent,so the data initilization scripts data.sql was not executed.
So I have changed my Data source creation logic to create DataSourceInitializedEvent as follows and that solved my issue.
#Autowired
private ConfigurableApplicationContext applicationContext;
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean userEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(primaryDataSource());
em.setPackagesToScan(new String[] { "com.projectx.mysql" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.hbm2ddl.auto", env.getProperty("spring.jpa.hibernate.ddl-auto_mysql"));
properties.put("hibernate.dialect", env.getProperty("spring.jpa.properties.hibernate.dialect_mysql"));
properties.put("hibernate.show_sql", env.getProperty("spring.jpa.show-sql"));
em.setJpaPropertyMap(properties);
this.applicationContext.publishEvent(new DataSourceInitializedEvent(primaryDataSource()));
return em;
}
#Bean
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
Added this.applicationContext.publishEvent(new DataSourceInitializedEvent(primaryDataSource())); to create the DataSourceInitializedEvent event
I managed to get 2 data-sources instantiated and initiate schema and data in one of them with this test project. Hope that helps, maybe i missed some requirement of yours that makes my suggestions invalid :(
For ref (guess you already saw this): https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-two-datasources

spring-boot application doesn't load fixtures into one of multiple data sources

I have some trouble loading data fixtures during start of my spring boot application (1.5.2.RELEASE). The application uses two different database connections, one to our customers postgresql database on which we do not have permissions to create, insert or update anything. The other database is a local embedded h2 database (file). I want to load some data during application start into that h2 database by using the spring boot database initialization phase as described here but the data is never inserted into the h2 database, it stays empty as I can see using squirrel-sql.
This is the configuration in my application.properties:
spring.datasource.abc.driver-class-name=org.postgresql.Driver
spring.datasource.abc.initialize=false
spring.datasource.abc.url=jdbc:postgresql://localhost:5432/abc
spring.datasource.abc.username=abc
spring.datasource.abc.password=abc
spring.datasource.def.driver-class-name=org.h2.Driver
spring.datasource.def.initialize=true
spring.datasource.def.url=jdbc:h2:./${path.prefix}def/def;DB_CLOSE_ON_EXIT=FALSE'
spring.datasource.def.data=classpath:/data-h2.sql
I configured spring boot to use two different databases like described in this stackoverflow post and it all works fine if I pre-insert data by hand into the h2 database.
Configuration of my postgresql datasource:
#Configuration
#EnableJpaRepositories(basePackages = "....abc", entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager")
public class AbcDatabaseConfig
{
#Primary
#Bean
#ConfigurationProperties(prefix = "spring.datasource.abc")
public DataSource dataSource()
{
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory()
{
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan("....abc");
HashMap<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.hbm2ddl-auto", "none");
properties.put("hibernate.ejb.entitymanager_factory_name", "entityManagerFactory");
em.setJpaPropertyMap(properties);
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
return em;
}
#Primary
#Bean(name = "transactionManager")
public JpaTransactionManager transactionManager(#Qualifier("entityManagerFactory") final EntityManagerFactory factory)
{
return new JpaTransactionManager(factory);
}
}
Configuration of h2-datasource:
#Configuration
#EnableJpaRepositories(basePackages = "....def", entityManagerFactoryRef = "defEntityManagerFactory", transactionManagerRef = "defTransactionManager")
public class InavetDatabaseConfig
{
#Bean
#ConfigurationProperties(prefix = "spring.datasource.def")
public DataSource defDataSource()
{
return DataSourceBuilder.create().build();
}
#Bean(name = "defEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean defEntityManagerFactory()
{
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(defDataSource());
em.setPackagesToScan("....def");
HashMap<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.hbm2ddl.auto", "create");
properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
properties.put("hibernate.ejb.entitymanager_factory_name", "defEntityManagerFactory");
em.setJpaPropertyMap(properties);
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
return em;
}
#Bean(name = "defTransactionManager")
public JpaTransactionManager defTransactionManager(
#Qualifier("defEntityManagerFactory") final EntityManagerFactory factory)
{
return new JpaTransactionManager(factory);
}
}
I found out, that only the #Primary marked data sources load fixtures. My workaround for this behaviour is adding code like this to my application:
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.setContinueOnError(true);
populator.addScript(new PathResource("src/main/resources/data-h2.sql"));
DataSource dataSource = (DataSource) cac.getBean("defDataSource");
DatabasePopulatorUtils.execute(populator, dataSource);
Where cac is the instance of ConfigurableApplicationContext you get as return value by of: SpringApplication.run(,);

Multiple data source and schema creation in Spring Boot

I'm using Spring Boot. I finally managed to setup two data sources, but now I'm facing another issue.
with two data sources in place spring.jpa.hibernate.ddl-auto=create seems to stop working in my spring boot application, only spring.jpa.generate-ddl=true do the job now
I can not manage to select the auto-creation strategy for each of the data sources. I would prefer to create the schema for data source one, and just use the created schema in second DB with data source two.
Any body have idea how to resolve any of these issues? Note I don't want to completely throw away the auto-config if possible. I don't even know yet, if hibernate is able to just initialize schema in one persistence unit.
application.properties
spring.datasource-internal.url=jdbc:hsqldb:mem:testdb
spring.datasource-internal.username=sa
spring.datasource-internal.password=sa
spring.datasource-internal.driver-class-name=org.hsqldb.jdbcDriver
spring.datasource-internal.jpa.database-platform=org.hibernate.dialect.HSQLDialect
spring.datasource-external.url=jdbc:hsqldb:mem:testexternal
spring.datasource-external.username=sa
spring.datasource-external.password=sa
spring.datasource-external.driver-class-name=org.hsqldb.jdbcDriver
spring.datasource-external.jpa.database-platform=org.hibernate.dialect.HSQLDialect
flyway.enabled=false
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true
spring.jpa.generate-ddl=true
DBInternalConfig
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = "cz.data.internal",
entityManagerFactoryRef = "internalEntityManagerFactory",
transactionManagerRef = "internalTransactionManager")
public class DBConfigInternal {
public static final String INTERNAL = "internal";
#Bean(name = "internalDataSource")
#Primary
#ConfigurationProperties(prefix = "spring.datasource-internal")
public DataSource internalDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "internalEntityManagerFactory")
#Primary
public LocalContainerEntityManagerFactoryBean internalEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(internalDataSource())
.packages("cz.data.internal.entity")
.persistenceUnit(INTERNAL)
.build();
}
#Bean(name = "internalTransactionManager")
#Primary
public PlatformTransactionManager internalTransactionManager() {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setDataSource(internalDataSource());
jpaTransactionManager.setPersistenceUnitName(INTERNAL);
return jpaTransactionManager;
}
}
DBExternalConfig
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
basePackages = "cz.data.external",
entityManagerFactoryRef = "externalEntityManagerFactory",
transactionManagerRef = "externalTransactionManager")
public class DBConfigExternal {
public static final String EXTERNAL = "external";
#Bean(name = "externalDataSource")
#ConfigurationProperties(prefix = "spring.datasource-external")
public DataSource externalDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "externalEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean externalEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(externalDataSource())
.packages("cz.data.external.entity")
.persistenceUnit(EXTERNAL)
.build();
}
#Bean(name = "externalTransactionManager")
public PlatformTransactionManager externalTransactionManager() {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setDataSource(externalDataSource());
jpaTransactionManager.setPersistenceUnitName(EXTERNAL);
return jpaTransactionManager;
}
}
M.W.
spring.jpa.hibernate.ddl-auto=create has stopped working, not because you have two DataSources, but because your application's creating its own LocalContainerEntityManagerFactoryBeans. This has the effect of disabling the auto-configuration of a LocalContainerEntityManagerFactoryBean so you now have to configure it yourself.
You can configure the two entity managers to have different schema generation behaviour like this (the first's doing update, the second's doing create):
#Bean(name = "externalEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean externalEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.hbm2ddl.auto", "update");
return builder
.dataSource(externalDataSource())
.packages("cz.data.external.entity")
.persistenceUnit(EXTERNAL)
.properties(properties)
.build();
}
#Bean(name = "internalEntityManagerFactory")
#Primary
public LocalContainerEntityManagerFactoryBean internalEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.hbm2ddl.auto", "create");
return builder
.dataSource(internalDataSource())
.packages("cz.data.internal.entity")
.persistenceUnit(INTERNAL)
.properties(properties)
.build();
}

Resources