8

有时我的应用程序中出现格式错误的异常。异常接踵而至,如下:

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)

当在数据库上执行选择查询时会发生这种情况,例如:

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=?

或者:

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

第一个查询是归档类的命名查询,如下所示:

@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;

由此方法调用:

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;
}

第二个是通过这个方法调用的:

@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;
}

这两种方法都应该通过 setEntityManager(database); 设置用户选择的数据库;

我不知道是什么原因导致发生此异常!有人知道吗?

4

3 回答 3

8

问题很可能与线程安全有关:当我尝试从同一个用户会话中并行访问两个单独线程的表时出现此错误(同一浏览器页面有两个并行运行的 ajax 请求)。

当我将访问权限更改为串行时摆脱了它。不知道是不是和你一样的问题,值得一试。

于 2012-05-21T18:51:33.913 回答
5

我已经研究了几天类似的问题。Supra 的回答为我指明了正确的方向:线程安全。

事实上,Hibernate 中的Session不是线程安全的,我们不应该允许 2 个线程访问同一个会话。这是“级联危险时冲洗”错误的可能原因之一。两个线程访问同一个会话会导致很多意外行为,因为 Hibernate 不是为此而设计的。

通常我会使用一个框架来做这种事情,但如果你需要自己做,你可以制作一个静态的ThreadLocal变量。

TL;DR :SessionFactory是线程安全的,Session不是。为每个线程提供一个新会话(通过自己做或使用 DI 框架)

于 2015-12-04T01:56:42.170 回答
0

我在 Spring 应用程序中使用 JPA EntityListeners 时遇到了同样的错误。通过使用解决了它TransactionSynchronizationManager.registerSynchronization(),但我不知道它是否是最佳实践(Kotlin 中的代码):

@PrePersist
fun prePersist(entity: EntityType) {
    TransactionSynchronizationManager.registerSynchronization(object : TransactionSynchronization {
        override fun beforeCommit(readOnly: Boolean) {
            /* Do your transactional stuff here */
            super.beforeCommit(readOnly)
        }
    })
}
于 2021-10-05T06:54:59.657 回答