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
Related
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
I have two H2 databases in my Spring boot application. Each of these databases is responsible for persistence of different entities.
The first one is initialized by Flyway which works fine, but now I need to initialize the other H2 database. I think I have found out that it is impossible to initialize multiple databases with Flyway. So I am trying to manually import my schema.sql and data.sql files
This is what I have till now:
Two separate DatasourceConfig files:
The first data source config (Notice how this datasource is configured to be primary):
#Configuration
#Profile({"prod","test"})
#EnableJpaRepositories(
basePackages = "tracker.repository.h2",
entityManagerFactoryRef = "h2EntityManager",
transactionManagerRef = "h2TransactionManager")
public class PrivateH2DatasourceConfig {
#Autowired
private Environment env;
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean h2EntityManager() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(h2DataSource());
em.setPackagesToScan(
new String[]{"tracker.domain.h2"});
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto",
env.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect",
env.getProperty("hibernate.dialect"));
em.setJpaPropertyMap(properties);
return em;
}
#Bean
#Primary
public DataSource h2DataSource() {
DriverManagerDataSource dataSource
= new DriverManagerDataSource();
dataSource.setDriverClassName(
env.getProperty("h2.datasource.driver-class-name"));
dataSource.setUrl(env.getProperty("h2.datasource.url"));
dataSource.setUsername(env.getProperty("h2.datasource.data-username"));
dataSource.setPassword(env.getProperty("h2.datasource.data-password"));
return dataSource;
}
#Bean
#Primary
public PlatformTransactionManager h2TransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(h2EntityManager().getObject());
return transactionManager;
}
}
Second H2 datasource config file:
#Configuration
#Profile("test")
#EnableJpaRepositories(
basePackages = "tracker.repository.wmx",
entityManagerFactoryRef = "wmxH2EntityManager",
transactionManagerRef = "wmxH2TransactionManager")
public class WmxH2DatasourceConfig {
#Autowired
private Environment env;
#Bean
public LocalContainerEntityManagerFactoryBean wmxH2EntityManager() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(wmxH2DataSource());
em.setPackagesToScan(
new String[]{"tracker.domain.wmx"});
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto",
env.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect",
env.getProperty("hibernate.dialect"));
em.setJpaPropertyMap(properties);
return em;
}
#Bean
public DataSource wmxH2DataSource() {
DriverManagerDataSource dataSource
= new DriverManagerDataSource();
dataSource.setDriverClassName(
env.getProperty("test.h2.datasource.driver-class-name"));
dataSource.setUrl(env.getProperty("test.h2.datasource.url"));
dataSource.setUsername(env.getProperty("test.h2.datasource.data-username"));
dataSource.setPassword(env.getProperty("test.h2.datasource.data-password"));
return dataSource;
}
#Bean
public PlatformTransactionManager wmxH2TransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(wmxH2EntityManager().getObject());
return transactionManager;
}
}
Then I have application.properties:
#can be over ridden by run arguments
spring.profiles.active=test
flyway.baseline-on-migrate=true
## H2
h2.datasource.driver-class-name=org.h2.Driver
h2.datasource.url=jdbc:h2:./data/deliverytracker.h2
h2.datasource.data-username=x
h2.datasource.data-password=xxx
## H2 WMX (Test environment)
test.h2.datasource.driver-class-name=org.h2.Driver
test.h2.datasource.url=jdbc:h2:./data/wmx_test.h2
test.h2.datasource.data-username=x
test.h2.datasource.data-password=xxx
test.h2.datasource.initialize=true
test.h2.datasource.data=classpath:schema-test.sql,classpath:data-test.sql
and the last line is how I have been trying to import the sql scripts in the correct database, but it doesn't seem to do anything. The first (non-primary) database file is created but it is empty.
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
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(,);
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();
}