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!
Related
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.
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)
I have to configure two database on same project by configuring two CFG file,
I tried but it is always use the first configuration file,
May i know how can i use two database on same project
In your code, what you need to do is to open two different session factory for different databases.
For example:
Configuration configA=new Configuration();//use the default hibernate.cgf.xml file
Congiruration configB=new Configuration.configure('/hibernate_db2.cfg.xml') // use hibernate_db2.cfg.xml under root folder.
SessionFactory sfa=configA.buildSessionFactory();
SessionFactory sfb=configB.buildSessionFactory();
Now, you open different session using different db.
You need to have two configuration files.
hibernate-mysql.cfg.xml
hibernate-oracle.cfg.xml
And code should be like this.
mysql configuration
private static SessionFactory sessionAnnotationFactory;
sessionAnnotationFactory = new Configuration().configure("hibernate-mysql.cfg.xml").buildSessionFactory();
Session session = sessionAnnotationFactory.openSession();
oracle sql configuration
sessionAnnotationFactory = new Configuration().configure("hibernate-oracle.cfg.xml").buildSessionFactory();
Session session = sessionAnnotationFactory.openSession()
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.
I'd like to provide the ability to completely wipe my webapp's database from its GUI, rebuilding the tables with the auto-generated DDL gleaned from an AnnotationConfiguration.
At the moment I do this by programatically setting hibernate.hbm2ddl.auto to create and then creating a new SessionFactory.
AnnotationConfiguration cfg = new AnnotationConfiguration();
if (nukeDB)
cfg.setProperty("hibernate.hbm2ddl.auto", "create");
for (Class<?> clas : HibernateMappedClasses) {
cfg.addAnnotatedClass(clas);
}
return cfg.buildSessionFactory();
but this requires creating a fresh AnnotationConfiguration from the one that Spring is building for the rest of the app.
Can I use my existing SessionFactory to recreate the database schema?
This isn't simple in raw Hibernate, but given that I'm using Spring, it turns out that the LocalSessionFactoryBean has methods dropDatabaseSchema and createDatabaseSchema that together do the trick.
Without Spring I think I'd copy the code in those methods!