Dynamic dialect with Hibernate and Hikari - java

I am new at Hibernate.
Into my code, the connection to the DB is managed with the Hikari data source.
My code is right now multitenant, but it manages the same hibernate dialect for all tenants.
Is it possible to create a configuration where each tenant can use a different dialect?
The type of dialect can be provided as a tenant's property.
This is an example of the entityManagerFactory:
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
Map<String, Object> jpaProperties = new HashMap<>();
jpaProperties.put(..., ...);
jpaProperties.put(org.hibernate.cfg.Environment.DIALECT, "myDialect");
LocalContainerEntityManagerFactoryBean emfBean = new LocalContainerEntityManagerFactoryBean();
emfBean.setPackagesToScan(new String[] {MyEntity.class.getPackage().getName()});
emfBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
emfBean.setJpaPropertyMap(jpaProperties);
return emfBean;
}
Edit
I was looking to this solution: it suggests to create a duplicated LocalContainerEntityManagerFactoryBean for each dialect.
What I do not understand is how can I tell when using one EntityManager (MySQL) and when the other one (Postgres or MsSQL): the solution discriminates the entities (each entity has its own DB) but in my case, all entities are on all DBs. Is the tenant that discriminates.
For example: if I create a second instance of LocalContainerEntityManagerFactoryBean (i.e. msSQLEntityManagerFactory()) with setted the dialect for SQL Server, the application fails to start with:
Application failed to start due to an exceptionorg.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type 'javax.persistence.EntityManagerFactory' available:
expected single matching bean but found 2:
msSQLEntityManagerFactory,entityManagerFactory

That's not really possible as the dialect affects certain quoting rules etc. which you can't just "swap out" at runtime based on a tenant identifier. Just create two persistence units, each pointing to a different data source for every database type. You will have to somehow lookup the appropriate EntityManager/EntityManagerFactory based on your tenant identifier which makes it a bit harder when you want to use Spring Data JPA as that requires a compilation static name reference for the entity manager factory. Maybe you can create a custom EntityManagerFactory that delegates all method calls to the appropriate instance based on the tenant identifier. Overall, this is not so easy and you will probably have to do a lot of trial and error.
IMO it would be better to have a separate application deployment with separate configuration if possible per database type.

I finally managed to find a solution for this problem.
I managed to get around the problem with the dialects by having an entity manager factory for each dialect (in this case MySQL, Postgres and MS SQL Server).
Create a bean for EntityManagerFactory and return a proxy of that interface and in the handler, based on your logic, you can switch which emf to use to suit the used data source.
I have created a video for this because it seems like there is no documentation online.
Session Scoped Connection
It is pretty similar to what you're trying to achieve but in my case the users are providing the credentials, so it's even more complicated.

Related

Spring Transaction Manager and DataSource Java Config way to pass same bean id without creating a new instance

Goal
I want to introduce a transaction manage on my data source.
Looking for the correct way to use same instance of datasource for the transaction manager as well. My requirement is specify to Java Config way to pass the "Same instance" of DS to Transaction Manager. Correct me If there is a gap in my understanding.
In my case I have a datasource and of type autocommit false, and by using the Transaction Manager specified below, I want to commit/rollback a transaction (e.g. Update an operation/Revert an Update operation ---when there a error/no error in the transaction).
However, while debugging I have noticed that when I used java config specified below, I get two different instance of data source and trx.commit() does not work.
Programmatic transaction management
(https://docs.spring.io/spring/docs/3.0.0.M4/reference/html/ch10s06.html)
#Bean
public DataSource dataSource() {
return getMyDataSource(); //new instance of datasource.//this datasource is autocommit-false
}
#Bean
public DataSourceTransactionManage trxManager() {
return getTransationManage(dataSource()); // this creates another instance of dataSource
}
Any help in this regard is highly appreciated.
Edit :-
I was using Mybatis with Spring. Basically, I had to configure the DataSouce correctly. Below links were useful.
[Pass parameters dynamically to Spring beans ][1] [Mybatis Transaction
Management CTM and PTM ][2] [Spring Transaction Management Notes
][3] [Spring & JTA NOtes][4]
[1]: https://stackoverflow.com/a/21202458/5086633
[2]: http://www.mybatis.org/spring/transactions.html
[3]: https://docs.spring.io/spring/docs/3.0.0.M4/reference/html/ch10s06.html
[4]: https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#transaction
To use back the same instance of dataSource how about you do this:
#Bean
#Autowired
public DataSourceTransactionManage trxManager(DataSource dataSource) {
return getTransationManage(dataSource);
}

Dynamically routed sqlSessionFactory in mybatis

Is it possible to choose Mapper to execute in Mybatis?
I am using dynamically routed datasource and give it as parameter to sqlSessionFactory - it is working.
The problem is, however, that different databases may require other SQL queries (very small differences, but differences).
Is it possible to create dynamically routed sqlSessionFactory - and give to them other package mappers?
This is how you obtain a session factory:
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
A couple of things are declared in in mybatis-config.xml, amongst whom are the datasources and the mappers.
Then create another Mybatis config file (with its own datesource and mappers) and dynamically choose the file to build the session factory. You can still provide the connection when opening the session, although I recommend using the implicit one provided by the factory because it will match with the mapper. (Unless this is a complex application where you need to reuse the connection, e.g: to complete a transaction)

how can I use my dynamic JNDI datasources with JPA?

We have an application which uses multiple databases to store the same data for different countries.
For example a Subscription object might be associated with Germany or Spain. If it's a German subscription, it needs to be stored in a different database to the Spanish subscriptions. The databases are identical in structure, but they have different contents.
We are running on jboss 5, and have a different datasource config (*ds.xml) file for each one, generated dynamically at startup. They are stored in JNDI - so we have DataSourceDE, DataSourceES, etc.
Here's how it should work: if a request comes in saying 'fetch subscription 17 for Germany' then I calculate that the datasource should be DataSourceDE and use JPA / hibernate to go fetch that object from the correct database. There will be a subscription 17 in the Spanish database too, which I don't want in this example.
I can generate the persistence.xml automatically to create the extra persistence units for the datasources, but the Subscription class is annotated with the following:
#PersistenceContext(unitName="core")
This is not going to work - how can I set the persistence context on the java object dynamically?
What you are trying to achieve is known as Multi-Tenancy. Here is a perfectly suitable tutorial for your question to make it work.
The main idea is to use a Stateless session bean which has a reference to both persistence units. Depending on what has to be done, this bean does a lookup to call the corresponding EntityManager. Furthermore here:
Multi-Tenancy With EJB 3.1 and JPA 2.0
You can change the persistence context for the EntityManager at runtime like this:
EntityManagerFactory emf =
Persistence.createEntityManagerFactory(persistenceUnitName);
EntityManager em = emf.createEntityManager();

Web application with one schema per User

I am trying to enable my web application to use one (MySQL) schema per user.
I am using Spring with JPA along with Hibernate.
Is the Hibernate Multi-tenancy concept relevant?
The way I am trying to do it is
(a) have a EntityManagerFactory per HTTPSession and
(b) set the schema in it at login.
I have figured out how to do (b), but I still have issues doing (a).
I tried putting #Scope(WebApplicationContext.SCOPE_SESSION), but what about the global EntityManagerFactory?
Any help?
You can implement your own ConnectionProvider and there make extra settings.
But I think that you have large problems with the architecture your application if you want to do one schema per user.
UPD1
If you use spring. You can try to declare a bean with own impl of ConnectionProvider in Session scope.
But there is a big problem. Hibernate make the ConnectionProvider. It means that you have to impl own ServiceRegistry (with working through Spring) and override StandardServiceRegistryBuilder, and impl EntityManagerFactoryBuilder (based on EntityManagerFactoryBuilderImpl but with your StandardServiceRegistryBuilder).
When new session have been created it'll use ConnectionProvider to create the connection (Probably you'll have to override some classes).
This is really not suggested.. This will harm you later, you can't do differnt schema for diffrent user. You can always create you own properties to the connection..
Configuration cfg = new Configuration();
cfg.configure();
System.setProperty("hibernate.connection.password",pass);
System.setProperty("hibernate.connection.username",usr);
System.setProperty("hibernate.connection.driver_class", driver_class);
System.setProperty("hibernate.connection.url", driver_url);
System.setProperty("hibernate.dialect", dialect);
// etc, etc, for all properties
cfg.setProperties(System.getProperties());
sessionFactory = cfg.buildSessionFactory();
It can be something like that.. But this is WRONG!

OSGi and Persistence Units

Good day guys, I'm programming an OSGi bundle in charge of authenticating users. For design reasons I'm obligated to perform queries over multiple database schemas (these schemas can be created or deleted dynamically). I'm running MySQL as storage engine.
Somehow I need to be able to create on demand entity managers for those schemas, but I haven't been successful in my attempts. Here is what I have tried that has got me closer to what I need:
Creating a persistence unit (Eclipselink) using a JTA data source that can, in fact, establish a database connection to the default schema. However, when I try to override any property, say, the javax.persistence.jdbc.url. However, it keeps pointing to the default schema always.
I believe I'm not overriding correctly the property, or that JTA datasource properties cannot be modified from EntityManager to EntityManager. Here is how I create the EntityManagers:
Map<String, String> dbProps = new HashMap<String, String>();
dbProps.put("javax.persistence.jdbc.url","jdbc:mysql://mydomain:3306/mydynamicdb);
EntityManagerFactory fact = Persistence.createEntityManagerFactory("myPersistenceUnit", dbProps);
EntityManager myEM = fact.createEntityManager();
At the end, they all keep poiting to the default schema, so my questions are:
Is this an efficient approach for dynamic EntityManager handling? if so, how can I override effectively the schema property?
Is there any other alternative besides the overriding?
I thank you in advance for any guidance you may provide.
If you want to use EclipseLink in OSGi you must use the Gemini JPA project that wraps EclipseLink and create and register an EntityManagerFactory and an EntityManagerFactoryBuilder services for your PU bundle.
If want to share the jdbc connections between PUs you can use JDBC services provided by Gemini DBAccess
You should be able to acquire the EntityManagerFactory as an OSGi service, you can (LDAP) filter the correct one using the service property osgi.unit.name as in:
ServiceReference[] refs = null;
String filter = "(osgi.unit.name=myPersistenceUnit)";
ServiceReference[] refs = ctx.getServiceReferences(EntityManagerFactory.class.getName(), filter);
//Should only be one reference, check (throw exception etc)
return (EntityManagerFactory)ctx.getService(refs[0]);
You can check that all Persistence Units are present, by listing the EntityManagerFactory services (with properties) in your OSGi shell.

Categories