How to manage java ee transactions? - java

I'm currently using Java EE to inject my EntityManager into a web app as follows:
#PersistenceContext
EntityManager em;
#Resource
UserTransaction utx;
I have this in a request scoped JSF bean. It works, but it's a pain because to avoid the NoTransactionException I have to wrap every DAO method like so:
public void saveSomething(Obj toSave) {
EntityManager em = getEntityManager();
UserTransaction utx = getTransaction();
try {
utx.begin();
em.persist(toSave);
utx.commit();
} catch(Exception e) {
logger.error("Error saving",e);
try {
utx.rollback();
} catch(Exception ne) {
logger.error("Error saving",ne);
}
return null;
}
}
}
Is there any way to have the container manage the transactions for me in a project like this consisting only of a WAR file?

If you are managing your own transactions, the best way is to provide an abstract DAO to do the boilerplate code for you:
#PersistenceContext
EntityManager em;
#Resource
UserTransaction utx;
abstract class AbstractDao<E,ID> implements IDAO<E,ID> {
public ID save(E e) {
try {
utx.begin();
em.persist(e);
utx.commit();
} catch(Exception e) {
logger.error("Error saving",e);
try {
utx.rollback();
} catch(Exception ne) {
logger.error("Error saving",ne);
}
return null;
}
}
}
The alternative is to use container-managed transactions. Please consult the J2EE guide: http://java.sun.com/javaee/5/docs/tutorial/doc/bncij.html

Related

How to create Spring JPA Entity Manager manually that contains all the default JPA properties

I need to create an Entity Manager manually to support having multiple datasources. I've realised that by creating it manually (instead of autowiring it as when you have only one single datasource), I actually do need to set all the JPA properties manually too, including what are usually set with default values by Spring. This means all the JPA parameters I specified in application.yaml and what Spring normally set default values for have now to be loaded and set manually.
Is there anyway to create an Entity Manager manually but have it automatically use all the JPA properties in application.yaml?
Here's my code
public LocalContainerEntityManagerFactoryBean entityManager(EntityManagerFactoryBuilder builder, DataSource secondDataSource) {
Map<String, Object> props = new HashMap<>();
// Is there a way to not do this one by one for all of the default JPA properties and what I specified in application.yaml?
props.put("hibernate.physical_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
// props.put("prop1", "propvalue1");
// ...
return builder.dataSource(secondDataSource).packages(MyEntity.class).properties(props).build();
}
Let's say, you have a persistence-unit with the name myData
private EntityManager getEntityManager() {
EntityManagerFactory emf = null;
try {
emf = Persistence.createEntityManagerFactory("myData");
} catch (Exception e) {
log.warn("An Error has occurred while creating persistence layer", e);
}
if (emf != null) {
return emf.createEntityManager();
} else {
log.warn("An error has occurred while retrieving Entity Manager from Persistence factory");
return null;
}
}
And use like below
final EntityManager em = getEntityManager();
assert em != null;
EntityTransaction entityTransaction = em.getTransaction();
try {
entityTransaction.begin();
userList.stream().forEach(user -> {
em.merge(RestUtil.convertToDBUser(user));
});
entityTransaction.commit();
log.info("Completed data persistence ");
} catch (RuntimeException e) {
if (entityTransaction.isActive()) {
entityTransaction.rollback();
}
log.warn("An error has occurred while committing JDBC transaction. ", e);
throw e;
} finally {
em.close();
}

Java Bulk Insert with Shared Entity Manager

I am trying to write a method that bulk adds entities to the database. Here's my method:
#Transactional
protected void bulkInsert(List<?> entities) {
int batchSize = 25;
AtomicInteger i = new AtomicInteger(0);
try {
em.getTransaction().begin();
entities.parallelStream().forEach(e -> {
em.persist(e);
if ( i.get() > 0 && i.get() % batchSize == 0 ) {
em.flush();
em.clear();
}
i.incrementAndGet();
});
em.getTransaction().commit();
} catch (RuntimeException e) {
LOG.error("Error", e);
} finally {
if (em != null) {
em.close();
}
}
}
But when I run this I get the following error:
java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:248) ~[spring-orm-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at com.sun.proxy.$Proxy145.getTransaction(Unknown Source) ~[na:na]
I tried removing the #Transactional annotation and still the same error. I tried using Session by doing Session session = em.unwrap(Session.class); but that still resulted in an error (although different error)
How do I bulk insert objects?
in pure hibernate exists managed- and non-managed environments
Non-managed environment
If an JPA persistence layer runs in a non-managed environment, database >connections are usually handled by Hibernate's pooling mechanism behind the >scenes. The common entity manager and transaction handling idiom looks like > this:
// Non-managed environment idiom
EntityManager em = emf.createEntityManager(); EntityTransaction tx =
null; try {
tx = em.getTransaction();
tx.begin();
// do some work
...
tx.commit(); } catch (RuntimeException e) {
if ( tx != null && tx.isActive() ) tx.rollback();
throw e; // or display error message } finally {
em.close(); }
Using JTA
If you use bean-managed transactions (BMT), the code will
look like this:
// BMT idiom
#Resource public UserTransaction utx;
#Resource public
EntityManagerFactory factory;
public void doBusiness() {
EntityManager em = factory.createEntityManager();
try {
// do some work
...
utx.commit(); } catch (RuntimeException e) {
if (utx != null) utx.rollback();
throw e; // or display error message
} finally {
em.close();
}
With Container Managed Transactions (CMT) in an EJB3 container, transaction >demarcation is done in session bean annotations
or deployment descriptors, not programatically. The EntityManager will
automatically be flushed on transaction completion (and if you have
injected or lookup the EntityManager, it will be also closed
automatically). If an exception occurs during the EntityManager use,
transaction rollback occurs automatically if you don't catch the
exception. Since EntityManager exceptions are RuntimeExceptions they
will rollback the transaction as per the EJB specification (system
exception vs. application exception).
you should follow on of two princeple.
annotation #Transactional it's spring annotaniot not hibernate. if you didn;t configued it (or you did configured spring) doen't work.
PC
An EntityManager is an inexpensive, non-threadsafe object that should
be used once, for a single business process
call em.flush() ,em.clear() might be dangerous .

BMT in client-side classes?

I have a 3-layer application with a client-container, EJB-container (business-layer and data-access-layer). I want to make transactions in my client by using BMT. My EJBs on the EJB-container are all working with CMT.
But I am not sure if that actually works, since the client is not working in an EJB-container. The client injects the EJBs of the EJB-container with lookups on a Context object, which seem to work fine. I declared my client-side class as:
#Singleton
#TransactionManagement(TransactionManagementType.BEAN)
Is this correct? I guess the client side class which is used by my GUI has to be a singleton.
I have these objects:
private Context jndiCntx;
private UserTransaction tx;
#PostConstruct
public void initialize() throws Exception {
jndiCntx = new InitialContext();
tx = (UserTransaction) jndiCntx.lookup("java:comp/UserTransaction");
tx.begin();
}
And this PostConstruct initializes the transaction.
Here is where I get a NullPointerException (in commitOrder()):
#Override
public void commitOrder() throws ApplicationException {
try {
tx.commit();
} catch (Exception e) {
e.printStackTrace();
throw new ApplicationException("", "commitOrder() failed!");
}
}
#Override
public void cancelOrder() throws ApplicationException {
try {
tx.rollback();
} catch (Exception e) {
throw new ApplicationException("", "cancelorder() failed!");
}
}
So it seems like that I don't get a UserTransaction object. I am slightly confused by the different ways of using contexts and transactions. What am I doing wrong? And by the way: What is the difference between using a Transaction with a TransactionManager and using a UserTransaction?

ejb3 isolated (autonomous) transaction inside session bean

I'm using XA (2-phase) transaction. I want to log to one log-table through Log class and Entity Manager. My method inside EJB Session bean looks like:
private void logError(Throwable throwable) {
LogEntity logEntity = new LogEntity();
// Set everything
entityManager.persist(logEntity);
}
I want to it in isolated (autonomous) transaction independent of any "outer" transaction. I have already tried to add #TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) and #TransactionAttribute(value = TransactionAttributeType.NOT_SUPPORTED) before method name and does not work.
Before I call EJB3 method I create user transaction like:
try {
UserTransaction transaction = (UserTransaction)context.lookup("javax.transaction.UserTransaction");
transaction.begin();
// Call EJB3 method
transaction.commit();
} catch (Throwable t) {
t.printStackTrace();
try {
transaction.rollback();
} catch (SystemException e) {
e.printStackTrace();
}
}
I want to Log no matter if commit is done or not. How to?
Regards
Transaction attributes are only relevant when called through a proxy. They do not apply to direct calls, which includes private methods. Try something like the following (which uses EJB 3.1 no-interface view, though you could create a separate local interface for logging if you only have EJB 3.0):
#Stateless
#Local(BusinessInterface.class)
#LocalBean
public class MyBean {
#EJB MyBean logger;
#TransactionAttribute(REQUIRED)
public void businessMethod() {
try {
...
} catch (Throwable t) {
logger.logError(t);
...
}
}
#TransactionAttribute(NOT_SUPPORTED)
public void logError(Throwable t) {
...
}
}
The important piece is that the call to logError happens through an injected EJB proxy, which allows the container to have control to suspend the XA transaction for the duration of the logError method.

update using JPA

I m using glassfish v2 and persistence in a web application.
I m calling persistence codes using a normal java class file inside a web Application
I can select easily using this code: -
#PersistenceUnit
public EntityManagerFactory emf;
EntityManager em;
public List fname (String id) {
String fname = null;
List persons = null;
//private PersistenceManagerFactory persistenceManagerFactory;
try {
emf = Persistence.createEntityManagerFactory("WebApplicationSecurityPU");
em = emf.createEntityManager();
persons = em.createQuery("select r from Roleuser r").getResultList();
int i=0;
for (i=0;i<persons.size(); i++)
System.out.println("Testing n "+ i +" " + persons.get(i));
} catch(Exception e) {
System.out.println("" + e);
}
finally {
if(em != null) {
em.close();
}
}
return persons;
}
I want to update using JTA as the persistence.xml file has
transaction-type="JTA"
When i try using update using this code i get a nullPointerException without any traces in the log
#PersistenceUnit
public EntityManagerFactory emf;
EntityManager em;
Context context;
#Resource
private UserTransaction utx;
public List fname (String id) {
String fname = null;
List persons = null;
try {
emf = Persistence.createEntityManagerFactory("WebApplicationSecurityPU");
utx.begin();
em = emf.createEntityManager();
int m = em.createQuery("update Roleuser r set r.firstName = 'Jignesh I' where r.userID=9").executeUpdate();
utx.commit();
} catch(Exception e) {
System.out.println("" + e);
}
finally {
if(em != null) {
em.close();
}
}
return persons;
}
Any help
Thanks
Pradyut
Perhaps your bean isn't managed - i.e. it's not part of any context (spring, EJB). How are you creating your object?
You really should not call createEntityManager() - inject one using #PersistenceContext
You must be absolutely sure you need JTA before using it.
You seem to be using PersistenceUnit, but then re-assign the etm - I'd suggest drop both and see p2 above.
If you are not using any dependecy injection at all, then drop all the annotations, retain the current code, and type:
em.getTransaction().begin();
...
em.getTransaction().commit();
(and define RESOURCE_LOCAL in your persistence.xml. You really don't need JTA)
well the code should be without any nightmares...(atleast for me in glassfish)
with the persistence.xml having
<persistence-unit name="WebApplicationSecurityPU" transaction-type="RESOURCE_LOCAL">
the code
#PersistenceUnit
public EntityManagerFactory emf;
public EntityManager em;
public EntityManager getEm() {
emf = Persistence.createEntityManagerFactory("WebApplicationSecurityPU");
em = emf.createEntityManager();
return em;
}
public List fname (String id) {
String fname = null;
List persons = null;
try {
System.out.println("test");
em = this.getEm();
em.getTransaction().begin();
int m = em.createQuery("update Roleuser r set r.firstName = 'Jignesh H' where r.userID=9").executeUpdate();
em.getTransaction().commit();
} catch(Exception e) {
System.out.println("" + e);
}
finally {
if(em != null) {
em.close();
}
}
return persons;
}
Any improvements are welcome...(actually needed...)
(How to go about using #PersistenceContext)
Thanks
Pradyut
Your "normal" class is very likely not a managed component i.e. a class whose life cycle is managed by the container (like Servlets, Servlet Filters, JSP tag handlers, JSF Managed Beans, ...) and can't benefit from resource injection1.
So neither the UserTransaction nor the EntityManagerFactory are injected here, hence the NullPointerException.
Honestly, you should try to use a container managed EntityManager, this would make your code less messy. If you cannot get it injected, get it via a JNDI lookup. See the resource below.
1 Have a look at Web Tier to Go With Java EE 5: A Look at Resource Injection for a nice overview of what can be injected, and where.
Resources
How to use EntityManager API in web module
References
JPA 1.0 specification
Section 5.2 "Obtaining an Entity Manager"
Section 5.6 "Container-managed Persistence Contexts"

Categories

Resources