SSH基于Hibernate eventListener 事件侦听器的操作日志自动保存到数据库

  1. 在spring xml配置文件中添加配置,包含:model、listener
  2. 在model中增加需要写入数据库对应表的model
  3. 在auditLog.xml配置文件中配置自己项目中,需要进行日志记录的model类shortName,以及相关属性。

相关代码如下:

首先spring xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <context:annotation-config />
<context:component-scan base-package="com.cqta" /> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:db.properties</value>
</list>
</property>
</bean> <bean id="dataSource" class="org.logicalcobwebs.proxool.ProxoolDataSource">
<property name="driver" value="${driver}" />
<property name="driverUrl" value="${driverUrl}" />
<property name="user" value="${user}" />
<property name="password" value="${password}" />
<property name="maximumActiveTime" value="600000" />
<property name="maximumConnectionCount" value="500" />
<property name="minimumConnectionCount" value="5" />
<property name="prototypeCount" value="5" />
<property name="simultaneousBuildThrottle" value="5" />
<property name="maximumConnectionLifetime" value="36000000" />
</bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mappingLocations">
<list>
<value>classpath*:com/cqta/dev/model/system/*.hbm.xml</value>
<value>classpath*:com/cqta/dev/model/authority/*.hbm.xml</value>
<value>classpath*:com/cqta/dev/model/question/*.hbm.xml</value>
<value>classpath*:com/cqta/dev/model/communication/*.hbm.xml</value>
<value>classpath*:com/cqta/dev/model/teacherwork/*.hbm.xml</value>
<value>classpath*:com/cqta/dev/model/workstation/*.hbm.xml</value>
<value>classpath*:com/cqta/dev/model/acupuncture/*.hbm.xml</value>
<value>classpath*:com/cqta/dev/model/online/*.hbm.xml</value>
<value>classpath*:com/cqta/dev/model/log/*.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect} </prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.jdbc.fetch_size">50</prop>
<prop key="hibernate.jdbc.batch_size">50</prop>
<prop key="hibernate.cache.use_query_cache ">true</prop>
<prop key="hibernate.connection.autocommit">true</prop>
</props>
</property>
<property name="eventListeners" >
<map>
<entry>
<key>
<value>post-insert</value>
</key>
<ref bean="auditlogEvent" />
</entry>
<entry>
<key>
<value>post-update</value>
</key>
<ref bean="auditlogEvent" />
</entry>
<entry>
<key>
<value>post-delete</value>
</key>
<ref bean="auditlogEvent" />
</entry>
</map>
</property>
<property name="packagesToScan" value="com.cqta.dev.model." />
</bean>
<bean id="auditlogEvent" class="com.cqta.dev.web.util.LogAuditListener"></bean> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean> <aop:config proxy-target-class="true"> </aop:config>
<!-- 如果Service层出现新的pacage,需要在此处添加配置 -->
<aop:config proxy-target-class="true">
<aop:advisor pointcut="execution(public * com.cqta.dev.service.system..*.*(..))" advice-ref="txAdvice" />
<aop:advisor pointcut="execution(public * com.cqta.dev.service.authority..*.*(..))" advice-ref="txAdvice" />
<aop:advisor pointcut="execution(public * com.cqta.dev.service.workstation..*.*(..))" advice-ref="txAdvice" />
<aop:advisor pointcut="execution(public * com.cqta.core.dao..*.*(..))" advice-ref="txAdvice" />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="edit*" propagation="REQUIRED" />
<tx:method name="save*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
</beans>

然后LogAuditListener.java代码

package com.cqta.dev.web.util;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import org.hibernate.event.PostDeleteEvent;
import org.hibernate.event.PostDeleteEventListener;
import org.hibernate.event.PostInsertEvent;
import org.hibernate.event.PostInsertEventListener;
import org.hibernate.event.PostUpdateEvent;
import org.hibernate.event.PostUpdateEventListener;
import org.springframework.security.core.userdetails.User;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException; import com.cqta.dev.model.log.Log;
import com.cqta.dev.model.log.LogAudit;
import com.cqta.dev.service.online.TxtContentService;
import com.cqta.dev.web.util.AppContextUtils; public class LogAuditListener implements PostInsertEventListener,PostUpdateEventListener, PostDeleteEventListener {
/**
*
*/
private static final long serialVersionUID = 1L;
private static String INSERT="添加";
private static String UPDATE="修改";
private static String DELETE="删除";
private static Map<String,LogAudit> logAuditMap=new HashMap<String, LogAudit>();
/**
* 添加操作 日志
*/
public void onPostInsert(PostInsertEvent event) {
Class<?> clazz = event.getEntity().getClass();
try {
Log log=null;
if (logAuditMap.size() > 0) {
LogAudit logAudit=logAuditMap.get(clazz.getSimpleName());
if (logAudit != null) {
log=newLog(logAudit,INSERT);
StringBuilder content = new StringBuilder(INSERT + logAudit.getDescription());//操作说明
User user = null; //操作人
user = SecurityUserHolder.getCurrentUser();
Object[] newState = event.getState();
String[] fields = event.getPersister().getPropertyNames();
if (newState != null && fields != null && newState.length == fields.length ) {
for (int i = 0; i < fields.length; i++) {
if(logAudit.getProperty().containsKey(fields[i])){
content.append( "\""+logAudit.getProperty().get(fields[i])+ "\" "+newState[i]);
}
}
}
log.setDescription(content.toString());
log.setActionUserName(user!=null?user.getUsername():null);
saveOperate(log);
}
}
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 修改操作 日志
*/
public void onPostUpdate(PostUpdateEvent event) {
Class<?> clazz = event.getEntity().getClass();
try {
Log log=null;
if (logAuditMap.size() > 0) {
LogAudit logAudit=logAuditMap.get(clazz.getSimpleName());
if (logAudit != null) {
log =newLog(logAudit,UPDATE);
String content = UPDATE + logAudit.getDescription(); //操作说明
User user = null; //操作人
user = SecurityUserHolder.getCurrentUser();
log.setActionUserName(user!=null?user.getUsername():null);
Object[] oldState = event.getOldState(); //获取旧值
Object[] newState = event.getState(); //获取更改后的值
String[] fields = event.getPersister().getPropertyNames(); //获取对象属性
if("User".equals(clazz.getSimpleName())){ //当对象是用用户时 将用户 单独拿出来 判断用户登录和修改密码
if(oldState != null && newState != null && fields != null && oldState.length == newState.length && oldState.length == fields.length){
boolean ref=false; //是否为登录操作
for (int i = 0; i < fields.length; i++) {
if(fields[i]=="lastlogintime" && oldState[i]!=newState[i]){ //登录操作 最后一次登录时间被修改
ref=false;break ;
}else if(fields[i]=="password" && oldState[i]!=newState[i]){ //登录操作 最后一次登录时间被修改
content = "修改密码";
ref=true;
}
if(logAudit.getProperty().containsKey(fields[i])&&! oldState[i].equals(newState[i])){
content += "{将 \"" + logAudit.getProperty().get(fields[i]) + "\" : \""+oldState[i]+ "\" 改为 \"" + String.valueOf(newState[i]) + "\"}";
ref=true;
}
}
if(ref){
log.setDescription(content);
saveOperate(log);
}
}
//如果修改的model不是USER
}else if (oldState != null && newState != null && fields != null && oldState.length == newState.length && oldState.length == fields.length ) {
for (int i = 0; i < fields.length; i++) { if(logAudit.getProperty().containsKey(fields[i])&&! oldState[i].equals(newState[i])){
content += "{将 \"" + logAudit.getProperty().get(fields[i]) + "\" : \""+oldState[i]+ "\" 改为 \"" + String.valueOf(newState[i]) + "\"}";
}
}
log.setDescription(content);
saveOperate(log);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 删除操作 日志
*/
public void onPostDelete(PostDeleteEvent event) {
Class<?> clazz = event.getEntity().getClass();
try {
Log log=null;
if (logAuditMap.size() > 0) {
LogAudit logAudit=logAuditMap.get(clazz.getSimpleName());
if (logAudit != null) {
log=newLog(logAudit,DELETE);
log.setDescription(DELETE+logAudit.getDescription());
User user = null; //操作人
user = SecurityUserHolder.getCurrentUser();
log.setActionUserName(user!=null?user.getUsername():null);
saveOperate(log);
}
}
} catch (Exception e) {
e.printStackTrace();
}
} public Log newLog(LogAudit audit,String operation){
Log log=new Log();
log.setAction(operation);
log.setCommand(audit.getOperate());
log.setActiontime(new Date());
return log;
}
/**
* 生成日志
* @param session
* @param entry
*/
private void saveOperate(final Log entry) {
//在新的线程中打开hibernate session,解决页面数据不同不问题.
new Thread(new Runnable() {
public void run() {
TxtContentService txtContentService = (TxtContentService) AppContextUtils.getBean("txtContentService");
txtContentService.saveObject(entry);
}
}).start();
}
/**
* 读取需做日志记录的XML配置文件
* @return
*/ static{
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.parse(LogAudit.class.getClassLoader().getResourceAsStream("auditLog.xml")); // 获取到xml文件
Element root = doc.getDocumentElement(); // 获取根元素
NodeList logAudits = root.getElementsByTagName("entity");
LogAudit log =null;
for (int i = 0; i < logAudits.getLength(); i++) {
Element ss = (Element) logAudits.item(i);
log= new LogAudit();
log.setClazz(ss.getAttribute("clazz")); //实体类
log.setDescription(ss.getAttribute("description")); //操作说明
log.setOperate(ss.getAttribute("operate")); //操作项
NodeList propertys = ss.getElementsByTagName("property");//属性
Map<String,String> proMap=new HashMap<String,String>();
for(int j = 0; j < propertys.getLength(); j++){
Element e = (Element) propertys.item(j);
proMap.put(e.getAttribute("name"), e.getTextContent());
}
log.setProperty(proMap);
logAuditMap.put(ss.getAttribute("clazz"), log);
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

最后auditLog.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<entities >
<!-- 记录的日志的实体类 -->
<entity clazz="User" description="用户" operate="用户管理">
<property name="username">用户名</property>
<property name="type">人员类型</property>
<property name="realname">真实姓名</property>
</entity>
<entity clazz="Datadictionary" description="数据字典" operate="数据字典管理">
<property name="name">分类名称</property>
<property name="optionName">选项名称</property>
<property name="mtype">所属类型</property>
</entity> </entities>

末尾附上相关model代码

Log.class

package com.cqta.dev.model.log;

import java.util.Date;

public class Log {
private int id;
private String action;// 用户所执行的数据操作:增|删|改
private String command;//执行的操作
private Date actiontime;// 执行此操作的时间
private String actionUserName;//操作者姓名
private int actionUserId;//操作者姓名
private String description;//操作说明
public String getAction() {
return action;
} public void setAction(String action) {
this.action = action;
} public String getCommand() {
return command;
} public void setCommand(String command) {
this.command = command;
} public Date getActiontime() {
return actiontime;
} public void setActiontime(Date actiontime) {
this.actiontime = actiontime;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getDescription() {
return description;
} public void setDescription(String description) {
this.description = description;
} public String getActionUserName() {
return actionUserName;
} public void setActionUserName(String actionUserName) {
this.actionUserName = actionUserName;
} public int getActionUserId() {
return actionUserId;
} public void setActionUserId(int actionUserId) {
this.actionUserId = actionUserId;
} }

Log.hbm.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.cqta.dev.model.log.Log" table="log" >
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="identity" />
</id>
<property name="action" type="string">
<column name="action">
</column>
</property>
<property name="command" type="string">
<column name="command">
</column>
</property>
<property name="description" type="string">
<column name="description">
</column>
</property>
<property name="actionUserId" type="java.lang.Integer">
<column name="actionUserId">
</column>
</property>
<property name="actionUserName" type="string">
<column name="actionUserName">
</column>
</property>
<property name="actiontime" type="date">
<column name="actiontime">
</column>
</property> </class>
</hibernate-mapping>

LogAudit.class

package com.cqta.dev.model.log;

import java.util.Map;

public class LogAudit {
private String description;//操作说明
private String clazz;//需要记录日志的类
private String operate;////操作项
private Map<String,String> property;
public String getDescription() {
return description;
} public void setDescription(String description) {
this.description = description;
} public String getOperate() {
return operate;
} public void setOperate(String operate) {
this.operate = operate;
} public String getClazz() {
return clazz;
} public void setClazz(String clazz) {
this.clazz = clazz;
} public Map<String,String> getProperty() {
return property;
} public void setProperty(Map<String,String> property) {
this.property = property;
}
}

mysql.sql-log

CREATE TABLE `log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`action` varchar(255) DEFAULT NULL,
`command` varchar(255) DEFAULT NULL,
`actiontime` date DEFAULT NULL,
`actionUserName` varchar(255) DEFAULT NULL,
`actionUserId` int(11) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;

/*
Navicat MySQL Data Transfer

Source Server : server
Source Server Version : 50602
Source Host : localhost:3306
Source Database : zy_dev

Target Server Type : MYSQL
Target Server Version : 50602
File Encoding : 65001

Date: 2014-12-15 16:55:42
*/

上一篇:计算机缺失缺少mfc110.dll等相关文件的解决办法


下一篇:ASP.NET Boilerplate 学习 AspNet Core2 浏览器缓存使用 c#基础,单线程,跨线程访问和线程带参数 wpf 禁用启用webbroswer右键菜单 EF Core 2.0使用MsSql/MySql实现DB First和Code First ASP.NET Core部署到Windows IIS QRCode.js:使用 JavaScript 生成