How can I commit a Play! JPA transaction manually? - java

Usually, Play! commits the transaction after a request completes successfully.
What is the correct way to commit a transaction manually in Play?
void addPerson() {
Person p = new Person("John", "Doe");
p.save();
// TODO - commit the transaction
// Now p should have an ID
assert p.id != null;
usePersonIdForSomethingNasty(p.id);
}

You can get the Hibernate EntityManager by calling JPA.em(). Then, from there, you have access to the transaction (JPA.em().getTransaction()).
If you intend to manage the transaction yourself, you will want to disable Play!'s transaction handling (there is a #NoTransaction annotation you can use on the method or controller to do that). Otherwise, Play! will try to commit the transaction at the end of the request anyway, and if you have already done that yourself, that will cause an exception.

You don't need to do anything. After the request finishes without any exception, the transaction will be commited for you.
Just ensure to call "save" on all entities you want to persist at the end of transaction.

Related

How to partially rollback data in spring boot, errors to be persited

I have sequence of table data creation in transaction, using springboot, java, jpa and hibernate sequence shown below. Database used is Oracle
Place order - insert into order details_table
Process Payment - insert into payment_info table
Error Message(info, warning, error) - insert into error_message table
I am inserting error messages in error_message table if any error occurs(info, warning, error).
My info and warning message get persisted into error_message if no error occurs during the processing of the request.
But if there is an error, I am throwing an error from code to rollback the transaction, its working but my error_message also get rollback.
I want to rollback details_table and payment_info but not the error_message, I want them to be persisted.
How I can achieve the same?
Use two different transactions.
The default transaction propagation is REQUIRED, when calling your #Transactional annotated method without a transactional context, a new transaction will be started but if there's already a transaction in place, the transaction will be joined, ending up with a single transaction. In case of rollback, all operations will be rollbacked as they belong to the same transaction.
Change the transaction propagation to REQUIRES_NEW in order to always force starting a new transaction (instead of joining it if there's one). This will allow you to commit the error messages transaction independently from the payment info.
#Transactional
public void placeOrder(Order oder) {
// process order
paymentGateway.processPayment();
// save order <- an exception here will rollback the current
// transaction but not the one in PaymentGateway which is independent
}
// In PaymentGateway
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void processPayment() {
// process payment
// insert error messages
}
Further reading: Understanding PROPAGATION_REQUIRED. Make sure you also understand how exceptions affect transactions.

Proper Hibernate Layer Design

Im developing a java web application using hibernate and I came across a basic problem:
Given user A triggers some Hibernate transaction. Start transaction, load, commit transaction.
At the same time, user B triggers a similar transaction. Then, the will get an exception: nested transactions not supported.
It seems that no more than one transaction can be active at one time.
I researched for a solution and found a lot of overview explainations like transaction-per-session pattern, but nothing tangible.
So my question is: What is a proper and simple way to handle hibernate transactions for multiple concurrent users?
the transaction management is quite standard, just remember any exceptions thrown by Hibernate are fatal , you have to roll back the transaction and close the current session immediately.
You have to close the each and every transaction.
Session session = null;
Transaction t = null;
try{
session = HibernateUtil.getSessionFactory().openSession();
t = session.beginTransaction();
//do what ever you want(session);
t.commit();
}catch(RuntimeException e){
System.out.println(e.getMessage());
}

When i have to use Hibernate's rollback

When i have to use Hibernate's rollback?
Hibernate will do rollback itself in exception case, so my rollback line just external:
Session session = HibernateUtil.getSessionFactory().openSession();
try {
session.beginTransaction();
test cl = (test) session.createCriteria(test.class)
.add(...)
.list()
.get(0); // Here's throws an exception, Hibernate was rolled back automatically.
session.getTransaction().commit();
}
catch (Exception ex)
{
session.getTransaction().rollback(); // The transaction already was rolled back. Unnecessary?
}
That's the only approach to use Hibernate's rollback which i can invent but rollback has not sense there.
So in which cases i really have to use rollback?
In case there is no exception, but you still want to roleback because you found something wrong in your program logic, that's when you can use it. Like if you are updating multiple entities and your modified one and found some anomaly in the data according to your business logic, you can call rollback.

JtaTransaction localStatus rolled back after creation

I'm creating a hibernate Session and try to start a new [Jta]Transaction. Though, the transaction cannot be started because the JtaTransaction that is used in the background seems to be rolled back.
Here is what I'm doing.
Session session = sessionFactory.openSession();
CustomSessionWrapper dpSession = new CustomSessionWrapper(session, this);
if (!session.isClosed() && !session.getTransaction().isActive()) {
session.beginTransaction();
}
Nevertheless the transaction is still not active after the beginTransaction is called. When I debug the beginTransaction method I come to the doBegin method of the JtaTransaction (I do not override this method, I'm just posting the original code of this method).
#Override
protected void doBegin() {
LOG.debug( "begin" );
userTransaction = locateUserTransaction();
try {
if ( userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION ) {
userTransaction.begin();
isInitiator = true;
LOG.debug( "Began a new JTA transaction" );
}
}
catch ( Exception e ) {
throw new TransactionException( "JTA transaction begin failed", e );
}
}
The userTransaction.getStatus() returns Status.STATUS_ROLLEDBACK and no transaction is started. Does anyone know how I can fix that?
UPDATE 1 (you can skip that since that was a mistake, see UPDATE 2)
I found out that there are two threads, one using the main session and another using smaller sessions for logging. The main session (and transaction) is open for a longer period of time, so basically until the operation is finished. It seems that locateUserTransaction always returns the same userTransaction. This means that the main session opens this userTransaction and one of the side transactions commit/rollback that transaction. Does anyone know what to do so that different transactions are retrieved?
UPDATE 2
I found out that I don't have two threads, it is only one threads that opens two sessions in parallel. Each session should then open their own transaction, though both get the same UserTransaction. How can I tell hibernate that each session should get its own [User]Transaction?
Hibernate abstracts both local as JTA transactions behind its own abstraction layer, so I don't see why you'd have to write such low level transaction handling code.
In Java EE you have the app server to manage transactions, in stand alone apps Bitronix + Spring do a job too.
Although you can manage to write your own transaction management logic, I always advice people to reuse what they have already available. Xa/JTA and Hibernate require extensive knowledge to work seamlessly.
Update 1
Two different threads shouldn't use the same user transaction, you should use different transactions for each thread.

jpa merge unmanaged entity

I would like to make an unmanaged entity managed in another Persistence Context. I read that this can be made with merge:
em.merge(user);
But if I do this it is not added to the context:
boolean isManaged = em.contains(user);
is always false.
Even if I make the following:
User dbuser = em.find(User.class, user.getId());
em.merge(user);
boolean isManaged = em.contains(user);
The dbuser and user are exactly the same.
What am I doing wrong?
I am using JPA, MySql DB, JBoss EAP 6.1
Call entityManager.flush()to commit your merge action into the database.
Ususally the commit is delayed. For example if your method has an #TransactionAttribute annotation. The transaction will be commited after the method has finished. But if you call em.contains(user) without a commit you just get the old state.

Categories