2011-05-11 53 views
4

這讓我感到莫名其妙。我搜索了很多論壇尋找線索。在postFlush方法中,我使用不同的會話工廠打開了一個新的hibernate(第3.3版 - 也嘗試過v.3.5)會話。我使用c3p0 v。0.9進行連接池。我開始一個新的事務,保存auditLog對象,並提交事務。這對所有我的實體保存一個很好。在刪除ChemoRegimen實體後嘗試提交auditLog時,應用程序掛起(這也會在創建和更新時發生)。不會拋出異常,但在中止線程我發現下面的堆棧跟蹤(這是一個Swing應用程序):爲什麼使用Hibernate Interceptor的審計日誌掛在SocketInputStream.socketRead0上?

Thread [AWT-EventQueue-0] (Suspended) 
SocketInputStream.socketRead0(FileDescriptor, byte[], int, int, int) line: not available [native method]  
SocketInputStream.read(byte[], int, int) line: 129 
VisibleBufferedInputStream.readMore(int) line: 145 
VisibleBufferedInputStream.ensureBytes(int) line: 114 
VisibleBufferedInputStream.read() line: 73 
PGStream.ReceiveChar() line: 274  
QueryExecutorImpl.processResults(ResultHandler, int) line: 1660 
QueryExecutorImpl.execute(Query[], ParameterList[], ResultHandler, int, int, int) line: 407 
Jdbc4PreparedStatement(AbstractJdbc2Statement).executeBatch() line: 2737  
NewProxyPreparedStatement.executeBatch() line: 1723 
BatchingBatcher.doExecuteBatch(PreparedStatement) line: 70 
BatchingBatcher(AbstractBatcher).executeBatch() line: 268 
ActionQueue.executeActions(List) line: 266 
ActionQueue.executeActions() line: 167 
DefaultFlushEventListener(AbstractFlushingEventListener).performExecutions(EventSource) line: 321 
DefaultFlushEventListener.onFlush(FlushEvent) line: 50 
SessionImpl.flush() line: 1027 
SessionImpl.managedFlush() line: 365  
JDBCTransaction.commit() line: 137 **[This is where I commit the auditLog]** 
MomsInterceptor.postFlush(Iterator) line: 254 
DefaultFlushEventListener(AbstractFlushingEventListener).postFlush(SessionImplementor) line: 375  
DefaultFlushEventListener.onFlush(FlushEvent) line: 51 
SessionImpl.flush() line: 1027 
SessionImpl.managedFlush() line: 365  
JDBCTransaction.commit() line: 137 
HibernateDAO.makeTransient(Entity) line: 119  
ChemoServices.deleteChemoRegimen(ChemoRegimen, String, Session) line: 290 

我使用PostgreSQL 8.4與9.0 jdbc4驅動後端。該postgresql.log顯示[我的括號內註釋]:

[首先,chemo_regimen被刪除]

2011-05-11 12:19:06 CDT moms postgres LOG: 00000: execute <unnamed>: delete from moms_chemo_regimen where crxreg_id=$1<BR> 

....
[很多級聯刪除的]
...
[然後,我在交易的攔截器會話開始]

2011-05-11 12:19:06 CDT moms postgres LOG: 00000: execute S_1: BEGIN 
2011-05-11 12:19:06 CDT moms postgres LOG: 00000: execute <unnamed>: select nextval ('moms_patient_change_log_seq') 

[審計日誌記錄插入]

2011-05-11 12:19:06 CDT moms postgres LOG: 00000: execute <unnamed>: insert into moms_patient_change_log (patclog_pat_id, patclog_action, patclog_reason, patclog_date, patclog_user_name, patclog_guid, patclog_id) values ($1, $2, $3, $4, $5, $6, $7) 
2011-05-11 12:19:06 CDT moms postgres DETAIL: parameters: $1 = '17108', $2 = 'Deleted ChemoRegimen ABVD', $3 = NULL, $4 = '2011-05-11 12:19:06.813', $5 = 'daver', $6 = 'BFAA9D91-7A4E-835E-7A57-B72B2A79A4F1', $7 = '520' 

,就是這樣。審計日誌插入事務永遠不會完成。刪除化療方案的交易也從不承諾。我沒有審覈時能夠對ChemoRegimen執行CRUD。在ChemoRegimen實體的片斷如下:

public class ChemoRegimen extends MOMSEntity implements Auditable 
{ 
    public static final String UNSCHEDULED = "UNSCHEDULED"; 

    private Date date = new Date(); 
    private Patient patient; 
    private WorkingProtocol protocol; 
    private Physician approvingPhysician; 
    private boolean canChangeCycles; 
    private List<ChemoEncounter> chemoEncounters = new ArrayList<ChemoEncounter>(); 
    private boolean complete; 
    private Icdm icdm;<BR> 
    ...<BR> 
} 

這是我的攔截器:

public class MomsInterceptor extends EmptyInterceptor 
{ 
    private static Logger logger = Logger.getLogger(MomsInterceptor.class.getName()); 
    private static Configuration configuration; 
    private static SessionFactory sessionFactory; 

//Create the initial SessionFactory from the default configuration files 
    static 
    { 
    initSessionFactory(); 
    } 

    public static void initSessionFactory() 
    { 
    try 
    { 
     configuration = new Configuration().configure(); 
     sessionFactory = configuration.buildSessionFactory(); 
    } 
    catch (Throwable ex) 
    { 
     // We have to catch Throwable, otherwise we will miss 
     // NoClassDefFoundError and other subclasses of Error 
     logger.severe("Building SessionFactory failed - " + ex.getMessage()); 
     System.err.println("Building SessionFactory failed - " + ex.getMessage()); 
     throw new ExceptionInInitializerError(ex.getMessage()); 
    } 
    } 

    private Set<Auditable> inserts = new HashSet<Auditable>(); 
    private Set<UpdatedEntity> updates = new HashSet<UpdatedEntity>(); 
    private Set<Auditable> deletes = new HashSet<Auditable>(); 
    private boolean audit; 

    public MomsInterceptor(boolean audit) 
    { 
    super(); 
    this.audit = audit; 
    } 

    private class UpdatedEntity 
    { 
    private Auditable auditable; 
    private String[] propertyNames; 
    private Object[] currentState; 
    private Object[] previousState; 
    private Type[] types; 

    public UpdatedEntity(Auditable auditable, String[] propertyNames, Type[] types, Object[] currentState, Object[] previousState) 
    { 
     super(); 
     this.auditable = auditable; 
     this.propertyNames = propertyNames; 
     this.currentState = currentState; 
     this.previousState = previousState; 
     this.types = types; 
    } 

    public Auditable getAuditable() 
    { 
     return auditable; 
    } 

    public String[] getPropertyNames() 
    { 
     return propertyNames; 
    } 

    public Object[] getCurrentState() 
    { 
     return currentState; 
    } 

    public Object[] getPreviousState() 
    { 
     return previousState; 
    } 

    public Type[] getTypes() 
    { 
     return types; 
    } 

    /** 
    * Return the previous value of the property name prop or null if the property name is not found. 
    * @param prop 
    * @return 
    */ 
    public Object getPrevious(String prop) 
    { 
     int i = 0; 
     for (String name : propertyNames) 
     { 
     if (prop.equals(name)) 
      return previousState[i]; 
     i++; 
     } 

     return null; 
    } 
    } 

    public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) throws CallbackException 
    { 
    boolean modified = false; 
    if (entity instanceof MutableEntity) // Update create info. 
    { 
     MutableEntity me = (MutableEntity)entity; 
     int i = findPropertyNameIndex("createUser", propertyNames); 
     if (i >= 0) 
     state[i] = SessionController.userName; 
     i = findPropertyNameIndex("modifyUser", propertyNames); 
     if (i >= 0) 
     state[i] = SessionController.userName; 
     modified = true; 

     if (audit && entity instanceof Auditable) 
     inserts.add((Auditable)entity); 
    } 

    return modified; 
    } 

    private int findPropertyNameIndex(String name, String[] propertyNames) 
    { 
    int i = -1; 
    if (propertyNames.length == 0) 
     return i; 

    for (String p : propertyNames) 
    { 
     i++; 
     if (p.equals(name)) 
     return i; 
    } 

    return -1; 
    } 

    public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) 
     throws CallbackException 
    { 
    boolean modified = false; 

    if (entity instanceof MutableEntity) // Update modify info. 
    { 
     MutableEntity me = (MutableEntity)entity; 
     int i = findPropertyNameIndex("modifyUser", propertyNames); 
     if (i >= 0) 
     currentState[i] = SessionController.userName; 
     i = findPropertyNameIndex("modifyDate", propertyNames); 
     if (i >= 0) 
     currentState[i] = new Date(); 
     modified = true; 

     if (audit && entity instanceof Auditable) 
     updates.add(new UpdatedEntity((Auditable)entity, propertyNames, types, currentState, previousState)); 
    } 

    return modified; 
    } 

    @Override 
    public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) 
    { 
    if (audit && entity instanceof Auditable) 
     deletes.add((Auditable)entity); 
    } 

    @Override 
    public void postFlush(Iterator iterator) throws CallbackException 
    { 
    if (inserts.isEmpty() && deletes.isEmpty() && updates.isEmpty()) 
     return; 

    Session session = sessionFactory.openSession(); 
    session.setFlushMode(FlushMode.COMMIT); 
    session.beginTransaction(); 

    try 
    { 
     String action = null; 
     for (Auditable entity : inserts) 
     { 
     action = "Created " + entity.getClass().getSimpleName() + " " + entity.toString(); 
     session.save(new PatientChangeLog(entity.getPatient(), action, entity.getReason(), SessionController.userName)); 
     } 
     for (Auditable entity : deletes) 
     { 
     action = "Deleted " + entity.getClass().getSimpleName() + " " + entity.toString(); 
     session.save(new PatientChangeLog(entity.getPatient(), action, entity.getReason(), SessionController.userName)); 
     } 
     for (UpdatedEntity entity : updates) 
     { 
     Auditable a = entity.getAuditable(); 
     StringBuffer actionBuf = new StringBuffer("Updated " + a.getClass().getSimpleName() + " " + a.toString() + ": changed "); 
     int count = 0; 
     for (int i = 0; i < entity.getPropertyNames().length; i++) 
     { 
      String prop = entity.getPropertyNames()[i]; 
      Type type = entity.getTypes()[i]; 
      Object curValue = entity.getCurrentState()[i]; 
      Object prevValue = entity.getPreviousState()[i]; 

      //Don't consider the id field or the metadata fields. 
      if (prop.equals("id") || prop.equals("createUser") || prop.equals("createDate") || prop.equals("modifyUser") 
       || prop.equals("modifyDate") || prop.equals("guid")) 
      continue; 

      if (prevValue == null) 
      prevValue = new String(""); 
      if (curValue == null) 
      curValue = new String(""); 
      if (!prevValue.equals(curValue)) 
      { 
      if (count > 0) 
       actionBuf.append(" and "); 
      actionBuf.append(prop).append(" from '").append(prevValue).append("' to '").append(curValue).append("'"); 
      count++; 
      } 
     } 

     Patient p = (Patient)entity.getPrevious("patient"); //In case the patient is changed, tie it to the previous patient. 
     session.save(new PatientChangeLog(p, actionBuf.toString(), a.getReason(), SessionController.userName)); 
     } 

     session.getTransaction().commit(); 
    } 
    catch (HibernateException e) 
    { 
     try 
     { 
     session.getTransaction().rollback(); 
     } 
     catch (Exception hex) 
     { 
     throw new RuntimeException(hex); 
     } 
     throw new RuntimeException(e); 
    } 
    finally 
    { 
     inserts.clear(); 
     updates.clear(); 
     deletes.clear(); 
     session.close(); 
    } 
    } 
} 

任何幫助將不勝感激。

回答

0

我猜你在審計事務和Chemo事務之間的數據庫中存在死鎖。

1

它看起來像事務之間的死鎖:審計事務等待一些資源被主事務鎖定,但主事務無法提交,因爲攔截器的執行被阻止。

雖然Interceptor的機制不允許它在同一個事務中執行審計操作,但它在概念上可能更清楚。您可以嘗試使用events來代替 - 至少,Envers將它們用於審計操作。

+0

謝謝axtavt和@SteveHall。我將研究hibernate事件。 – David 2011-05-11 19:18:41

+0

axtavt,你知道關於使用事件來實現審計日誌而不訴諸於Envers的任何文檔嗎?由於我們不使用註釋,因此Envers需要主要的Hibernate相關更改。我一直無法找到像這樣的任何文檔。 – David 2011-05-13 20:46:05

2

無法找到合適的文檔來使用Hibernate事件來代替Envers,我決定將代碼從MyInterceptor.postFlush()移動到MyInterceptor.afterTransactionCompletion()。這樣,我在爲審計日誌記錄打開一個新的會話/事務之前已經提交了當前事務。感謝您的幫助。