Hibernate4 拦截器(Interceptor) 实现实体类增删改的日志记录
来源:互联网 发布:淘宝长款半身裙 编辑:程序博客网 时间:2024/06/06 19:15
前言
开发应用程序的过程中,经常会对一些比较重要的数据修改都需要写日志。在实际工作的工程中,这些数据都是存在表中的, 一个常见的做法是用触发器,在增删改的时候,用触发器将数据写入到另一张表中去,但个人不推荐这么做,原因如下:
1. 如果有多个表,得写很多触发器。2. 触发器与数据库特性关联太紧,不同的数据库,虽然思路一样,但语法却不太一样。
对数据库表操作的日志记录,完全可以利用Hibernate的Interceptor特性来实现,也就是拦截器。下面用一个具体的例子来说明如何使用Hibernate的Interceptor。
1、创建一个表,用来记录日志的表
CREATE TABLE `auditlog` (`AUDIT_LOG_ID` BIGINT (20) UNSIGNED NOT NULL AUTO_INCREMENT,`ACTION` VARCHAR (100) NOT NULL,`DETAIL` text NOT NULL,`CreateD_DATE` DATE NOT NULL,`ENTITY_ID` BIGINT (20) UNSIGNED NOT NULL,`ENTITY_NAME` VARCHAR (255) NOT NULL,PRIMARY KEY (`AUDIT_LOG_ID`)) ENGINE = INNODB AUTO_INCREMENT = 9 DEFAULT CHARSET = utf8;
2、创建这个表对应的实体类:
@Entity@Table(name = "core_db.sys_audit_log")public class AuditLog extends BaseEntity { private static final long serialVersionUID = 3538696750590772955L; private String auditLogId; private String action; private String detail; private String entityId; private String entityName; public AuditLog() { } public AuditLog(String action, String detail, String entityId, String entityName) { this.action = action; this.detail = detail; this.entityId = entityId; this.entityName = entityName; } public AuditLog(String auditLogId, String action, String detail, String entityId, String entityName) { super(); this.auditLogId = auditLogId; this.action = action; this.detail = detail; this.entityId = entityId; this.entityName = entityName; } @Id @GenericGenerator(name = "systemUUID", strategy = "uuid") @GeneratedValue(generator = "systemUUID") @Column(name = "audit_id", unique = true, nullable = false) public String getAuditLogId() { return this.auditLogId; } public void setAuditLogId(String auditLogId) { this.auditLogId = auditLogId; } ......getter and setter 省略}
3、创建一个接口,所有实现了这个接口的实体类,都会写日志
public interface IAuditLog { public String getId(); public String getLogDeatil();}这里有两个方法,getId,getLogDetail 需要实现类去实现具体的方法,也就是要被写入到日志表中的详细记录.
4、创建一个类实现了IAuditLog 接口,并给出接口方法的具体实现
@Entity@Table(name = "core_db.sys_right")public class Right extends BaseEntity implements IAuditLog { private static final long serialVersionUID = -6438614246840973733L; /** 主键 */ private String rightId; /** 权限名称 */ @UpdateAnnotation private String rightName; /** 权限类型 */ @UpdateAnnotation private String rightType; /** 权限备注 */ @UpdateAnnotation private String rightRemark; @Id @GenericGenerator(name = "generator", strategy = "assigned") @GeneratedValue(generator = "generator") @Column(name = "right_id") public String getRightId() { return rightId; } public void setRightId(String rightId) { this.rightId = rightId; } ......省略部分 setter and getter @Transient @Override public String getPrimaryKey() { return this.rightId; } @Transient @Override public String getLogDeatil() { StringBuilder sb = new StringBuilder(); sb.append(" Right Id : ").append(rightId).append(" Right Name : ").append(rightName); return sb.toString(); }}5、创建记录日志的工具类,所有写日志公用
public class AuditLogUtil { /** * 数据库操作日志 * * @param action * @param entity * @param token */ public static void LogAuditIt(String action, IAuditLog entity, String token) { AuditLog auditRecord = new AuditLog(action, entity.getLogDeatil(), entity.getPrimaryKey(), entity.getClass() .toString()); // 日志单线程 AuditLogThread auditLogThread = new AuditLogThread(auditRecord, token); CacheProjectInfo.getInstance().getAuditLogPoolExecutor().execute(auditLogThread); }}// 创建记录日志的线程public class AuditLogThread implements Runnable { private AuditLog auditLog; private String token; public AuditLogThread(AuditLog auditLog, String token) { this.auditLog = auditLog; this.token = token; } public void run() { TokenManager.getCurrHashMap().put(Thread.currentThread(), token); // 保存日志到数据库 LogService logService = (LogService) CacheProjectInfo.getInstance().getApplicationContext() .getBean("logServiceImpl"); logService.addAuditLog(auditLog); }}// 程序启动时创建线程池public class ServletOnStart extends HttpServlet { private static final Logger LOGGER = LoggerFactory.getLogger(ServletOnStart.class); private static final long serialVersionUID = 5494158583746262904L; /** * 通过web方式,系统初始化,启动相应的服务 */ @Override public void init(ServletConfig servletConfig) throws ServletException { // 加载顺序不能改变 CacheProjectInfo projectInfo = CacheProjectInfo.getInstance(); // SPRING 信息保存 WebApplicationContext applicationContext = WebApplicationContextUtils .getWebApplicationContext(servletConfig.getServletContext()); projectInfo.setApplicationContext(applicationContext);// 设置SPRING属性 // 日志线程池 ThreadPoolExecutor auditLogPoolExecutor = new ThreadPoolExecutor(1, 1, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10000), new ThreadPoolExecutor.DiscardPolicy());// 不能执行的任务将被删除 projectInfo.setAuditLogPoolExecutor(auditLogPoolExecutor);// 加入线程池 }}6、创建 Hibernate interceptor 拦截器,这是重点,这里拦截所有需要记录日志的类,并处理
@SuppressWarnings({"rawtypes", "unchecked"})public class AuditLogInterceptor extends EmptyInterceptor { @Resource protected LoginCacheService loginCache; private static final long serialVersionUID = 2723788204258441665L; Session session; private Set inserts = new HashSet(); private Set updates = new HashSet(); private Set deletes = new HashSet(); public void setSession(Session session) { this.session = session; } @Override public String onPrepareStatement(String sql) { return super.onPrepareStatement(sql); } public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) throws CallbackException { if (entity instanceof IAuditLog) { inserts.add(entity); } String token = TokenManager.getCurrHashMap().get(Thread.currentThread()); // 无需token的操作-如果token为空则设置默认值 if (StringUtils.isEmpty(token)) { for (int i = 0; i < propertyNames.length; i++) { if ("createInsId".equals(propertyNames[i]) && null == state[i]) { state[i] = "C000000"; } if ("createUserId".equals(propertyNames[i]) && null == state[i]) { state[i] = "0"; } if ("updateUserId".equals(propertyNames[i]) && null == state[i]) { state[i] = "0"; } if ("flag".equals(propertyNames[i]) && null == state[i]) { state[i] = 1; } } return true; } LoginUser loginVo = (LoginUser) loginCache.getLogin(token); // 超时或token已不存在 if (loginVo == null) { for (int i = 0; i < propertyNames.length; i++) { if ("createInsId".equals(propertyNames[i]) && null == state[i]) { state[i] = "C000000"; } if ("createUserId".equals(propertyNames[i]) && null == state[i]) { state[i] = "0"; } if ("updateUserId".equals(propertyNames[i]) && null == state[i]) { state[i] = "0"; } if ("flag".equals(propertyNames[i]) && null == state[i]) { state[i] = 1; } } return true; } else { // 后台用户 for (int i = 0; i < propertyNames.length; i++) { if ("createInsId".equals(propertyNames[i]) && null == state[i]) { state[i] = loginVo.getUserVo().getCreateInsId(); } if ("createUserId".equals(propertyNames[i]) && null == state[i]) { state[i] = loginVo.getUserVo().getUserId(); } if ("updateUserId".equals(propertyNames[i]) && null == state[i]) { state[i] = loginVo.getUserVo().getUserId(); } if ("flag".equals(propertyNames[i]) && null == state[i]) { state[i] = 1; } } return true; } } public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) throws CallbackException { if (entity instanceof IAuditLog) { updates.add(entity); } return false; } public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { if (entity instanceof IAuditLog) { deletes.add(entity); } } // called before commit into database public void preFlush(Iterator iterator) { } // called after committed into database public void postFlush(Iterator iterator) { try { String token = TokenManager.getCurrHashMap().get(Thread.currentThread()); for (Iterator it = inserts.iterator(); it.hasNext();) { IAuditLog entity = (IAuditLog) it.next(); AuditLogUtil .LogAuditIt("Saved", entity, token); } for (Iterator it = updates.iterator(); it.hasNext();) { IAuditLog entity = (IAuditLog) it.next(); AuditLogUtil .LogAuditIt("Updated", entity, token); } for (Iterator it = deletes.iterator(); it.hasNext();) { IAuditLog entity = (IAuditLog) it.next(); AuditLogUtil .LogAuditIt("Deleted", entity, token); } } finally { inserts.clear(); updates.clear(); deletes.clear(); } }}这里面有几个比较常用的方法:
onSave – 保存数据的时候调用,数据还没有保存到数据库.
onFlushDirty – 更新数据时调用,但数据还没有更新到数据库
onDelete – 删除时调用.
preFlush – 保存,删除,更新 在提交之前调用 (通常在 postFlush 之前).
postFlush – 提交之后调用(commit之后)
注意:如果是在SPRING 容器中使用,应该将这个interceptor 注入进去
<bean id="coreInstitutionInterceptor" class="com.mnt.database.interceptor.AuditLogInterceptor" /><!-- core-db配置 --><bean id="coreSessionFactory"class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"><property name="configLocation" value="classpath:hibernate-core-mysql.cfg.xml" /><property name="entityInterceptor" ref="coreInstitutionInterceptor" /></bean>这样就能实现对整个项目中需要记录日志的实体类进行拦截,并记录增删改的日志记录. 还是很方便的,重点就是 Hibernate interceptor 的使用.
测试在是在 Hibernate 4.3 下测试的, 如果是hibernate 3 在openSession的时候是不同的
// hibernate 4session = HibernateUtil.getSessionFactory().withOptions().interceptor(interceptor).openSession(); // Hibernate 3session = HibernateUtil.getSessionFactory().openSession(interceptor);
0 0
- Hibernate4 拦截器(Interceptor) 实现实体类增删改的日志记录
- Hibernate4 拦截器(Interceptor) 实现实体类增删改的日志记录
- Hibernate4 拦截器(Interceptor) 实现实体类增删改的日志记录
- spring mvc 使用拦截器interceptor和自定义Log类实现持久层记录日志
- struts2 spring4 hibernate4(s2sh)整合开发--简单实体的增删改查操作
- 反射实现所有实体的增删改操作(jQuery+json)
- 通过拦截器Interceptor实现Spring MVC中Controller接口访问信息的记录
- 通过拦截器Interceptor实现Spring MVC中Controller接口访问信息的记录
- 使用Map代替实体类,实现ssm框架下的增删改查
- interceptor拦截器实现aop
- 根据hibernate拦截器实现可配置日志的记录
- EF实体类的增删改查操作
- Struts2 自定义日志 拦截器 interceptor
- okhttp 日志拦截器Logging-interceptor
- struts拦截器实现记录日志
- spring boot-实现WebService(CXF实现)的拦截器(Interceptor)
- springMVC+Spring3+hibernate4整合实现增删改查demo
- springMVC+Spring3+hibernate4整合实现增删改查demo
- can 总线 intel、motorola数据填充算法
- Step 1: 盲打第五天
- cloudera 离线安装
- Android四种方法写按钮点击事件
- json字符串和字典、数组之间互转
- Hibernate4 拦截器(Interceptor) 实现实体类增删改的日志记录
- oracle中sign函数
- Java RMI详解
- 网易视频云分享:1.5亿活跃用户背后的Twitter架构
- Java - 计蒜客 - 罗马数字转换成整数
- javabean以及内省技术的详解
- Java异常:选择Checked Exception还是Unchecked Exception?
- Ubuntu 16.04安装Docker-Compose
- codeforces-3A-Shortest path of the king( 棋盘最短路径 + 贪心 )