I want to configure two datasource for my spring boot project, but I have same entities in the two databases, so I ask if it is possible to make the entities in one package and initialise the specific datasource in the repositories.
This my configuration :
#Configuration
#EnableAutoConfiguration
public class NwlConfiguration {
#Bean
#Primary
#ConfigurationProperties("spring.datasource")
public DataSourceProperties source1DataSourceProperties() {
return new DataSourceProperties();
}
#Bean(name = "source1")
#Primary
#ConfigurationProperties("spring.datasource")
public DataSource source1DataSource() {
return source1DataSourceProperties().initializeDataSourceBuilder().build();
}
#Bean
#ConfigurationProperties("source2.datasource")
public DataSourceProperties source2DataSourceProperties() {
return new DataSourceProperties();
}
#Bean(name = "source2")
#ConfigurationProperties("source2.datasource")
public DataSource source2DataSource() {
return source2DataSourceProperties().initializeDataSourceBuilder().build();
}
}
Your Configuration looks ok. Only thing is you need to assign which Configuration will be used for which Repository.
If there are multiple entities you can put them in a common package.
Like this
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "barEntityManagerFactory",
transactionManagerRef = "barTransactionManager", basePackages = {"com.foobar.bar.repo"})
public class BarDbConfig {
Complete Code
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "barEntityManagerFactory",
transactionManagerRef = "barTransactionManager", basePackages = {"com.foobar.bar.repo"})
public class BarDbConfig {
#Bean(name = "barDataSource")
#ConfigurationProperties(prefix = "bar.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "barEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean barEntityManagerFactory(
EntityManagerFactoryBuilder builder, #Qualifier("barDataSource") DataSource dataSource) {
return builder.dataSource(dataSource).packages("com.foobar.bar.domain").persistenceUnit("bar")
.build();
}
#Bean(name = "barTransactionManager")
public PlatformTransactionManager barTransactionManager(
#Qualifier("barEntityManagerFactory") EntityManagerFactory barEntityManagerFactory) {
return new JpaTransactionManager(barEntityManagerFactory);
}
}
2nd Datasource Config Class
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory",
basePackages = {"com.foobar.foo.repo"})
public class FooDbConfig {
#Primary
#Bean(name = "dataSource")
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder, #Qualifier("dataSource") DataSource dataSource) {
return builder.dataSource(dataSource).packages("com.foobar.foo.domain").persistenceUnit("foo")
.build();
}
#Primary
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Refer this article for more Detail
Related
I want to use hibernate to connect two different databases and use different sessionfactory to operate the data
In the example below , Another datasource and LocalSessionFactoryBean will be added in the #Configuration class , but how do I give these two sessionFactory specific names?
Reference:
https://www.baeldung.com/hibernate-5-spring
#Configuration
#EnableTransactionManagement
public class HibernateConf {
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(
{"com.baeldung.hibernate.bootstrap.model" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:mem:db;DB_CLOSE_DELAY=-1");
dataSource.setUsername("sa");
dataSource.setPassword("sa");
return dataSource;
}
#Bean
public PlatformTransactionManager hibernateTransactionManager() {
HibernateTransactionManager transactionManager
= new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
private final Properties hibernateProperties() {
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty(
"hibernate.hbm2ddl.auto", "create-drop");
hibernateProperties.setProperty(
"hibernate.dialect", "org.hibernate.dialect.H2Dialect");
return hibernateProperties;
}
}
Want to reach the effect:
#Service
#Transactional
public class TestRepositoryImpl implements TestRepository{
#Qualifier("connectionA")
#Autowired
private SessionFactory sessionFactory;
#Qualifier("connectionB")
#Autowired
private SessionFactory sessionFactory;
...
...
}
Another project example:
#Configuration
public class DataSourceConfig {
#Primary
#Bean(name = "test1DataSource")
#ConfigurationProperties(prefix = "spring.datasource.test1")
public DataSource test1DataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "test2DataSource")
#ConfigurationProperties(prefix = "spring.datasource.test2")
public DataSource test2DataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Autowired
#Bean(name = "test1Connection")
public NamedParameterJdbcTemplate test1JdbcTemplate(#Qualifier("test1DataSource") DataSource dataSource) {
return new NamedParameterJdbcTemplate(dataSource);
}
#Autowired
#Bean(name ="test2Connection")
public NamedParameterJdbcTemplate test2JdbcTemplate(#Qualifier("test2DataSource") DataSource dataSource) {
return new NamedParameterJdbcTemplate(dataSource);
}
}
Any help is greatly appreciated!
If you want to achieve hibernate with two Database: use session factory like below.
But i recommend use Spring Data JPA it will give more specification and easier to understand.
public class Transfer {
public static void main(String[] args) {
// get the SessionFacotry
SessionFactory connectionAFactory = HibernateConf.getSessionFactory();
SessionFactory connectionBFactory= DataSourceConfig.getSessionFactory();
// get The Session
Session connectionASes = HibernateConf.getSession();
Session connectionBSes = DataSourceConfig.getSession();
For this you can configure your data connection beans like this:
#Configuration
public class DataSourceConfig {
#Primary
#Bean(name = "test1DataSource")
#ConfigurationProperties(prefix = "spring.datasource.test1")
public DataSource test1DataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "test2DataSource")
#ConfigurationProperties(prefix = "spring.datasource.test2")
public DataSource test2DataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Autowired
#Bean(name = "test1Connection")
public NamedParameterJdbcTemplate test1JdbcTemplate(#Qualifier("test1DataSource") DataSource dataSource) {
return new NamedParameterJdbcTemplate(dataSource);
}
#Autowired
#Bean(name ="test2Connection")
public NamedParameterJdbcTemplate test2JdbcTemplate(#Qualifier("test2DataSource") DataSource dataSource) {
return new NamedParameterJdbcTemplate(dataSource);
}
}
After that you can autowire them separately by using #Qualifier in following way in your service class:
#Service
#Transactional
public class TestRepositoryImpl implements TestRepository{
#Qualifier("test1Connection")
#Autowired
private SessionFactory sessionFactory1;
#Qualifier("test2Connection")
#Autowired
private SessionFactory sessionFactory2;
...
...
}
I want to configure Spring Boot to use 2 JNDI datasources. I tried this configuration:
application.properties
spring.production-datasource.jndi-name=java:/global/production_gateway
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = update
spring.warehouse-datasource.jndi-name=java:/global/production_warehouse
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = update
primary database
#Configuration
#EnableJpaRepositories(
basePackages = "org.datalis.plugin.production.entity",
entityManagerFactoryRef = "productionEntityManager",
transactionManagerRef = "productionTransactionManager"
)
#EnableTransactionManagement
public class ContextProductionDatasource {
#Autowired
private Environment env;
#Primary
#Bean
#ConfigurationProperties(prefix="spring.production-datasource")
public DataSource productionDataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean
public EntityManager productionEntityManager(EntityManagerFactory emf) {
return emf.createEntityManager();
}
#Primary
#Bean
public PlatformTransactionManager productionTransactionManager(final EntityManagerFactory emf) {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Primary
#Bean
public PersistenceExceptionTranslationPostProcessor productionExceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
second datasource:
#Configuration
#EnableJpaRepositories(
basePackages = "org.datalis.plugin.warehouse.entity",
entityManagerFactoryRef = "warehouseEntityManager",
transactionManagerRef = "warehouseTransactionManager"
)
#EnableTransactionManagement
public class ContextWarehouseDatasource {
#Autowired
private Environment env;
#Primary
#Bean
#ConfigurationProperties(prefix="spring.warehouse-datasource")
public DataSource warehouseDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public EntityManager warehouseEntityManager(EntityManagerFactory emf) {
return emf.createEntityManager();
}
#Bean
public PlatformTransactionManager warehouseTransactionManager(final EntityManagerFactory emf) {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor warehouseExceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
When I deploy the code I get exception:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: more than one 'primary' bean found among candidates: [productio nDataSource, warehouseDataSource]"}}
Full error stack:
https://pastebin.com/EsNp2Fp9
Do you know how I can solve this issue?
You can have only 1 primary Datasource. Other confuguration looks fine.
Assuming ContextWarehouseDatasource as secondary connection
Remove #Primary from warehouseDataSource() like this and try.
#Bean
#ConfigurationProperties(prefix="spring.warehouse-datasource")
public DataSource warehouseDataSource() {
return DataSourceBuilder.create().build();
}
I'm trying to truncate a table with jpa when having multiple datasources configuartion, the command seems to be committed but actually- the data from the table is still persists and not deleted.
My code snippets below:
relevant datasource configuration
#Configuration
#EnableJpaRepositories(basePackages = "com.haaretz.jobs.newsletter_preferences_job.data.newsletter_alerts")
public class NewsletterAlertsConfig {
private Environment environment;
#Autowired
public NewsletterAlertsConfig(Environment environment){
this.environment = environment;
}
#Primary
#Bean(name = "newsletterAlertsDataSource")
public DataSource newsletterAlertsDataSource() {
return DataSourceBuilder
.create()
.driverClassName(environment.getProperty("datasource.newsletterAlerts.driverClassName"))
.url(environment.getProperty("datasource.newsletterAlerts.jdbcUrl"))
.username(environment.getProperty("datasource.newsletterAlerts.username"))
.password(environment.getProperty("datasource.newsletterAlerts.password"))
.build();
}
#Primary
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("newsletterAlertsDataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("com.haaretz.jobs.newsletter_preferences_job.data.newsletter_alerts")
.persistenceUnit("newsletterAlerts")
.build();
}
}
second datasource configured
#Configuration
#EnableJpaRepositories(basePackages = "com.haaretz.jobs.newsletter_preferences_job.data.gstat",
entityManagerFactoryRef = "gstatEntityManagerFactory")
public class GstatConfig {
private Environment environment;
#Autowired
public GstatConfig(Environment environment){
this.environment = environment;
}
#Bean(name = "gstatDataSource")
public DataSource gstatDataSource() {
return DataSourceBuilder
.create()
.driverClassName(environment.getProperty("datasource.gstat.driverClassName"))
.url(environment.getProperty("datasource.gstat.jdbcUrl"))
.username(environment.getProperty("datasource.gstat.username"))
.password(environment.getProperty("datasource.gstat.password"))
.build();
}
#Bean(name = "gstatEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean gstatEntityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("gstatDataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("com.haaretz.jobs.newsletter_preferences_job.data.gstat")
.persistenceUnit("gstat")
.build();
}
}
repository
#Repository
public interface NewsletterAlertsRepository extends CrudRepository<NewsletterOpPersonalNl, String> {
#Modifying
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Query(
value = "truncate table OP_PERSONAL_NL",
nativeQuery = true
)
void truncate();
}
the job that activates the deletion
#Component
public class NewsletterPreferencesJob {
private NewsletterAlertsRepository newsletterAlertsRepository;
#Autowired
public NewsletterPreferencesJob(NewsletterAlertsRepository newsletterAlertsRepository) {
this.newsletterAlertsRepository = newsletterAlertsRepository;
}
#Transactional
public void doTask() {
newsletterAlertsRepository.truncate();
}
}
I've also tried to use crudRepoditory default function, which returns the same result (commited but not actually deleted):
newsletterAlertsRepository.deleteAll();
or using a transaction manager, which also doesn't actually deletes the data
TransactionTemplate transactionTemplate = new TransactionTemplate(platformTransactionManager);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
entityManager.createNativeQuery("truncate table OP_PERSONAL_NL").executeUpdate();
}
});
I'm trying to configure two H2 datasource. I tried dozen of tutorials but it still does not works. Now I have two configuration classes
First, default configuration:
#Configuration
#EnableJpaRepositories(
basePackages = "com.yyy.xxx.repository",
entityManagerFactoryRef = "defaultEntityManagerFactory",
transactionManagerRef = "defaultTransactionManager")
#EnableTransactionManagement
public class DefaultJpaConfiguration {
private final JpaVendorAdapter jpaVendorAdapter;
public DefaultJpaConfiguration(JpaVendorAdapter jpaVendorAdapter) {
this.jpaVendorAdapter = jpaVendorAdapter;
}
#Bean(name = "defaultDataSource")
#ConfigurationProperties(prefix = "datasource.default")
public DataSource defaultDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "defaultEntityManagerFactory")
public EntityManagerFactory defaultEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(defaultDataSource());
lef.setJpaVendorAdapter(jpaVendorAdapter);
lef.setPackagesToScan("com.yyy.xxx.entity");
lef.setPersistenceUnitName("defaultPersistenceUnit");
lef.afterPropertiesSet();
return lef.getObject();
}
#Bean(name = "defaultTransactionManager")
public PlatformTransactionManager defaultTransactionManager() {
return new JpaTransactionManager(defaultEntityManagerFactory());
}
#Bean(name = "defaultEntityManager")
public EntityManager defaultEntityManager() {
return defaultEntityManagerFactory().createEntityManager();
}
}
And the second one:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
basePackages = "com.yyy.xxx.adminPassword.repository",
entityManagerFactoryRef = "passwordEntityManagerFactory",
transactionManagerRef = "passwordTransactionManager")
public class PlkPasswordsDatabaseConfiguration {
private final JpaVendorAdapter jpaVendorAdapter;
public PlkPasswordsDatabaseConfiguration(JpaVendorAdapter jpaVendorAdapter) {
this.jpaVendorAdapter = jpaVendorAdapter;
}
#Bean(name = "passwordDataSource")
#ConfigurationProperties(prefix = "datasource.adminPasswords")
public DataSource passwordDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "passwordEntityManagerFactory")
public EntityManagerFactory passwordEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(passwordDataSource());
lef.setJpaVendorAdapter(jpaVendorAdapter);
lef.setPackagesToScan("com.yyy.xxx.adminPassword.entity");
lef.setPersistenceUnitName("passwordPersistenceUnit");
lef.afterPropertiesSet();
return lef.getObject();
}
#Bean(name = "passwordEntityManager")
public EntityManager passwordEntityManager() {
return passwordEntityManagerFactory().createEntityManager();
}
#Bean(name = "passwordTransactionManager")
public PlatformTransactionManager passwordTransactionManager() {
return new JpaTransactionManager(passwordEntityManagerFactory());
}
}
Properties file:
datasource.default.url=jdbc:h2:mem:xxx_db;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false
datasource.default.username=sa
datasource.default.password=
datasource.adminPasswords.url=jdbc:h2:mem:xxx_plk_passwords_db;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false
datasource.adminPasswords.username=sa
datasource.adminPasswords.password=
The error I got say:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available
But as you can see in configuration classes entityManagerFactoryRef is set.
when you have multiple datasources, EntityManager and PlatformTransactionManager, spring expects you to specify which one of it is the primary one. Add #Primary on one of the datasource, entitymanager and transactionmanager(doesn't matter which) bean
I have to connect to two databases (PostgreSQL , Oracle) which contains same tables.
When I create entities of same tables in different packages it doesn't work.
Even though using two database connections the application always points to one database connection only.
Is it possible in Hibernate to connect to same tables from different databases?
application.properties
#DataSource settings for Postgres
datasource.secondary.url =jdbc:postgresql://localhost:5433/****
datasource.secondary.username =postgres
datasource.secondary.password =Postgre#1234
datasource.secondary.driverClassName=org.postgresql.Driver
datasource.secondary.dialect=org.hibernate.dialect.PostgreSQLDialect
#DataSource settings for oracle
datasource.primary.url = jdbc:oracle:thin:#localhost:1521:xe
datasource.primary.username = ***
datasource.primary.password = ***
datasource.primary.driverClassName=oracle.jdbc.OracleDriver
Configuration
#Configuration
public class MultipleDBConfig {
#Primary
#Bean(name = "oracleDb")
#ConfigurationProperties(prefix = "datasource.primary")
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "postgresDb")
#ConfigurationProperties(prefix = "datasource.secondary")
public DataSource postgresDataSource() {
return DataSourceBuilder.create().build();
}
}
Primary
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "primaryEntityManager",
transactionManagerRef = "primaryEntityManagerFactory",
basePackages = {"com.ubl.model.*"})
public class PrimaryDBConfig {
#Bean(name = "primaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] {"com.ubl.model.migration.entity.oracle"});
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalJpaProperties());
em.setPersistenceUnitName("customers");
return em;
}
Properties additionalJpaProperties(){
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "update");
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.OracleDialect");
properties.setProperty("hibernate.show_sql", "true");
return properties;
}
#Bean
public DataSource dataSource(){
return DataSourceBuilder.create()
.url("jdbc:oracle:thin:#localhost:1521:xe")
.driverClassName("oracle.jdbc.OracleDriver")
.username("****")
.password("****")
.build();
}
#Bean(name = "primarytransactionManager")
public JpaTransactionManager transactionManager(EntityManagerFactory customerEntityManager){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(customerEntityManager);
return transactionManager;
}
}
Secondary
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "secondaryEntityManagerFactory",
transactionManagerRef = "secondaryTransactionManager",
basePackages = {"com.ubl.*"})
public class SecondaryDBConfig {
#Autowired
JpaVendorAdapter jpaVendorAdapter;
#Value("${datasource.secondary.url}")
private String databaseURL;
#Value("${datasource.secondary.username}")
private String username;
#Value("${datasource.secondary.password}")
private String password;
#Value("${datasource.secondary.driverClassName}")
private String driverClassName;
#Value("${datasource.secondary.dialect}")
private String dialect;
public SecondaryDBConfig() {
System.out.println("Secondary repository");
System.out.println("driverClassName: *************" +driverClassName);
}
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource(databaseURL, username, password);
dataSource.setDriverClassName(driverClassName);
return dataSource;
}
#Bean(name = "secondaryEntityManager")
public EntityManager entityManager() {
return entityManagerFactory().createEntityManager();
}
#Bean(name = "secondaryEntityManagerFactory")
public EntityManagerFactory entityManagerFactory() {
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", dialect);
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource(dataSource());
emf.setJpaVendorAdapter(jpaVendorAdapter);
emf.setPackagesToScan("com.ubl.model.*"); // package for entities
emf.setPersistenceUnitName("secondaryPersistenceUnit");
emf.setJpaProperties(properties);
emf.afterPropertiesSet();
return emf.getObject();
}
#Bean(name = "secondaryTransactionManager")
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager(entityManagerFactory());
}
}
When I run the application I get below error:
Caused by: org.hibernate.tool.schema.extract.spi.SchemaExtractionException: More than one table found in namespace (, )
Your second config appears to use the same namespace as your first:
basePackages = {"com.ubl.model.*"}
basePackages = {"com.ubl.*"}
Once your second config looks for it's entities, it discovers the same as the fist does and thus resulting in the exception. You will want to separate your entities and both your configs.
basePackages = {"com.ubl.model.datasource1"}
basePackages = {"com.ubl.model.datasource2"} // well you get the idea and will find better names ;)
Then move all your entities in the respective folder. Although the table are "the same" you need one #Entity-Class for each individual Table, even though the tables you will want to use are structurally identical.