Managing Multiple Database Connections - java

I've been struggling with this problem for days,
Here is the scenario:
I have several databases, one for each of my customers, all of them with the same
structure(same tables and columns), so my application needs to decide at runtime with which one it needs to connect. I'm using JPA2, EclipseLink and EJB3.
My first attempt was to implement a custom EntityManager with all the logic to performs the operations on the right database, then I configured this EntityManager as an Stateless EBJ in order to make it possible to inject it with the #EBJ annotation (as described at this link: http://www.hostettler.net/blog/2012/11/20/multi-tenancy/). I coundn't make it work because it was throwing an exception when trying to inject the EntityManager.
So I decided to try something else, I've created EntityManagerFactory and I passed the
JTA_DATASOURCE to it(after decide at runtime which one to use), so it could connect to the
right database.
Here is the code:
#Stateless
#TransactionManagement(TransactionManagementType.CONTAINER)
public class TestEntDAO {
private EntityManager em;
private EntityManagerFactory emf;
#PostConstruct
public void init() {
em = getEntityManager();
}
public EntityManager getEntityManager() {
Map props = new HashMap();
props.put(PersistenceUnitProperties.TRANSACTION_TYPE, "JTA");
props.put(PersistenceUnitProperties.JTA_DATASOURCE, dataSourceName());
emf = Persistence.createEntityManagerFactory("testePU", props);
em = emf.createEntityManager();
return em;
}
public String dataSourceName(){
if(someCondition){
return "db1";
}else{
return "db2";
}
}
}
This worked perfectly, the only problem is that the transaction is not managed by the
container, so I had to explicitly mark the transaction's boundaries(call begin() and
commit()). I could just use the #PersistenceContext annotation to make it work, but then I
wouldn't have the EntityManagerFactory to pass the datasource.
Does anyone know of a way to use the Container-Managed Transactions(CMT) and still be able
to pass the datasource?

Maybe try to define 3 Data sources and 3 Persistence units.
<persistence-unit name="PU1">
<jta-data-source>jdbc/DS1</jta-data-source>
...
</persistence-unit>
<persistence-unit name="PU2">
<jta-data-source>jdbc/DS2</jta-data-source>
...
</persistence-unit>
<persistence-unit name="PU3">
<jta-data-source>jdbc/DS3</jta-data-source>
...
</persistence-unit>
And inject Entity manager from whatever Persistence unit you want.
#PersistenceContext(unitName = "PU2")
EntityManager em;
This should work, although I didn't test it.

Related

Transaction is not accessible when using JTA with JPA-compliant transaction access enabled

I have a Transaction Manager injected with #PersistenceContext context annotation. Every time I'm trying to start the transaction with em.getTransaction().begin I got an exception with the error message
Transaction is not accessible when using JTA with JPA-compliant transaction access enabled
I know that I should inject UserTransaction or use #Transactional annotation but I want to know the reason for such behavior.
I think you should inject a container managed transaction.
Something like:
#Resource
private UserTransaction transaction;
public method() {
try {
transaction.begin();
...do some persistence...
transaction.commit();
} catch (Exception e) {
transaction.rollback();
}
}
OR
if you don't need such control you can use #Transaction annotation on your method.
Hope it helps.
I had the very same issue but problem lied elsewhere...
So the stack: I was using Wildfly server , postgresql, and JPA
At first, my persistence.xml was configured to use RESOURCE_LOCAL transactions e.g.
<persistence-unit name="unit" transaction-type="RESOURCE_LOCAL">
and I had a problem that even when the method was marked #Transactional it was not doing any rollbacks when error occured, yet saving worked fine.
So then I decided to use transaction-type JTA instead of RESOURCE_LOCAL so that the container manages the transactions.
When I changed my persistence.xml to this, it was still not working
<persistence-unit name="unit" transaction-type="JTA">
<jta-data-source>java:jboss/datasources/unit</jta-data-source>
and was throwing the very error that is in the title of this question.
My problem was, how I was creating the EntityManager, this is how it was defined
#Produces
#PersistenceContext
private static EntityManager em = Persistence.createEntityManagerFactory(UNIT_NAME).createEntityManager()
The solution? I had to remove the annotation #PersistenceContext
P.s. for JTA transactions do not forget to tell wildfly about the datasource for example via CLI like this
data-source add --jndi-name=java:jboss/datasources/unit --name=MyDb --connection-url=jdbc:postgresql://localhost:5432/postgres --driver-name=postgresql --user-name=username --password=password

Save #Entity using #EntityManager not working

Trying to persist an entity is not working but retrieving a list is.
Below is my Bean: (a)
#Stateless
public class UsersClass {
#PersistenceContext(unitName = "unit")
private EntityManager em;
public UsersClass () {}
public void create(Users entity) {
em.persist(entity);
}
}
persistence.xml as below:
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="unit" transaction-type="JTA">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jta-data-source>jdbc/ds</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
</properties>
</persistence-unit>
</persistence>
Beans have to be #Stateless. Above, setup executes fine - no exception or anything but nothing gets saved in DB also.
Tried whats mentioned here. Not sure if I did it correctly or not but was getting exception.
I also tried whats mentioned here and modified as below:(b)
#Stateless
public class UsersClass {
#PersistenceContext(unitName = "unit")
private EntityManager em;
#Resource
private SessionContext sessionContext;
public UsersClass () {}
public void create(Users entity) {
UserTransaction userTxn = sessionContext.getUserTransaction();
try {
userTxn.begin();
getEntityManager().persist(entity);
userTxn.commit();
} catch(Throwable e){
e.printStackTrace();
try {
userTxn.rollback();
} catch (IllegalStateException | SecurityException | SystemException e1) {
e1.printStackTrace();
}
}
}
}
but got the below stack trace -
Caused by: java.lang.IllegalStateException: Only session beans with bean-managed transactions can obtain UserTransaction
at com.sun.ejb.containers.EJBContainerTransactionManager.getUserTransaction(EJBContainerTransactionManager.java:566)
at com.sun.ejb.containers.BaseContainer.getUserTransaction(BaseContainer.java:995)
at com.sun.ejb.containers.AbstractSessionContextImpl.getUserTransaction(AbstractSessionContextImpl.java:120)
So as per my readings, I thought adding #TransactionManagement(TransactionManagementType.BEAN) to my class should help. Indeed, the exception is gone but nothing get persisted in the db.
Have also tried - entitymanager.getTransaction().begin() and commit() but there I get the below stack-trace
Caused by: java.lang.IllegalStateException: A JTA EntityManager cannot use getTransaction()
at org.hibernate.internal.AbstractSharedSessionContract.getTransaction(AbstractSharedSessionContract.java:360)
at org.hibernate.internal.AbstractSessionImpl.getTransaction(AbstractSessionImpl.java:23)
at com.sun.enterprise.container.common.impl.EntityManagerWrapper.getTransaction(EntityManagerWrapper.java:806)
It would really help me if someone can point out the missing piece of code.
Update 1 -
I have tried the below set of changes also with (b)
userTxn.begin();
getEntityManager().joinTransaction();
getEntityManager().persist(entity);
userTxn.commit();
Am getting below stack-trace -
org.hibernate.resource.transaction.backend.jta.internal.JtaPlatformInaccessibleException: Unable to access TransactionManager or UserTransaction to make physical transaction delegate
at org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl.makePhysicalTransactionDelegate(JtaTransactionCoordinatorImpl.java:229)
at org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl.getTransactionDriverControl(JtaTransactionCoordinatorImpl.java:203)
at org.hibernate.engine.transaction.internal.TransactionImpl.<init>(TransactionImpl.java:37)
at org.hibernate.internal.AbstractSharedSessionContract.accessTransaction(AbstractSharedSessionContract.java:372)
at org.hibernate.internal.AbstractSharedSessionContract.markForRollbackOnly(AbstractSharedSessionContract.java:342)
at org.hibernate.internal.ExceptionConverterImpl.handlePersistenceException(ExceptionConverterImpl.java:271)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:148)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:155)
at org.hibernate.internal.SessionImpl.joinTransaction(SessionImpl.java:3736)
at org.hibernate.internal.SessionImpl.joinTransaction(SessionImpl.java:3718)
at com.sun.enterprise.container.common.impl.EntityManagerWrapper.joinTransaction(EntityManagerWrapper.java:990)
Container Managed Transaction
This seems to be OK, your create method in your Users stateless bean starts the transaction automatically. You entityManager takes part in the transaction and the changes should be persisted to the Database. What was the exception in this case?
Container Managed Transaction and try to start a JTA transaction
Your create method starts a transaction automatically, you tried to start another JTA transaction in the code and you got a 'IllegalStateException'. If you want to take control of the transaction in the code, change the configuration to Bean Managed Transaction.
Bean Managed Transaction
You are controlling the Transaction in the code. The code doesn't throw any exception but the changes are not being persisted. This is because the EntityManager is not joining the transaction. If you have an EntityManager created before the start of the JTA transaction, you need to call the method joinTransaction() to make the EntityManager to join the transaction:
userTxn.begin();
EntityManager em = getEntityManager();
em.joinTransaction();
em.persist(entity);
userTxn.commit();
Can't you get the Transaction from the EntityManager
em.getTransaction().begin ();
see https://docs.oracle.com/javaee/7/api/javax/persistence/EntityManager.html#getTransaction--
You are using JTA transaction. You dont need to manage transaction in this case. It is container managed. I think you should use #Transactional annotation on create function or if you want to manage transaction by yourself then you need to use transaction type LOCAL and you need to get entitymanager instance from entitymanager factory in this case.

Java Hibernate(JPA) queries

what is the right way to make a query with JPA. Here is my way now:
EntityManagerFactory factory = Persistence.createEntityManagerFactory("hellojpa");
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
//query here
em.getTransaction().commit();
em.close();
I just started learning it and I am pretty sure that there will be more intelligent way. Some helper class, maybe?
Thanks.
In my opinion it is the right way, if you use local transactions. Keep in mind that EntityManager is not thread safe.
You will have less boilerplate code in a JavaEE environment, where the application server takes care of EntityManager injection and uses JTA.
#PersistenceContect
EntityManager em;
public void foo() {
// query
}

How can I receive the persistence unit name of an EntityManager?

In a Java EE application I am using #PersistenceContext on an EJB3.0 SessionBean to let an EntityManager be autowired.
As I am using multiple Datasources, I want to programmatically determine the autowired PersistenceUnit name of the EntityManager. Any chance?
You can retrieve more than one entity manager in this way:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("name your PU")
EntityManager em = emf.createEntityManager();
...
em.close();
emf.close();
But I do not know if a good solution. Annotation #PersistenceContext allows retrieve only one entity manager. But you may try create one class/stateless bean which will keep more than one PU, and take from him PU which you need. Maybe this little better than use EntityManagerFactory.

What to put into jta-data-source of persistence.xml?

What value should I place into <jta-data-source> of my persistence.xml?
In glassfish admin panel I created a datasource name "abcDS". In my jndi.properties (inside src/test/resources) I defined it like this:
[...]
abcDS=new://Resource?type=DataSource
abcDS.JdbcDriver=org.hsqldb.jdbcDriver
abcDS.JdbcUrl=jdbc:hsqldb:mem:testdb
abcDS.JtaManaged=true
[...]
What shall I place into persistence.xml? I've found a lot of variants in the Net, like: "jdbc/abcDS", "java:/abcDS", "abcDS". Which one is right? And is there some rule for this? I understand that it's related to JNDI, but...
I'm trying to create EMF in my unit test:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("abc");
This is what I'm getting in log:
[...]
SEVERE: Could not find datasource: abcDS javax.naming.NameNotFoundException:
Name "abcDS" not found.
at org.apache.openejb.core.ivm.naming.IvmContext.federate(IvmContext.java:193)
at org.apache.openejb.core.ivm.naming.IvmContext.lookup(IvmContext.java:150)
at org.apache.openejb.core.ivm.naming.ContextWrapper.lookup(ContextWrapper.java:115)
at javax.naming.InitialContext.lookup(InitialContext.java:392)
[...]
The problem is that Persistence.createEntityManagerFactory("abc") is the "do it yourself" API and doesn't take advantage of the Embedded EJB Container. You can get a container managed EntityManager in your test case very easily.
Just as with the related jndi/datasource question I recommend you check out the examples in the examples.zip. They're all designed to take the struggle out of getting started.
Here's a snippet from the testcase-injection example which shows how you can get an EntityManager and other things from the container for use in a test.
First, add an empty ejb-jar.xml or application-client.xml to your test to turn on scanning for your test code:
src/test/resources/META-INF/application-client.xml
Then, annotate your test case with #org.apache.openejb.api.LocalClient and use the standard JavaEE annotations for the actual injection.
#LocalClient
public class MoviesTest extends TestCase {
#EJB
private Movies movies;
#Resource
private UserTransaction userTransaction;
#PersistenceContext
private EntityManager entityManager;
public void setUp() throws Exception {
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
p.put("movieDatabase", "new://Resource?type=DataSource");
p.put("movieDatabase.JdbcDriver", "org.hsqldb.jdbcDriver");
p.put("movieDatabase.JdbcUrl", "jdbc:hsqldb:mem:moviedb");
InitialContext initialContext = new InitialContext(p);
// Here's the fun part
initialContext.bind("inject", this);
}
As movieDatabase is the only DataSource that we've setup, OpenEJB will automatically assign that DataSource to your persistence unit without the need to modify your persistence.xml. You can even leave the <jta-data-source> or <non-jta-data-source> empty and OpenEJB will still know what to do.
But for the sake of completeness, here's how this particular application has defined the persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="movie-unit">
<jta-data-source>movieDatabase</jta-data-source>
<non-jta-data-source>movieDatabaseUnmanaged</non-jta-data-source>
<class>org.superbiz.testinjection.Movie</class>
<properties>
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/>
</properties>
</persistence-unit>
</persistence>
Then the fun part, using it all together in tests
public void test() throws Exception {
userTransaction.begin();
try {
entityManager.persist(new Movie("Quentin Tarantino", "Reservoir Dogs", 1992));
entityManager.persist(new Movie("Joel Coen", "Fargo", 1996));
entityManager.persist(new Movie("Joel Coen", "The Big Lebowski", 1998));
List<Movie> list = movies.getMovies();
assertEquals("List.size()", 3, list.size());
for (Movie movie : list) {
movies.deleteMovie(movie);
}
assertEquals("Movies.getMovies()", 0, movies.getMovies().size());
} finally {
userTransaction.commit();
}
}

Categories

Resources