這讓我感到莫名其妙。我搜索了很多論壇尋找線索。在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();
}
}
}
任何幫助將不勝感激。
謝謝axtavt和@SteveHall。我將研究hibernate事件。 – David 2011-05-11 19:18:41
axtavt,你知道關於使用事件來實現審計日誌而不訴諸於Envers的任何文檔嗎?由於我們不使用註釋,因此Envers需要主要的Hibernate相關更改。我一直無法找到像這樣的任何文檔。 – David 2011-05-13 20:46:05