How to use Eclipselink #Multitenant in JSF/EJB? - java

The #Multitenant support in Eclipselink 2.3 looks really interesting, but I'm having a hard time understanding how to use it in a JSF or EJB which injects an EntityManager with #PersistenceContext. The EclipseLink docs are pretty clear that #PersistenceContext injection doesn't work in this case, but you could inject an EntityManagerFactory via #PersistenceUunit instead.
Still, what I'm not seeing is how to manage the lifecycle of an EntityManager you might create via injected EntityManagerFactory.createEntityManager() - in particular, when to close the resulting EntityManager, and how to participate in transactions.
Has anyone gotten this to work? Or am I missing something obvious?
See also: http://wiki.eclipse.org/EclipseLink/Examples/JPA/Multitenant
UPDATE
I had some success with #PersistenceContext (EntityManager) injection and then passing parameters to EclipseLink via session listener. I'm not 100% sure this is the right answer and would appreciate confirmation that it isn't creating a non-obvious race condition or thread-safety issue.
For example:
public static class TenantListener extends SessionEventAdapter {
#Override
public void postAcquireClientSession(SessionEvent event) {
long tenantId = **business logic**;
event.getSession().setProperty("eclipselink.tenant-id", tenantId);
}
}

Using events is fine.
You could also inject the EntityManager and set the property, or inject the EntityManagerFactory and use joinTransaction() to join the active JTA transaction.

Related

Handle JPA cache

I have many persistence units in my persistence.xml, one by postgreSQL schema.
I instantiate my EntityManager whenever I need by creating EntityManagerFactory dynamically :
EntityManagerFactory emf = Persistence.createEntityManagerFactory(schemaToChoose);
EntityManager em = emf.createEntityManager();
and it works fine. But thus, I don't use container injection.
Now, I wonder if it doesn't cost too much resources by this way ?
I don't probably understand well JPA cache but I believe that entities are stored inside em cache and inside emf cache, there are 2 levels. So when I instantiate em and emf each time I probably loose JPA cache and thus, I recreate it each time too.
I have 2 questions : could instantiate each time emf and em impact perfomances ? Should I inject as many em that I have schemas instead to keep cache ?
Thank you
An EntityManagerFactory is a heavyweight, long-to-create object. It should be created only once, and then reused. Indeed, every time it's created, it parses the persistence.xml file, computes the mapping metadata of every entity, etc.
If you're running inside a Java EE container, then you should definitely let the container instantiate it for you, and inject the entity managers inside your EJBs.
I find it strange that a single application uses several database shemas. Why is it so?
Regarding the caches: there is a first-level cache associated with the entity manager (which is a short-lived cache, typically having the same life-time as a transaction. It's also possible to have a second-level cache, associated with the entity manager factory, but this is off by default, and, if enabled, must only be used for some entities and configured with care.
You basically have a multi-tenant pattern. EJB injection doesn't handle this very well.
An EntityManagerFactory is not something you want to create more than once, and does contain the shared cache. (the share cache default is not specified by JPA, it is on by default in EclipseLink, but configurable through JPA cache-mode and #Cacheable and #Cache).
I would recommend some sort of registry for your factories. Perhaps this would even be an application object, such as your Universe.
public class Universe {
static Map<String, Universe> universes;
EntityManagerFactory emf;
public static Universe getUniverse(String schema) {
... hopefully you get the idea ...
}
public EntityManagerFactory getEntityManagerFactory() {
return emf;
}
}
Also, I would recommend you look into EclipseLink's multi-tenant support,
http://www.eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_multitenant.htm#BABEGBIJ

JPA's EntityManager should be RequestScoped?

I am developing a JavaEE6 based web application using JBoss7.
In my application I am injecting the EntityManager in my EJBs as:
class ForumServiceEJB
{
#PersistenceContext(type=EXTENDED)
private EntityManager em;
}
class TopicServiceEJB
{
#PersistenceContext(type=EXTENDED)
private EntityManager em;
}
The problem when I update some data using ForumServiceEJB's EntityManager then the changes are made into DB but TopicServiceEJB's EntityManager is not able to see those changes and the results are always fetched from Cache.
I am using ExtendedPerssisteenceContext as My Entities contain child Entity Collections of Lazy Loading type.
How can I use/Inject EntityManager of type ExtendedPersistenceContext and make different EntityManager in one EJB can still see the changes done by other different EJB EntityManagers?
Somewhere I read EntityManagers should be RequestScoped objects.
public class MyEntityManagerProducers {
#Produces #RequestScoped
public EntityManager createDbEm() {
return Persistence.createEntityManagerFactory("forumDb").
createEntityManager();
}
public void disposeUdEm(#Disposes EntityManager em) {
em.close();
}
Is it the way to go?
I am using ExtendedPerssisteenceContext as My Entities contain child
Entity Collections of Lazy Loading type.
This is not a good reason to use EXTENDED. I would suggest you to make it default, which is TRANSACTION. And it's good to make your EntityManager request-scoped, or method-scoped, in non-enterprise environment or when using application-managed persistence, as this is not a very heavy object to create. Moreover, neither using application-scoped EntityManager is a good idea, as it is not threadsafe.
Having said that, as you are using JBoss, you should let the container handle the lifecycle of EntityManager, in case you are using JTA. Hence, just inject that with everything default
Note:
Only stateful session beans can have a container-managed, extended entity manager.
Links:
http://javanotepad.blogspot.com/2007/08/managing-jpa-entitymanager-lifecycle.html
https://blogs.oracle.com/enterprisetechtips/entry/extended_persistence_context_in_stateful
Suggestions:
Your business method should know whether to load the children or not. But that's the ideal case. A number of times we can't say that and completely depends on the user input -- we can't predict that well. Therefore, there are two solutions available,
make a separate AJAX call to load children
Use the filter called open-session-in-view. I would prefer the former.
Links:
https://community.jboss.org/wiki/OpenSessionInView
http://static.springsource.org/spring/docs/1.2.9/api/org/springframework/orm/hibernate3/support/OpenSessionInViewFilter.html
Why is Hibernate Open Session in View considered a bad practice?

CDI #TransactionAttribute for bean

I am experimenting with CDI on a test application. I have a DAO which injects a container managed JTA persistence context like this:
public class TestDAO implements Serializable {
#PersistenceContext
private EntityManager entityManager;
public void insertEntity(Test test) {
entityManager.persist(test);
}
}
Now I have a CDI controller bean like this:
#Named
#SessionScoped
public class TestController implements Serializable {
#Inject
private TestDAO testDAO;
public void finishGame() {
testDAO.insertEntity(new Test(1, 2, 3));
}
}
If I run this, I receive an error in the DAO when trying to insert the entity, because there is no active transaction available. So far so good. I can solve this by making the controller bean a stateful EJB which will wrap the finishGame() in a transaction.
But let assume I don't want an EJB. As a test I annotated the finishGame() with the #TransactionAttribute annotation and it worked(the controller bean is NOT an EJB). So my question is: how does it work? Does the CDI define #TransactionAttribute for plain beans? I know that Seam Persistence Module does this, but I am not using it. Actually I added it to the project, but I removed it after, because I received awkward exceptions.
Could anyone clear my confusion? Do really CDI define #TransactionAttribute for plain beans?
P.S. I have another sort of question. I see the tendencies is to port all EJB annotations to plain beans. So will EJBs become obsolete in the future? I mean I saw in JIRA that #TransactionAttribute will be added in the future for plain beans(the task is still not resolved). So isn't this eclipsing EJBs, sort of duplicating functionality?
Best regards,
Petar
You need do define a transaction interceptor. Basically define a #Transactional annotation and intercept all methods annotated with it. In the interceptor just begin, commit or rollback the transaction. It gets more complicated when transaction propagation comes into the picture. So check if Seam doesn't have anything ready-to-use http://seamframework.org/Seam3/PersistenceModule

HibernateDaoSupport , transaction is not rolled back

I'm playing around with Spring + Hibernate and some "manual" transaction management with PostgreSQL
I'd like to try this out and understand how this works before moving to aop based transaction management.
#Repository
public class UserDAOImpl extends HibernateDaoSupport implements UserDAO {
#Override
public void saveUser(User u) {
Transaction tx = getSession().beginTransaction();
getHibernateTemplate().saveOrUpdate(u);
tx.rollback();
}
}
Calling saveUser here, I'd assume that saving a new User will be rolled back.
However, moving to a psql command line, the user is saved in the table.
Why isn't this rolled back, What do I have to configure to do transactions this way ?
Edit; a bit more debugging seems to indicate getHibernateTemplate() uses a different session than what getSession() returns (?)
Changing the code to
Transaction tx = getSession().beginTransaction();
getSession().persist(u);
tx.rollback();
and the transaction does get rolled back. But I still don't get why the hibernateTemplate would use/create a new session..
A couple of possibilities spring to mind (no pun intended):
a) Your JDBC driver defaults to autocommit=true and is somehow ignoring the beginTransaction() and rollback() calls;
b) If you're using Spring 3, I believe that SessionFactory.getSession() returns the Hibernate Session object wrapped by a Spring proxy. The Spring proxy is set up on the Session in part to handle transaction management, and maybe it's possible that it is interfering with your manual transaction calls?
While you can certainly use AOP-scoped proxies for transaction management, why not use the #Transactional(readOnly=false|true) annotation on your service layer methods? In your Spring config file for your service layer methods, all you need to do to make this work is to add
<tx:annotation-driven />
See chapters 10 and 13 of the Spring Reference Documentation on Transaction Management and ORM Data Access, respectively:
http://static.springsource.org/spring/docs/3.0.x/reference/index.html
Finally, if you're using Spring 3, you can eliminate references to the Spring Framework in your code by injecting the Spring-proxied SessionFactory bean into your DAO code - no more need to use HibernateDaoSupport. Just inject the SessionFactory, get the current Session, and use Hibernate according to the Hibernate examples. (You can combine both HibernateDaoSupport and plain SessionFactory-based Hibernate code in the same application, if required.)
If you see the JavaDoc for HibernateDaoSupport.getSession() it says it will obtain a new session or give you the one that is used by the existing transaction. In your case there isn't a transaction listed with HibernateDaoSupport already.
So if you use getHibernateTemplate().getSession() instead of just getSession(), you should get the session that is used by HibernateTemplate and then the above should work.
Please let me know how it goes.
EDIT:
I agree its protected...my bad. So the other option then is to keep the session thread bound which is usually the best practice in a web application. If HibernateDaoSupport is going to find a thread bound session then it will not create a new one and use the same one. That should let you do rollbacks.

Why JPA injection not works on #PersistentUnit

It is continues of question ( struts 2 bean is not created )
I'm using struts2 + toplink in my very simple web application under Tomcat.
On the page I would like use iteration tag. That is why I've declared some factory (SomeFactory) that resolves collection of entities (Entity).
Per article: http://download-uk.oracle.com/docs/cd/B32110_01/web.1013/b28221/usclient005.htm#CIHCEHHG
the only thing I need is declaration:
#PersistenceContext(unitName="name_in_persistence_xml")
public class SomeFactory
{
#PersistenceUnit(unitName="name_in_persistence_xml")
EntityManagerFactory emf;
public EntityManager getEntityManager() {
assert(emf != null); //HERE every time it is null
return emf.createEntityManager();
}
public Collection<Entity> getAll()
{
return getEntityManager().createNamedQuery("Entity.findAll").getResultList();
}
}
What is wrong? May be i miss something in web.xml? How to pre-init toplink for web application to allow injection happen?
You won't get anything injected by Tomcat which is not a Java EE container (and even with a Java EE 5 container, injection only works for managed components like servlets, filters, listeners, EJB, web service endpoints...). So you will have to create the EntityManagerFactory manually (typically in a servlet or a helper class) and get the EntityManager from it:
EntityManagerFactory emf = Persistence.createEntityManagerFactory(PU_NAME);
EntityManager entityManager = emf.createEntityManager();
Note that creating an EntityManagerFactory is a costly operation and should not be done for each request. However, creating an EntityManager is not and you should get one for each thread. But in your case, I'd suggest to use the struts2-persistenceplugin to handle this.
Thanks, but it seems [...] that Java EE is not mandatory to use injection [...] the Spring brings necessary engine for it.
Indeed. But you wrote "NO spring at all" in your other question and you didn't list any piece that could provide injection out of the box. Anyway, check out the struts2-persistenceplugin, it might be enough for your needs.

Categories

Resources