Change current schema before using spring data Repository - Multi tenancy - java

I am using a multi tenant spring boot web application which reads some data from a database.
I have built schema per tenant model, implementing MultiTenantConnectionProvider and CurrentTenantIdentifierResolver to set connections based on the tenant. The tenant is resolved using a ThreadLocal variable, set in a Filter (built by extending OncePerRequestFilter).
However, I need to solve for a specific case where I do not get the tenant information at the filter. I can however, get the tenant information later while processing the request in a service, but by this time the entity manager is set (seems to be done by OpenSessionInViewFilter looking at spring sources) to use the default schema and all my queries fail because the default schema do not contain the data I need.
My question is, how do I set the entity manager to point to the tenant specific schema at service level, after the hibernate filter has already set the session? I could do whatever the filter (OpenSessionInViewFilter) is doing, something like the below:
EntityManager entityManager = entityManagerFactory.createEntityManager();
TransactionSynchronizationManager.bindResource(entityManager, new EntityManagerHolder(entityManager));
//unbind once I am done using the repositories
But I was thinking if this is the right way or if there is any other better, easier, documented way.

EntityManager entityManager = entityManagerFactory.createEntityManager();
TransactionSynchronizationManager.bindResource(entityManager, new
EntityManagerHolder(entityManager));
The above didn't work for me. However, I found a workaround - by running my task in a different thread (I just created a fixed thread pool executor and submitted my tasks) and setting the new tenant in a ThreadLocal object, I was able to achieve what I wanted. With the right CurrentTenantIdentifierResolver and MultiTenantConnectionProvider implementations, a connection to the expected tenant schema got established.

Related

An equivalent to StickySession in Karaf/OSGI?

The issue is the following: in ma Karaf container, I have two modules, the first one is used to fetch data in the DB via a JPA interface implemented by Hibernate. Collections are fetched in a lazy way.
The second module gets the object containing the collections. When trying to access elements of a collection, an error is thrown:
failed to lazily initialize a collection of role:
mapp3.model.ProductDefinition, could not initialize proxy - no Session
It has no session to access the DB and fetch the missing elements.
I know in J2EE there is a concept of Sticky Session that makes a Thread create and share the same session across all beans.
It there something similar in Karaf/OSGi or is there another way of properly acheiving lazy loading between different modules ?
I have just implemented a similar feature in Aries JPA 2.1.0. It uses the OSGi Coordinator spec to share an EntityManager session on a thread. It works together with Aries transaction blueprint 1.3.0 which now also uses a Coordination. Both are available in Apache Karaf 4.0.1.
So to achieve a session that is kept over several calls between beans you annotate the outermost method involved with #Transactional. If you do not want an actual transaction but just the shared EM then you can use #Transaction with type Support. So starting from this method all calls made downwards will share the same em.
So for example you can do something like this:
#Transactional
public void myServiceMethod() {
Person person = personRepo.getPerson();
List<Task> person.getTask();
}
So in the example above the PersonRepo would inject the EntityManager using #PersistenceContext and work with it.
MyService.myServiceMethod would be on the service layer and should not know about jpa or the EntityManager. Still using the #Transactional annoation the method would provide a coordinaton over the execution of the method that holds the EntityManager.
Also see my example Apache Karaf Tutorial Part 9 - Annotation based blueprint and JPA. The example does not show the overarching #Transactional but should get you started easily if you want to dig into it. As soon as the upcoming Aries releases are done I will also create an example that shows exactly the case you are looking for.

Can we have multiple dataSources to single database

I am having spring webservice application with oracle as a database. Right now i have datasource created using weblogic server. Also using eclipse linkg JPA to do both read and write transactions(insert,Read and update). Now we want to separate dataSources for read(read) and wrtie(insert or update) transactions.
My current dataSource is as followed:
JNDI NAME : jdbc/POI_DS
URL : jdbc:oracle:thin:#localhost:1521:XE
using this, I am doing both read and write transactions.
What if i do the following:
JNDI NAME : jdbc/POI_DS_READ
URL : jdbc:oracle:thin:#localhost:1521:XE
JNDI NAME : jdbc/POI_DS_WRITE
URL : jdbc:oracle:thin:#localhost:1521:XE
I knew that using XA datasource we can define multiple dataSources. Can I do same thing without XA dataSource. Does any one tried this kind of approach.
::UPDATE::
Thank you all for your responses I have implemented following solution.
I have taken the multiple database approach. where you will define multiple transactionManagers and managerFactory. I have taken only single non xa dataSource(JNDI) that is refereed in EntityManagerFactory Bean.
you can reefer following links here which are for multiple dataSources
Multiple DataSource Approach
defining #transactional value
Also explored on transaction managers org.springframework.transaction.jta.WebLogicJtaTransactionManager and org.springframework.orm.jpa.JpaTransactionManager as well.
There is an interesting article about this in Spring docs - Dynamic DataSource Routing. There is an example there, that allows you to basically switch data sources at runtime. It should help you. I'd gladly help you more, if you have any more specific questions.
EDIT: It tells, that the actual use is to have connection to multiple databases via one configuration, but you could manage to create different configs to one database with different params, as you'd need to.
I would suggest using Database "services". Each workload, read-only and read-write, would be using its own service to access the database. That way you can use AWR reports to get statistics for each service. You can also turn off read-write when you keep read-only up and running.
Here is a pointer to the Oracle Database documentation that talks about Services:
https://docs.oracle.com/database/121/ADMIN/create.htm#CIABBCAI
If you're using spring, you should be able to accomplish this without using 2 Datasources via spring #Transactional with the readonly property set to true. The reason why I suggest this is that you seem to be concerned about the transactionality only and this seems to be catered for in the spring framework?
I'd suggest something like this for your case:
#Transactional(readOnly = true)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// do something
}
// these settings have precedence for this method
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
// do something
}
}
Using this style, you should be able to split read only services from their write counterparts, or even have read and write service methods combined. But both of these do not use 2 datasources.
Code is from the Spring Reference
I am pretty sure that you need to address the problem on the database / connection url + properties layer.
I would google around for something like read write replication.
Related to your question with JPA and transaction. You are doomed when you are using multiple Datasources. Also XA datasources are not really a solution for that. The only thing they do for you is to ensure consistency over multi data source operations. XA Transaction do only span some sort of logical transaction over two transactions (one for each datasource). From the transaction isolation point of view (as long as your not using READ_UNCOMMITED) both datasources use their own transaction. This means the read data source would not see the changes made by the write transaction.

How do I access data from my database in Spring Roo?

I've set up Spring Roo following some basic guides, and I have a setup where data from my database can be accessed from a web browser using Roo's standard forms (like in this Youtube video). Now I'd like to access that data from Java code so I can "inject" it into a view from other pages on the site. How is this done?
Edit: Here's how I was able to access my data: From a controller (or any class, really), I use the this annotation along with this property definition:
#PersistenceContext
private EntityManager manager;
Then I can access the data with a query like this:
List<Announcement> results = manager.createQuery("from Announcement a where a.id = :id").setParameter("id", new Long(1)).getResultList();
This will give you a List of type Announcement (which is just an entity I created). Of course this query will yield only one result (or zero if the database doesn't have an entry with an id of 1). Thanks Micha for this solution.
You can use the #PersistenceContext annotation to get a JPA EntityManager instance in your application. Using the EntityManager you can query the database (like showed here). Since you are using Roo the entityManagerFactory bean and transaction support should already be included in your bean configuration file.
You can also use Spring data repositories to access your data.
Maybe this video can help you.

"suspend" hibernate session managed by spring transaction manager

Is there any way to remove/suspend a current spring managed hibernate session from a thread so a new one can be used, to then place the original session back onto the thread? Both are working on the same datasource.
To describe the problem in more detail. I'm trying to create a plugin for a tool who has it's own spring hibernate transaction management. In this plugin I would like to do some of my own database stuff which is done on our own spring transaction manager. When I currently try to perform the database actions our transaction manager starts complaining about an incompatibly transactionmanager already being used
org.springframework.transaction.IllegalTransactionStateException: Pre-bound JDBC Connection found! HibernateTransactionManager does not support running within DataSourceTransactionManager if told to manage the DataSource itself. It is recommended to use a single HibernateTransactionManager for all transactions on a single DataSource, no matter whether Hibernate or JDBC access.
A workaround that seems to do the trick is running my own code in a different thread and waiting for it to complete before I continue with the rest of the code.
Is there a better way then that, seems a bit stupid/overkill? Some way to suspend the current hibernate session, then open a new one and afterworths restoring the original session.
Is there any reason you can't have the current transaction manager injected into your plugin code? Two tx managers sounds like too many cooks in the kitchen. If you have it injected, then you should be able to require a new session before doing your work using the #transactional annotation's propagation REQUIRES_NEW attribute see the documentation for an example set-up
e.g.
#transactional(propogation = Propogation.REQUIRES_NEW)
public void addXXX(Some class) {
...
}
But this would use spring's PlatformTransactionManager rather than leaving it up to hibernate to manage the session / transaction.

How to dynamically manage multiple datasources

Similar topics have been covered in other threads, but I couldn't find a definitive solution to my problem.
What we're trying to achieve is to design a web app which is able to:
read a datasource configuration at startup (an XML file containing multiple datasource definitions, which is placed outside the WAR file and it's not the application-context or hibernate configuration file)
create a session factory for each one of them (considering that each datasource is a database with a different schema)
switch at runtime to different datasources based on user input (users can select which datasource they want to use)
provide the correct dao object to manage user requests.
At the moment we have a DAO Manager object which is able to read the datasource configuration file and instantiate multiple session factories, saving them in a map. Each session factory is created with a configuration containing the proper hibernate mapping classes (different for each database schema). Moreover we have multiple DAO interfaces with their implementations, used to access "their database".
At this point we would need a way to get from the DAO Manager a specific DAO object, containing the right session factory attached, all based on the user request (basically a call from the above service containing the datasource id or a custom datasource object).
Ideally the service layer should use the DAO Manager to get a DAO object based on the datasource id (for instance), without worrying about it's actual implementation: the DAO Manager would take care of it, by creating the correct DAO object and injecting in it the right session factory, based on the datasource id.
My questions are:
Is this a good approach to follow?
How can I use Spring to dynamically inject in the DAO Manager multiple DAO implementations for each DAO interface?
Once the session factories are created, is there a way to let Spring handle them, as I would normally do with dependency injection inside the application-context.xml?
Would the 2nd Level Cache still work for each Session Factory?
Is this a good approach to follow?
It's probably the only possible approach. So, yes.
How can I use Spring to dynamically
inject in the DAO Manager multiple DAO
implementations for each DAO
interface?
Dynamically? I thought you wanted to do it at startup time. If so, just provide an accessor with a list or array:
public void setMyDaos(List<Mydao> daos){
this.daos = daos;
}
Once the session factories are
created, is there a way to let Spring
handle them, as I would normally do
with dependency injection inside the
application-context.xml?
This one's tough. I'd say you will probably have to store your sessionFactory bean in scope=session

Categories

Resources