Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HibernateException: Flush during cascade is dangerous

Tags:

hibernate

jpa

Sometimes I get a malformed exception in my application. Exceptions come one after another as the following:

Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.GeneratedMethodAccessor77.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:592)
at org.apache.wicket.RequestListenerInterface.invoke(RequestListenerInterface.java:183)
... 22 common frames omitted   

Caused by: org.springframework.orm.jpa.JpaSystemException: Error while commiting the transaction; nested exception is javax.persistence.RollbackException: Error while commiting the transaction
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:294)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerSynchronization.convertCompletionException(ExtendedEntityManagerCreator.java:483)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerSynchronization.afterCommit(ExtendedEntityManagerCreator.java:464)
at org.springframework.transaction.support.TransactionSynchronizationUtils.invokeAfterCommit(TransactionSynchronizationUtils.java:90)

Caused by: javax.persistence.RollbackException: Error while commiting the transaction
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:71)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerSynchronization.afterCommit(ExtendedEntityManagerCreator.java:461)
... 52 common frames omitted

Caused by: org.hibernate.HibernateException: Flush during cascade is dangerous
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:996)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:54)

It occurs when a select query is executed on database such as the followings:

select archive0_.ARCHIVE_KEY as ARCHIVE1_16_, 
   archive0_.ARCHIVE_DATE as ARCHIVE2_16_, 
   archive0_.ARCHIVE_TYPE as ARCHIVE3_16_, 
   archive0_.DELETION_DATE as DELETION4_16_, 
   archive0_.ENCODING as ENCODING16_, 
   archive0_.FILENAME as FILENAME16_, 
   archive0_.JMSPROPERTIES as JMSPROPE7_16_, 
   archive0_.MESSAGE_ID as MESSAGE8_16_, 
   archive0_.MESSAGE_TYPE as MESSAGE9_16_, 
   archive0_.MESSAGE_VERSION as MESSAGE10_16_, 
   archive0_.PAYLOAD as PAYLOAD16_, 
   archive0_.SERVICE_NAME as SERVICE12_16_, 
   archive0_.TYPE_OF_SERVICE as TYPE13_16_, 
   archive0_.TIME_TO_LIVE as TIME14_16_, 
   archive0_.TRANSACTION_ID as TRANSAC15_16_ 
from   LOGGING.ARCHIVED_MESSAGES archive0_ 
where  archive0_.MESSAGE_ID=? and archive0_.SERVICE_NAME=?

or:

select audit0_.AUDIT_ID as AUDIT1_17_, 
   audit0_.CODE as CODE17_, 
   audit0_.FLOW_NAME as FLOW3_17_, 
   audit0_.KEY_FIELD_NAME as KEY4_17_, 
   audit0_.KEY_FIELD_VALUE as KEY5_17_, 
   audit0_.LOG_TIME as LOG6_17_, 
   audit0_.MESSAGE_ID as MESSAGE7_17_, 
   audit0_.MESSAGE_SIZE as MESSAGE8_17_, 
   audit0_.MESSAGE_TYPE as MESSAGE9_17_, 
   audit0_.MESSAGE_TYPE_VERSION as MESSAGE10_17_, 
   audit0_.PRIORITY as PRIORITY17_, 
   audit0_.RECEIVER as RECEIVER17_, 
   audit0_.SENDER as SENDER17_, 
   audit0_.SERVICE_NAME as SERVICE14_17_, 
   audit0_.TRANSACTION_ID as TRANSAC15_17_, 
   audit0_.TRANSPORT_ID as TRANSPORT16_17_ 
from   LOGGING.AUDIT audit0_ 
where  audit0_.TRANSACTION_ID=? 
and    (audit0_.LOG_TIME between ? and ?) 
order by audit0_.LOG_TIME

The first query is a named query for Archive class like this:

@Entity
@Table(schema = "LOGGING", name = "ARCHIVED_MESSAGES")
@NamedQuery(name = "findArchiveByMessageIdAndServiceName", query = "SELECT ar FROM        Archive ar WHERE ar.messageId = :messageId and ar.serviceName = :serviceName")
public class Archive implements Serializable {
private static final long serialVersionUID = 1L;

which is called by this method:

public Archive findArchive(String database, Audit audit) {
    LOGGER.debug("findArchive {}", audit);
    setEntityManager(database);
    javax.persistence.Query query;
    Archive archive = null;

    // Get the query
    query = em.createNamedQuery("findArchiveByMessageIdAndServiceName");

    // Set the parameters
    query.setParameter("messageId", audit.getMessageId());
    query.setParameter("serviceName", audit.getServiceName());

    try {
        List archives = query.getResultList();
        if(archives != null && archives.size() != 0)
            archive = (Archive) archives.get(0);
    } catch (NoResultException e) {
        // Ok so not all audit records have a matching archive but...
    } catch (Exception e) {
        // Any other error is a problem
        LOGGER.error("Failed to find archive", e);
    }

    return archive;
}

The second one is called by this method:

@SuppressWarnings("unchecked")
public Iterator findByCriteria(Criteria criteria, int first, int count) {
    LOGGER.debug("findByCriteria {}", criteria);
    setEntityManager(criteria.getDatabase());
    StringBuffer queryString = new StringBuffer();
    Query query;

    queryString.append("SELECT MAX(a.code), MIN(a.logTime), MAX(a.logTime), "
            + "a.transactionId as transactionId, a.sender as sender, a.receiver as receiver ");

    //Add the common where clause
    queryString.append(" FROM Audit a where a.logTime BETWEEN :from AND :to");
    // Append the appropriate addition where clauses depending on what
    // values are set 
    if (criteria.getSender() != null
            || criteria.getFilter().getSender() != null) {
        queryString.append(" AND a.sender = :sender");
    }

    if (criteria.getReceiver() != null
            || criteria.getFilter().getReceiver() != null) {
        queryString.append(" AND a.receiver = :receiver");
    }

    if (criteria.getMessageType() != null
            || criteria.getFilter().getMessageType() != null) {
        queryString.append(" AND a.messageType = :messageType");
    }
    if (criteria.getTransactionId() != null
            || criteria.getFilter().getTransactionId() != null) {
        queryString
                .append(" AND a.transactionId = :transactionId");
    }
    if (criteria.getKeyFieldValue() != null
            || criteria.getFilter().getKeyFieldValue() != null) {
        queryString
                .append(" AND UPPER(a.keyFieldValue) LIKE :keyFieldValue");
    }


    // We want a summary so lets group by the common ids
    queryString.append(" GROUP BY a.transactionId, a.sender, a.receiver ");

    // Add order by clause
    if (criteria.getOrderBy() != null) {
        queryString.append(" ORDER BY ");           
        queryString.append(criteria.getOrderBy());          
        queryString.append(criteria.isAscending() ? " ASC" : " DESC");
    }

    Session session = ((Session) em.getDelegate()).getSessionFactory().openSession();
    query = session.createQuery(queryString.toString());
    query.setReadOnly(true);
    query.setFetchSize(Integer.valueOf(1000));
    query.setCacheable(true);
    query.setCacheMode(CacheMode.NORMAL);

    // Will always have from and to dates
    query.setParameter("from", criteria.getFromDate());
    query.setParameter("to", criteria.getToDate());

    // Set remaining parameters depending on what is set
    if (criteria.getSender() != null) {
        query.setParameter("sender", criteria.getSenderValue());
    }

    // Override the search criteria with the filter if set
    if (criteria.getFilter().getSender() != null) {
        query.setParameter("sender", criteria.getFilter().getSender());
    }

    if (criteria.getReceiver() != null) {
        query.setParameter("receiver", criteria.getReceiverValue());
    }

    if (criteria.getFilter().getReceiver() != null) {
        query.setParameter("receiver", criteria.getFilter().getReceiver());
    }

    if (criteria.getMessageType() != null) {
        query.setParameter("messageType", criteria.getMessageTypeValue());
    }

    if (criteria.getFilter().getMessageType() != null) {
        query.setParameter("messageType", criteria.getFilter()
                .getMessageType());
    }
    if (criteria.getTransactionId() != null) {
        query.setParameter("transactionId", criteria.getTransactionId());
    }

    if (criteria.getFilter().getTransactionId() != null) {
        query.setParameter("transactionId", criteria.getFilter()
                .getTransactionId());
    }
    if (criteria.getKeyFieldValue() != null) {
        query.setParameter("keyFieldValue", "%"
                + criteria.getKeyFieldValue().toUpperCase() + "%");
    }

    if (criteria.getFilter().getKeyFieldValue() != null) {
        query.setParameter("keyFieldValue", "%"
                + criteria.getFilter().getKeyFieldValue().toUpperCase()
                + "%");
    }
    // Set the limits       
    query.setFirstResult(first);
    query.setMaxResults(PAGE_SIZE);

    Iterator iterator = query.list().iterator();
    session.close();
    return iterator;
}

Both methods should set the database that user chooses by setEntityManager(database);

I do not know what causes to occuring this exception! Does anyone know anything about it?

like image 926
Noushin Khaki Avatar asked May 18 '12 10:05

Noushin Khaki


2 Answers

Most likely the issue is with Thread Safety : I got this error when I was trying to access the tables from two separate threads in parallel from the same user session (same browser page had two ajax requests running in parallel).

Got rid of it when I changed the access to serial. Not sure if this is the same issue with you, worth a try.

like image 144
Supra Avatar answered Nov 18 '22 14:11

Supra


I have researched a similar problem for a few days. Supra's answer points me to the right direction: thread safety.

The fact is that Session in Hibernate is not thread-safe, and we should not allow 2 threads access the same session. That is one of possible causes of the error "Flush when cascading is dangerous". Two threads access the same session can cause lots of unexpected behavior, because Hibernate isn't designed for that.

Usually I'd use a framework to do this kind of stuff, but if you need to do it your self, you can make a static ThreadLocal variable.

TL;DR: SessionFactory is thread-safe, Session is not. Provide one new Session for each thread (either by do it yourself or utilizing a DI framework)

like image 5
Hoàng Long Avatar answered Nov 18 '22 15:11

Hoàng Long