2012-05-18 14 views
7

A volte viene rilevata un'eccezione non valida nella mia applicazione. Eccezioni si susseguono come segue:HibernateException: il lavaggio durante la cascata è pericoloso

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) 

Essa si verifica quando una query di selezione è eseguita sul database come i seguenti:

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

o:

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 

La prima query è una query di nome per la classe archivio come questo:

@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; 
.515.053.691,36321 milioni

che è chiamato da questo metodo:

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

Il secondo è chiamato da questo metodo:

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

Entrambi i metodi dovrebbero impostare il database utente sceglie da setEntityManager (database);

Non so che cosa causi questa eccezione! Qualcuno ne sa qualcosa a riguardo?

+0

qual è la domanda? "può succedere di nuovo" -> dipende dal codice – Firo

risposta

6

Molto probabilmente il problema è con Thread Safety: Ho ricevuto questo errore quando stavo cercando di accedere alle tabelle da due thread separati in parallelo dalla stessa sessione utente (la stessa pagina del browser aveva due richieste Ajax in esecuzione in parallelo).

Si è sbarazzato di esso quando ho cambiato l'accesso alla seriale. Non sono sicuro se questo è lo stesso problema con te, vale la pena provare.

+0

puoi approfondire questo argomento: "cambiato l'accesso al seriale"? Non so cosa significhi. –

+0

Ho semplicemente avuto due richieste parallele alla risorsa da chiamate jQuery asincrone, che ho fatto quindi sincronizzare. Questa non è la soluzione ideale, ma usala solo per diagnosticare e restringere il tuo problema particolare. – Supra

+0

oh così è come .. –

3

Ho ricercato un problema simile per alcuni giorni. La risposta di Supra mi indica la giusta direzione: sicurezza del filo.

Il fatto è chein Hibernate non è thread-safe, e non dovremmo permettere che 2 thread accedano alla stessa sessione. Questa è una delle possibili cause dell'errore "Flush quando la cascata è pericolosa". Due thread accedono alla stessa sessione può causare molti comportamenti imprevisti, perché Hibernate non è progettato per questo.

Di solito userei un framework per fare questo genere di cose, ma se hai bisogno di farlo da solo, puoi creare una variabile statica ThreadLocal.

TL; DR: SessionFactory è thread-safe, Session no. Fornire una nuova sessione per ogni thread (sia da solo o utilizzando un framework DI)

+0

Credo che sia quasi la conoscenza più fondamentale quando si utilizza JDBC/Hibernate/JPA che Connection/Session/EntityManager deve essere utilizzato solo sotto un thread.Non riesco a immaginare ci siano davvero dei casi che violano questo –

+1

Sì, il caso esiste. Per me è perché ho rovinato la gestione dello stile di vita a Castle Windsor (sono passato di recente allo stack C#), il risultato è stato che la sessione non è per-thread come pensavo. Ma posso dire da 3 giorni di ricerca, questo non è l'unico caso –

+1

Ho visto una domanda simile in passato, ma soprattutto per quelle applicazioni a livello di giocattolo o di autoapprendimento. Fortunatamente non ho riscontrato casi del genere nella mia vita di sviluppo per le applicazioni della vita reale: P L'uso corretto dei framework fa davvero risparmiare un sacco di problemi :) –

Problemi correlati