Hibernate MultiTenancy with spring jpa - spring

I want to add multiple tenant into my application using separate schema.My application is based on spring jpa and hibernate. I implement MultiTenantConnectionProvider and CurrentTenantIdentifierResolver.
And my configuration class is:
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource){
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setPackagesToScan("com.**.api.entity");
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
entityManagerFactory.setJpaVendorAdapter(hibernateJpaVendorAdapter);
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.globally_quoted_identifiers",true);
jpaProperties.put("hibernate.dialect",org.hibernate.dialect.MySQL5Dialect.class);
jpaProperties.put("hibernate.multi_tenant_connection_provider",multiTenantConnectionProvider);
jpaProperties.put("hibernate.tenant_identifier_resolver",currentTenantIdentifierResolver);
jpaProperties.put("hibernate.multiTenancy","SCHEMA");
entityManagerFactory.setJpaProperties(jpaProperties);
return entityManagerFactory;
}
And I use MapDataSourceLookup to save dataSources.It works.But it's a little problem. I must assign the packagesToScan.I want it to be a basic service.And some application depend on it.It seems that assign the packagesToScan is not a good practice.
Is there any better way to do it?

Related

spring jpa batch update doesn't work - not spring boot

I have a project, which is not a spring boot project, but it uses different spring framework libraries (spring-data-jpa, spring-security-web, sping-mvc, etc), and hibernate-core, hibernate-entitymanager.
The database is postgresql.
I have difficulties setup database batch processing for updates. Practically running JpaRepository.saveAll method with a List
tried to setup properties in application.properties file:
spring.jpa.properties.hibernate.generate_statistics=true
spring.jpa.properties.hibernate.jdbc.batch_size=10
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
doesn't work
tried to setup properties in the ApplicationContext class as entityManagerFactoryBean
#Bean(name = "entityManagerFactory")
#DependsOn("flyway")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
...
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.generate_statistics", "true");
jpaProperties.put("hibernate.jdbc.batch_size", "25");
jpaProperties.put("hibernate.order_inserts", "true");
jpaProperties.put("hibernate.order_updates", "true");
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
also doesn't work.
but even the statistics are not reporting, though i have setup logging.level.org.hibernate=INFO
what is the solution if there is no spring-boot?

How can i connect to 2 different Schemas on one datasource in springboot

I am trying to extract data from multiple databases in different servers. This has been successful after configuring different Data sources as shown in my code however i cannot see to configure 2 different Data sources on the same connection.
I have tried to create 2 beans within the config file but i would still need to set the Data source.
#Configuration
#EnableJpaRepositories(basePackages = {"balances.Repository.World"},
entityManagerFactoryRef = "ASourceEntityManager",transactionManagerRef = "ASourceTransactionManager")
#EnableTransactionManagement
public class World{
#Autowired
private Environment env;
#Bean
public LocalContainerEntityManagerFactoryBean ASourceEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(ASourceDatasource());
em.setPackagesToScan(new String("balances.Repository.World"));
em.setPersistenceUnitName("ASourceEntityManager");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.dialect",env.getProperty("app.datasource.ASource.hibernate.dialect"));
properties.put("hibernate.show-sql",env.getProperty("app.datasource.ASource.show-sql"));
em.setJpaPropertyMap(properties);
return em;
}
#Bean
#ConfigurationProperties(prefix = "app.datasource.ASource")
public DataSource ASourceDatasource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("app.datasource.ASource.driverClassName"));
dataSource.setUrl(env.getProperty("app.datasource.ASource.url"));
dataSource.setUsername(env.getProperty("app.datasource.ASource.username"));
dataSource.setPassword(env.getProperty("app.datasource.ASource.password"));
return dataSource;
}
#ConfigurationProperties(prefix = "app.datasource.ASourceB")
public DataSource ASourceDatasource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("app.datasource.ASourceB.driverClassName"));
dataSource.setUrl(env.getProperty("app.datasource.ASourceB.url"));
dataSource.setUsername(env.getProperty("app.datasource.ASourceB.username"));
dataSource.setPassword(env.getProperty("app.datasource.ASourceB.password"));
return dataSource;
}
#Bean
public PlatformTransactionManager ASourceTransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
ASourceEntityManager().getObject());
return transactionManager;
}
After some thought i have come up with a workaround that might be a bit hacky but gets the job done.
Instead of declaring a new database connection for the second schema in my app.properties i have used one connection. I placed all my entities in one model package and used a native query to access the other schema. In the native query i would then specify the schema eg:
select * from DATABASE1.Id;
This solution does not scale well as it would create a lot of work when dealing with a large number of entities so if there is a way of specifying the schema in the repository that would also help.
I tried using the entity attributes to define my schema but jpa seems to be ignoring it and prefixing the table with the wrong schema for example if
i annotate my class with the following
#Table(name = "Payment", schema = "DATABASE1", catalog = "")
The resulting query would be "select * from DATABASE2.Payment" instead of
select * from DATABASE1.Payment

Spring Boot - Use underscored table and column names for custom data source

I am trying to configure two data sources for a Spring Boot project. Here is my configuration file for primary data source.
#PropertySource({"classpath:application-local.properties"})
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "primaryEntityManager",
basePackages = {"portal.api.repository"}
)
public class PrimaryDbConfig {
#Autowired
private Environment env;
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean primaryEntityManager() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(primaryDataSource());
em.setPackagesToScan(
new String[]{"portal.api.model"});
HibernateJpaVendorAdapter vendorAdapter
= new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto",
env.getProperty("app.datasource.spring.jpa.hibernate.ddl"));
properties.put("hibernate.dialect",
env.getProperty("hibernate.dialect"));
em.setJpaPropertyMap(properties);
return em;
}
#Primary
#Bean
public DataSource primaryDataSource() {
DriverManagerDataSource dataSource
= new DriverManagerDataSource();
dataSource.setDriverClassName(
env.getProperty("app.datasource.driver-class-name"));
dataSource.setUrl(env.getProperty("app.datasource.url"));
dataSource.setUsername(env.getProperty("app.datasource.username"));
dataSource.setPassword(env.getProperty("app.datasource.password"));
return dataSource;
}
#Primary
#Bean
public PlatformTransactionManager userTransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
primaryEntityManager().getObject());
return transactionManager;
}
}
The tables are generated but their names and column names are in camel case. However I would like to have the underscore names which spring boot has by default. For example ApiKey entity would be changed to table name api_key.
How do I configure Spring Boot to use the underscored naming strategy?
I think if you use #Column(name = "api_key") in your instance variable for your domain model it'll work.
e.g.
#Column(name = "api_key")
private String apiKey;
Reference from Official doc
By default, Spring Boot configures the physical naming strategy with
SpringPhysicalNamingStrategy. This implementation provides the same
table structure as Hibernate 4: all dots are replaced by underscores
and camel casing is replaced by underscores as well.
You can set the following property in application.yml to tell springboot which naming strategy to use:
spring:
jpa:
hibernate:
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
Similar post for reference:
java.sql.SQLSyntaxErrorException: Unknown column .JPA Entity Issue?

Integrating Hibernate OGM and Spring Data JPA

How can we integrate Hibernate OGM with Spring Data JPA, so that existing application containing Hibernate ORM can be used with Hibernate OGM.
This is the configuration i'm currently using for Hibernate ORM
#Bean(name = "jdbc")
public DriverManagerDataSource getDriverManager() {
DriverManagerDataSource driverManagerDataSource = new
DriverManagerDataSource(
env.getProperty(dataBase + ".url"), env.getProperty(dataBase +
".username"),
env.getProperty(dataBase + ".password"));
driverManagerDataSource.setDriverClassName(env.getProperty(dataBase +
".driver"));
return driverManagerDataSource;
}
#Bean(name = "japaImplementation")
public HibernateJpaVendorAdapter getHibernate() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabasePlatform(env.getProperty(dataBase + ".dialect"));
vendorAdapter.setGenerateDdl(true);
vendorAdapter.setShowSql(true);
return vendorAdapter;
}
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean
getEntityManagerFactoryBean() {
LocalContainerEntityManagerFactoryBean factoryBean = new
LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(getDriverManager());
factoryBean.setJpaVendorAdapter(getHibernate());
factoryBean.setPackagesToScan("com.xyz.abc.entity");
return factoryBean;
}
I just had a quick glance at Hibernate OGM. It seems it is just a JPA implementation. So Spring Data JPA should work out of the box for it. Although up to now it does not get tested against it.
But it raises the question, why you would want to do that? There are already Spring Data modules for most, if not all of the data stores supported by Hibernate OGM. So why don't you use Spring Data MongoDB directly?
The projects are more alternatives to each other than something to let one run on top of the other. See this question for arguments from both sides.

AbstractRoutingDatasource and hibernate.dialect with multiples datasources

In my Spring Java Config file,i use AbstractRoutingDatasource successfully to switch multiple databases connections.
public MyRoutingDataSource myRoutingDataSource() {
MyRoutingDataSource dataSource = new MyRoutingDataSource();
Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
dataSource.setDefaultTargetDataSource(defaultDataSource());
dataSource.setTargetDataSources(targetDataSources);
return dataSource;
}
But when i want specific SQL requests that differs from datasources PostgreSQL/Oracle etc.. (like pagination limit/rowNums), i have to associated a specific hibernate dialect.
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(false);
vendorAdapter.setShowSql(true);
vendorAdapter.setDatabasePlatform("org.hibernate.dialect.PostgreSQL81Dialect");
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("fr.appli.model");
factory.setDataSource(myRoutingDataSource());
My question is how can i change hibernate dialect in my EntityManager, when i change datasource from with AbstractRoutingDatasource ? Is it possible to do it programmatically ?
Thanks
I think to achive this you will need multiple datasource configuration files and each database has its own SessionFactory object in an DAO which will permof DB operations.
For example :
SessionFactory sessionFactory1 = new Configuration().configure("oracleconfig.cfg.xml").buildSessionFactory();
SessionFactory sessionFactory2 = new Configuration().configure("mysqlconfig.cfg.xml").buildSessionFactory();

Resources