Hibernate审计日志时业务数据无法同步问题解决
整个项目采用SpringMVC3.0+Hibernate3.6作为基础框架,项目刚开始没有加入日志审计功能,业务添加、删除、修改、查询都正常,没有任何问题。后期增加了操作历史的审计日志功能后,添加、修改、删除数据时,页面的数据时十有八九不能刷新。说明一下,日志审计功能使用Hibernate的监听器来实现的。
业务很简单,添加、修该、删除功能都用Ajax异步提交,调用成功后,根据Action方法的返回值,来刷新列表数据。调试跟踪代码也没找到问题,数据都能及同步数据库,也没有丢过数据。但有一点是调试时,页面数据总能刷出新增的数据。尝试在Action返回页面之前然线程休眠1秒钟,页面就可以刷出本次添加的数据,问题很诡异啊。果断画出调用时序图仔细分析一下:
从表面看,很像是数据脏都问题,刷新数据时,读取到了脏数据。事物也没发现问题,但为什么读到脏数据呢,不解啊。看以下审计代码,感觉有段代码很奇怪,伪代码如下
public class HistoryListener implementsPostInsertEventListener, PostUpdateEventListener,PostDeleteEventListener {
void onPostInsert (PostInsertEvent event){
Loglog = new Log();
Log.setAction(ACTION.INSERT);
Log.setXXXX(event.getState().getXX());
……
saveOperation(log,event.getSession());
}
…..
saveOperation(Log log,Session session){
Transacton tx = session.beginTransaction();
try{
session.save(log);
session.commit()
}catch{
If(tx!=null){
tx.rollback();
}
}
}
}
问题找到了,从画出的时序图可以看出,Hibernate监听器其实是异步实现的,图中的动作3成功返回和动作4saveActionLog()是异步执行的,在监听器代码中event.getSession()和Service中用的是同一session,这就延长session的生命周期,成功返回后页面刷新调用list这个时间间隔非常小,上一个session没有结束数据可能还没有flush(),所以list()返回的数据可能是没有同步之前的数据。
缩短回话的生命周期
public class HistoryListener implementsPostInsertEventListener, PostUpdateEventListener, PostDeleteEventListener {
voidonPostInsert (PostInsertEvent event){
Log log = new Log();
Log.setAction(ACTION.INSERT);
Log.setXXXX(event.getState().getXX());
……
//saveOperation(log,event.getSession()); 不能使用这个session保存日志
saveOperation(log);
}
…..
saveOperation(finalLog log){
//使用新的线程会开启一个新的session
new Thread(new Runnable(){
void run(){
LogService logService =ServiceFaced.getService(LogService.class);
logservice.save(log);
}
}).start()
}catch{
If(tx!=null){
tx.rollback();
}
}
}
}
业务系统中的session尽可能只用来进行业务操作,生命周期应该和页面请求保持一致(页面发起request请求时, session生命周期开时开始,页面response时,生命周期结束)。如果session的生命周期是绑定在线程(Thread)上,做审计日志,尽可能使用一个新的线程来记录日志,这也本例子的解决办法。