Hibernate freezes at transaction begin function - java

I'm using Hibernate on my server, (tomcat8, hibernate, postgresql).
Every end of the day my code runs (using Quartz) some code, which calls inside the stored procedure (hibernate):
public void execute(JobExecutionContext context)
throws JobExecutionException {
log.info("=========Start daily update==========");
long startTime = System.currentTimeMillis();
boolean transactionCompleted = false;
int retryCount = HibernateUtil.RETRY_COUNT;
HibernateUtil.closeCurrentEntityManager();
EntityManager em = HibernateUtil.currentEntityManager();
while (!transactionCompleted)
{
try {
em.getTransaction().begin();
dailyUpdateDao.dailyUpdate();
em.getTransaction().commit();
transactionCompleted = true;
} catch (PersistenceException ex) {
if (!HibernateUtil.isDeadlockException(ex) || retryCount == 0) {
log.error("non deadlock error", ex);
throw ex;
}
log.error("deadlock detected. Retrying {}", HibernateUtil.RETRY_COUNT - retryCount);
retryCount--;
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
try {
Thread.sleep(HibernateUtil.sleepIntervalWhenDeadlockDetected(retryCount));
} catch(Exception sleepex) {
log.error("non deadlock sleep ex", ex);
throw ex;
}
}
}
log.info("===========Daily Update Job Completed ============== It took {} ms", System.currentTimeMillis() - startTime);
}
The dailyUpdate function in the code above is doing the following:
public void dailyUpdate() {
String sql = "select count(*) FROM daily_update()";
EntityManager em = HibernateUtil.currentEntityManager();
em.createNativeQuery(sql).getSingleResult();
}
(calling stored procedure using native sql through hibernate).
When I run the server, it does first 2 or 3 calls normally. Next calls never finishes. I reproduced the issue locally, instead of each day I put the schedule to start the task each 1 minute. It showed me logs like this:
Daily Update Job Completed ============== It took 7338 ms
Daily Update Job Completed ============== It took 6473 ms
...
Daily Update Job Completed ============== It took 183381 ms
so the delay increased and I decided to see whats going on inside.
In the code above, when it tries to execute the
em.getTransaction().begin();
it never finishes and the stack trace is shown below as an image:
What is the reason and how to resolve the problem?
EDIT 1: currentEntityManager and closeCurrentEntityManager codes:
public class HibernateUtil {
...
private static EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(PERSISTENT_UNIT);
public static EntityManager currentEntityManager(EntityManagerFactory emf)
throws HibernateException {
EntityManager em = (EntityManager) entityManager.get();
if (em == null||!em.isOpen()) {
em = emf.createEntityManager();
entityManager.set(em);
}
return em;
}
public static void closeCurrentEntityManager() {
EntityManager s = (EntityManager) entityManager.get();
try {
if (s != null) {
if (s.getTransaction().isActive()) {
if (s.getTransaction().getRollbackOnly()) {
s.getTransaction().rollback();
} else {
s.getTransaction().commit();
}
}
s.close();
}
} finally {
entityManager.remove();
}
}

Ok, perfect, after posting details about what is HibernateUtil, how do closeCurrentEntityManager() and currentEntityManager() work, and what is entityManager fiels inside this class, everything becomes clear.
Look at your code. You firstly closes "current" entity manager:
HibernateUtil.closeCurrentEntityManager();
But you should take into consideration that fact that quartz scheduller starts its tasks in different threads. So
public static void closeCurrentEntityManager() {
EntityManager s = (EntityManager) entityManager.get();
will return null (if it is new thread).
Next step you invoke
EntityManager em = HibernateUtil.currentEntityManager();
which also will create new EntityManager as it is new thread:
em = emf.createEntityManager();
Now look at you screenshot: your beginTransaction() methods awaits for new connection. What's happening here is that you create new entitmanager which opens new connection, but do not close it. So basically your are out of free connections in you pool.
Simply try move HibernateUtil.closeCurrentEntityManager(); into final { ... }block and test again.

Related

Slow connection between java jpa application and a remote Mysql database?

I have a strange situation with my java play framework (2.3) application. All works fine If I have deployed my applications close (geographically) my database mysql. The request, with connections to database works fine and fast. But, last day, I moved the database remotely, in another country. The application go on fine, but, each time I create a JPA Entity Manager (and I think the application create a new connections to remote database) the connections is very very slowly. And the result is that all request are extremely slow.
According your experience there is a way to optimize this situation via application?
Below my controller java code:
#Transactional
public Result testperson() {
Person person= JPAEntityManager.find(Person .class, "XXXXXX");
person.setAddress("XXXXXXX");
JPA.em().persist(person);
return ok("");
}
The #Transactional annotation intercept a play framework jpa implementation for the connections:
public static <T> F.Promise<T> withTransactionAsync(String name, boolean readOnly, play.libs.F.Function0<F.Promise<T>> block) throws Throwable {
EntityManager em = null;
EntityTransaction tx = null;
try {
em = JPA.em(name);
JPA.bindForCurrentThread(em);
if(!readOnly) {
tx = em.getTransaction();
tx.begin();
}
F.Promise<T> result = block.apply();
final EntityManager fem = em;
final EntityTransaction ftx = tx;
F.Promise<T> committedResult = result.map(new F.Function<T, T>() {
#Override
public T apply(T t) throws Throwable {
try {
if(ftx != null) {
if(ftx.getRollbackOnly()) {
ftx.rollback();
} else {
ftx.commit();
}
}
} finally {
fem.close();
}
return t;
}
});
committedResult.onFailure(new F.Callback<Throwable>() {
#Override
public void invoke(Throwable t) {
if (ftx != null) {
try { if (ftx.isActive()) ftx.rollback(); } catch(Throwable e) {}
}
fem.close();
}
});
return committedResult;
} catch(Throwable t) {
if(tx != null) {
try { tx.rollback(); } catch(Throwable e) {}
}
if(em != null) {
em.close();
}
throw t;
} finally {
JPA.bindForCurrentThread(null);
}
}
The JPA.em() create a new EntityManager...
All connections details are default for the play framework: https://www.playframework.com/documentation/2.3.x/SettingsJDBC
Maybe Is there a problem with MySQl database during remote connections?
Can there be some settings to set on the database side to improve a remote connection?
Thanks in advance!
How long does SELECT 1 take? That gives you a good clue of the new overhead for every SQL statement because of reaching into "another country".
If it turns out that there are "too many" queries, consider wrapping a set of them in a Stored Procedure. Then have your app CALL the SP -- this will be one roundtrip, not many.

Transaction not successfully started (while tx.commit() is surrounded by a if condition)

First time that I ran into this error I've surrounded my tx.commit() with a if condition but am not sure why I am still receiving this error.
Struts Problem Report
Struts has detected an unhandled exception:
Messages:
Transaction not successfully started
File: org/hibernate/engine/transaction/spi/AbstractTransactionImpl.java
Line number: 200
Stacktraces
org.hibernate.TransactionException: Transaction not successfully started
org.hibernate.engine.transaction.spi.AbstractTransactionImpl.rollback(AbstractTransactionImpl.java:200)
After a product has been selected by user, in my main function I will call two functions as following.
First function to retrieve the object of selected product.
Second function to check if selected user has the product therefore it returns true if client has the product otherwise returns false;
Function 1
....
Product pro = new Product();
final Session session = HibernateUtil.getSession();
try {
final Transaction tx = session.beginTransaction();
try {
pro = (Product) session.get(Product.class, id);
if (!tx.wasCommitted()) {
tx.commit();
}
} catch (Exception e) {
tx.rollback();
e.printStackTrace();
}
} finally {
HibernateUtil.closeSession();
}
.....
Function 2
.....
final Session session = HibernateUtil.getSession();
try {
final Transaction tx = session.beginTransaction();
try {
User user = (User) session.get(User.class, id);
if (!tx.wasCommitted()) {
tx.commit();
}
if(client.hasProduct(proId)){
return client.getProduct(proId);
}
return false;
} catch (Exception e) {
tx.rollback(); <<<Error is on this line
e.printStackTrace();
}
} finally {
HibernateUtil.closeSession();
}
....
Take a look at Transaction.isActive() method. You can wrap call to rollback() method with condition, checking whether transaction is still active. And the second, I'd prefer the following code:
final Session session = HibernateUtil.getSession();
try {
final Transaction tx = session.beginTransaction();
// do things
tx.commit();
} finally {
if (tx.isActive()) {
try {
tx.rollback();
} catch (Exception e) {
logger.log("Error rolling back transaction", e);
}
}
try {
session.close();
} catch (Exception e) {
logger.log("Error closing session", e);
}
}
Of course, code in the finally section better to wrap into public static method and just call it in every finally.
BTW, why are you doing something outside tranaction? I usually commit after all things get done, to achieve a better consistency and avoid LazyInitializationException.
One possibility is that the exception you are catching in the second functions is from the code after the commit(), so you end up trying to rollback a transaction that is already committed, which is not allowed.
You could try reorganizing your code to make sure that rollback is never called after commit. Maybe even something simple like reducing the scope of the inner try-catch:
final Session session = HibernateUtil.getSession();
try {
final Transaction tx = session.beginTransaction();
try {
User user = (User) session.get(User.class, id);
if (!tx.wasCommitted()) {
tx.commit();
}
} catch (Exception e) {
tx.rollback();
e.printStackTrace();
}
if(client.hasProduct(proId)){
return client.getProduct(proId);
}
return false;
} finally {
HibernateUtil.closeSession();
}
The error indicates the transaction wasn't started at the time tried to roll back - and the problem may be that you are trying to wrap a get, which does not alter the db state and does not leave behind garbage that needs to be committed or rolled back. Nothing changes when you perform select *.
In addition to this, you may want to extract this transaction handling into a common method that is independent of the work being done, so you don't have to write this over and over again, that leaves your code open for bugs. Basically, it seems like you are getting DB objects but then intermingling some business logic withing the same method. Perhaps consider doing something like below:
DB Handling Function
public static <T> T getDBObject( Class<T> clazz, Serializable id )
throws SQLException
{
Session session = null;
try
{
session = HibernateUtil.getSession();
return (T)session.get( clazz, id );
}
finally
{
if ( session != null )
{
session.close();
}
}
}
Now that you can pull object of the DB (note that they will be detached, but still valid), you can then perform work on the objects. I many not have captured exactly what you need to check, but it seems like it is something like:
Example Comparison Function
public boolean doesUserHaveProduct(Serializable userId, Serializable productId)
{
try
{
User user = getDBObject(User.class, userId);
Product product = getDBObject( Product.class, productId );
return user.hasProduct( product );
}
catch (SQLException e)
{
e.printStackTrace();
return false;
}
}

Lock wait timeout exceeded with Hibernate and MySQL (using play framework)

In my web application I'm using Stateless sessions with Hibernate to have better performances on my inserts and updates.
It was working fine with H2 database (the one used in play framework in dev mode).
But when I test it with MySQL I get the following exception :
ERROR ~ Lock wait timeout exceeded; try restarting transaction
ERROR ~ HHH000315: Exception executing batch [Lock wait timeout exceeded; try restarting transaction]
Here is the code :
public static void update() {
Session session = (Session) JPA.em().getDelegate();
StatelessSession stateless = this.session.getSessionFactory().openStatelessSession();
try {
stateless.beginTransaction();
// Fetch all products
{
List<ProductType> list = ProductType.retrieveAllWithHistory();
for (ProductType pt : list) {
updatePrice(pt, stateless);
}
}
// Fetch all raw materials
{
List<RawMaterialType> list = RawMaterialType.retrieveAllWithHistory();
for (RawMaterialType rm : list) {
updatePrice(rm, stateless);
}
}
} catch (Exception ex) {
play.Logger.error(ex.getMessage());
ExceptionLog.log(ex, Thread.currentThread());
} finally {
stateless.getTransaction().commit();
stateless.close();
}
}
private static void updatePrice(ProductType pt, StatelessSession stateless) {
pt.priceDelta = computeDelta();
pt.unitPrice = computePrice();
stateless.update(pt);
PriceHistory ph = new PriceHistory(pt, price);
stateless.insert(ph);
}
private static void updatePrice(RawMaterialType rm, StatelessSession stateless) {
rm.priceDelta = computeDelta();
rm.unitPrice = computePrice();
stateless.update(rm);
PriceHistory ph = new GoodPriceHistory(rm, price);
stateless.insert(ph);
}
In this example I have 3 simple Entities (ProductType, RawMaterialType and PriceHistory).
computeDelta and computePrice are just algorithm functions with no DB stuff.
retrieveAllWithHistory functions are functions that fetch some data from the database using Play framework model functions.
So, this code retrieves some data, edit some, create new one and finally save everything.
Why have I a lock exception with MySQL and no exception with H2 ?
I'm not sure why you have a commit in a finally block. Give this structure a try:
try {
factory.getCurrentSession().beginTransaction();
factory.getCurrentSession().getTransaction().commit();
} catch (RuntimeException e) {
factory.getCurrentSession().getTransaction().rollback();
throw e; // or display error message
}
Also, it might be helpful for you to check this documentation.

JDBC Replication driver always returning same data without active cache

I am using the MySQL JDBC Replication Driver com.mysql.jdbc.ReplicationDriver to shift load between Master and Slave.
I am using that connection URL
jdbc.de.url=jdbc:mysql:replication://master:3306,slave1:3306,slave2:3306/myDatabase?zeroDateTimeBehavior=convertToNull&characterEncoding=UTF-8&roundRobinLoadBalance=true
As soon as I am starting my application I am getting only that data from where it has been started, like I am working on a locked snapshot of the database. If I am doing any CRUD operation the data is not callable or updates are not shown. Replication of mysql is working just fine and I can query the correct data from the database.
There is no level2 cache active and I am using hibernate with pooled connections
If I am using the normal JDBC Driver com.mysql.jdbc.Driver everything is working just fine. So why am I getting always the same resultsets, no matter what I do change in the database...
Update 1
It seems like it is related to my aspect
#Aspect
public class ReadOnlyConnectionInterceptor implements Ordered {
private class ReadOnly implements ReturningWork<Object> {
ProceedingJoinPoint pjp;
public ReadOnly(ProceedingJoinPoint pjp) {
this.pjp = pjp;
}
#Override
public Object execute(Connection connection) throws SQLException {
boolean autoCommit = connection.getAutoCommit();
boolean readOnly = connection.isReadOnly();
try {
connection.setAutoCommit(false);
connection.setReadOnly(true);
return pjp.proceed();
} catch (Throwable e) {
//if an exception was raised, return it
return e;
} finally {
// restore state
connection.setReadOnly(readOnly);
connection.setAutoCommit(autoCommit);
}
}
}
private int order;
private EntityManager entityManager;
public void setOrder(int order) {
this.order = order;
}
#Override
public int getOrder() {
return order;
}
#PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
#Around("#annotation(readOnlyConnection)")
public Object proceed(ProceedingJoinPoint pjp,
ReadOnlyConnection readOnlyConnection) throws Throwable {
Session hibernateSession = entityManager.unwrap(Session.class);
Object result = hibernateSession.doReturningWork(new ReadOnly(pjp));
if (result == null) {
return result;
}
//If the returned object extends Throwable, throw it
if (Throwable.class.isAssignableFrom(result.getClass())) {
throw (Throwable) result;
}
return result;
}
}
I annotate all my readOnly request with #ReadOnlyConnection. Before I had all my service layer methods annotated with that even though they might be calling each other. Now I am only annotating the request method and I am to the state, where I am getting the database updates on the second call.
1) Doing initial call => getting data as expected
2) Changing data in the database
3) Doing same call again => getting the exact same data from the first call
4) Doing same call again => getting the changed data
The thing with connection.setAutoCommit(false) is that it seems to not do a commit after set back to connection.setAutoCommit(true). So after adding the following line to the aspect, everything worked as expected again
try {
connection.setAutoCommit(false);
connection.setReadOnly(true);
return pjp.proceed();
} catch (Throwable e) {
return e;
} finally {
// restore state
connection.commit(); // THIS LINE
connection.setReadOnly(readOnly);
connection.setAutoCommit(autoCommit);
}

Hibernate 4.1.9 (latest final build) reporting `nested transactions not supported`

I am getting a
org.hibernate.TransactionException: nested transactions not supported
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.begin(AbstractTransactionImpl.java:152)
at org.hibernate.internal.SessionImpl.beginTransaction(SessionImpl.java:1395)
at com.mcruiseon.server.hibernate.ReadOnlyOperations.flush(ReadOnlyOperations.java:118)
Code that throws that exception. I am calling flush from a thread that runs infinite until there is data to flush.
public void flush(Object dataStore) throws DidNotSaveRequestSomeRandomError {
Transaction txD;
Session session;
session = currentSession();
// Below Line 118
txD = session.beginTransaction();
txD.begin() ;
session.saveOrUpdate(dataStore);
try {
txD.commit();
while(!txD.wasCommitted()) ;
} catch (ConstraintViolationException e) {
txD.rollback() ;
throw new DidNotSaveRequestSomeRandomError(dataStore, feedbackManager);
} catch (TransactionException e) {
txD.rollback() ;
} finally {
// session.flush();
txD = null;
session.close();
}
// mySession.clear();
}
Edit :
I am calling flush in a independent thread as datastore list contains data. From what I see its a sync operation call to flush, so ideally flush should not return until transaction is complete. I would like it that way is the least I want to expect. Since its a independent thread doing its job, all I care about it flush being a sync operation. Now my question is, is txD.commit a async operation ? Does it return before that transaction has a chance to finish. If yes, is there a way to get commit to "Wait" until the transaction completes ?
public void run() {
Object dataStore = null;
while (true) {
try {
synchronized (flushQ) {
if (flushQ.isEmpty())
flushQ.wait();
if (flushQ.isEmpty()) {
continue;
}
dataStore = flushQ.removeFirst();
if (dataStore == null) {
continue;
}
}
try {
flush(dataStore);
} catch (DidNotSaveRequestSomeRandomError e) {
e.printStackTrace();
log.fatal(e);
}
} catch (HibernateException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Edit 2 : Added while(!txD.wasCommitted()) ; (in code above), still I get that freaking nested transactions not supported. Infact due to this exception a record is not being written to by table too. Is there something to do with the type of table ? I have INNODB for all my tables?
Finally got the nested transaction not supported error fixed. Changes made to code are
if (session.getTransaction() != null
&& session.getTransaction().isActive()) {
txD = session.getTransaction();
} else {
txD = session.beginTransaction();
}
//txD = session.beginTransaction();
// txD.begin() ;
session.saveOrUpdate(dataStore);
try {
txD.commit();
while (!txD.wasCommitted())
;
}
Credits of above code also to Venkat. I did not find HbTransaction, so just used getTransaction and beginTransaction. It worked.
I also made changes in the hibernate properties due to advice on here. I added these lines to the hibernate.properties. This alone did not solve the issue. But I am leaving it there.
hsqldb.write_delay_millis=0
shutdown=true
You probably already began a transaction before calling this method.
Either this should be part of the enclosing transaction, and you should thus not start another one; or it shouldn't be part of the enclosing transaction, and you should thus open a new session and a new transaction rather than using the current session.

Categories

Resources