標籤:
網上搜尋發現,實現使用者動作記錄的方式有:自訂註解方式、Hibernate攔截器方式、Hibernate監聽器方式等。
1、自訂註解方式較為麻煩,需要進行操作記錄的方法均需要添加註解,但是相對的操作描述更為針對性,缺點是無法獲得所操作的實體ID以及成員;
2、攔截器方式經我自己實驗,攔截器是在Hibernate操作資料庫之前執行的,所以同樣擷取不了所操作的實體ID和成員,但是相對註解方式來說,不用在原有代碼上更改添加註解等,耦合性比較低。
使用攔截器需要保證資料庫操作均是對實體類的操作,即使用save、update、delete、get、load等方式,原生sql語句的執行是不會被攔截的;
3、監聽器方式是我最終採用的方法,監聽器是在Hibernate操作資料庫之後執行的回調方式,可以擷取操作實體的ID和成員變數,同樣的相對業務層耦合性低,
使用監聽器需要保證資料庫操作均是對實體類的操作,即使用save、update、delete、get、load等方式,原生sql語句的執行是不會被攔截的。
下邊展示My Code:
這部分是監聽器的註冊部分
1 import javax.annotation.PostConstruct; 2 3 import org.hibernate.SessionFactory; 4 import org.hibernate.event.service.spi.EventListenerRegistry; 5 import org.hibernate.event.spi.EventType; 6 import org.hibernate.internal.SessionFactoryImpl; 7 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.stereotype.Component; 9 10 /** 11 * hibernate的事件監聽註冊 12 * @author tianzhen13 */ 14 @Component 15 public class HibernateEvent { 16 17 @Autowired18 private SessionFactory sessionFactory; 19 @Autowired 20 private OperListener operListener; 21 22 @PostConstruct 23 public void registerListeners() { 24 EventListenerRegistry registry = ((SessionFactoryImpl) sessionFactory).getServiceRegistry().getService( 25 EventListenerRegistry.class); 26 registry.getEventListenerGroup(EventType.POST_COMMIT_INSERT).appendListener(operListener);//對實體儲存的監聽27 registry.getEventListenerGroup(EventType.POST_COMMIT_UPDATE).appendListener(operListener);//對實體修改的監聽28 registry.getEventListenerGroup(EventType.POST_COMMIT_DELETE).appendListener(operListener);//對實體刪除的監聽29 } 30 }
這部分是監聽器
import java.io.Serializable;import java.text.SimpleDateFormat;import java.util.Date;import javax.servlet.http.HttpSession;import org.hibernate.Session;import org.hibernate.event.spi.PostCommitDeleteEventListener;import org.hibernate.event.spi.PostCommitInsertEventListener;import org.hibernate.event.spi.PostCommitUpdateEventListener;import org.hibernate.event.spi.PostDeleteEvent;import org.hibernate.event.spi.PostInsertEvent;import org.hibernate.event.spi.PostUpdateEvent;import org.hibernate.persister.entity.EntityPersister;import org.hibernate.type.Type;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import com.nuctech.model.Useroper;import com.nuctech.util.Constant;/** * hibernate的事件監聽 * @author tianzhen */ @Componentpublic class OperListener implements PostCommitDeleteEventListener,PostCommitInsertEventListener,PostCommitUpdateEventListener{ private static final long serialVersionUID = -4253791237768268101L; @Autowired private HttpSession httpSession; /** * 監聽修改事件 */ @Override public void onPostUpdate(PostUpdateEvent event) { StringBuffer des = new StringBuffer();//操作描述 des.append("更新操作,更新內容{"); String diff = arrayDiff(event.getState(), event.getOldState(), event.getPersister().getPropertyNames(), event.getPersister().getPropertyTypes());//判斷修改了哪些部分,並拼接成字串 des.append(diff); des.append("}"); saveOperLog(event.getSession(), event.getEntity().getClass().getSimpleName(), event.getId(), des); } /** * 監聽插入事件 */ @Override public void onPostInsert(PostInsertEvent event) { if(!(event.getEntity() instanceof Useroper)){//當是對使用者動作表的插入時,不進行操作,否則進入死迴圈 StringBuffer des = new StringBuffer();//操作描述 des.append("建立操作,建立內容{"); String inser = arrayToString(event.getState(), event.getPersister().getPropertyNames(), event.getPersister().getPropertyTypes());//判斷添加的哪些成員,並拼接成字串 des.append(inser); des.append("}"); saveOperLog(event.getSession(), event.getEntity().getClass().getSimpleName(), event.getId(), des); } } /** * 監聽刪除事件 */ @Override public void onPostDelete(PostDeleteEvent event) { StringBuffer des = new StringBuffer();//操作描述 des.append("刪除操作,刪除內容{"); String del = arrayToString(event.getDeletedState(), event.getPersister().getPropertyNames(), event.getPersister().getPropertyTypes());//判斷刪除了哪些成員,並進行拼接 des.append(del); des.append("}"); saveOperLog(event.getSession(), event.getEntity().getClass().getSimpleName(), event.getId(), des); } /** * 日誌的添加 * @param session * @param des */ private void saveOperLog(Session session, String tableName, Serializable targetId, StringBuffer des){ int currUserId = (int) httpSession.getAttribute(Constant.CURRENT_USERID);//擷取操作人 Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String time = sdf.format(date);//操作日期 String sql = "INSERT useroper(UserID,TableName,TargetID,OperDesc,OperTime) VALUES("+currUserId+",‘"+tableName+"‘,"+targetId+",‘"+des.toString()+"‘,‘"+time+"‘)"; Session logSession = session.getSessionFactory().openSession();//重新開啟一個新的Hibernate session,並在使用完進行關閉,不可使用原session。 logSession.createSQLQuery(sql).executeUpdate(); logSession.close(); } /** * 數組轉字串 * @param o 成員值 * @param names 成員名 * @param types 成員類型 * @return */ private String arrayToString(Object[] o, String[] names, Type[] types){ StringBuffer result = new StringBuffer(); for(int i=0;i<o.length;i++){ if(types[i].isAssociationType())//外鍵忽略處理 continue; result.append(names[i]+":"+o[i]+";"); } if(result.toString().equals("")) result.append(";"); return result.substring(0, result.length()-1); } /** * 數組不同部分轉字串 * @param n 成員新值 * @param o 成員原值 * @param names 成員名 * @param types 成員類型 * @return */ private String arrayDiff(Object[] n, Object[] o, String[] names, Type[] types){ StringBuffer result = new StringBuffer(); //各參數數組均按序傳進來的,按index取值即可 for(int i=0;i<n.length;i++){ if(types[i].isAssociationType())//外鍵忽略處理 continue; //如不相等,則加入字串中 if(!String.valueOf(n[i]).equals(String.valueOf(o[i]))){ result.append(names[i]+":"+o[i]+">"+n[i]+";"); } } return result.substring(0, result.length()-1); } @Override public boolean requiresPostCommitHanding(EntityPersister persister) { return true; } @Override public void onPostUpdateCommitFailed(PostUpdateEvent event) { } @Override public void onPostInsertCommitFailed(PostInsertEvent event) { } @Override public void onPostDeleteCommitFailed(PostDeleteEvent event) { }}
我使用的監聽器介面均為PostCommitDeleteEventListener、PostCommitInsertEventListener、PostCommitUpdateEventListener,而不是PostDeleteEventListener、PostInsertEventListener、PostUpdateEventListener,前三者是商務邏輯對資料庫操作已經Commit後進行回調,後三者則不是,後三者在進行監聽時,雖然可以擷取各項值,但是在對值進行資料庫記錄時就會很麻煩,容易出現事物鎖等待逾時的Bug,導致業務處理也不能完成,本人菜鳥沒有找到解決辦法,用的是前三者的介面,anyway,實現功能效果就好,哈哈
利用Hibernate監聽器實現使用者動作記錄