当系统中有审计需求时,特别是需要对某些数据进行动态监控时,我们可以使用EntityentiListeners来实现,当然这是基于使用JPA而不是mybatis的情况下。

当前我们的需求场景:

1.需要监控某一个实体的数据变化(add,update,delete)

2.需要记录:id,who,when, action, entity,condition,value分别表示id,操作人,操作时间,动作(add,update,delete),实体名称,状态(before:操作前,after:操作后),值

如何做?

1.如何识别add,update,delete操作?

  entityListenners有定义的@PreUpdate:更新前,@PostUpdate:更新后,@PrePersist:保存前,@PostPersist:保存后,@PreRemove:删除前。

  我们可以在保存前获取保存的数据,记录为add新增操作数据,我们在删除前获取数据,记录为删除的数据,我们在更新前,获取当前数据为更新后的数据,另外根据id从数据库获取之前的数据作为之前的数据,此时加以对比,若存在差异则为更新。

ps:如何区分update和add?updata时实体中是存在id的,add时不存在。

2.如何获取当前操作人:entityListenner和AOP有点不一样,需要在启动类中加上下列语句:

@Bean
public MethodInvokingFactoryBean methodInvokingFactoryBean() {
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setTargetClass(SecurityContextHolder.class);
methodInvokingFactoryBean.setTargetMethod("setStrategyName");
methodInvokingFactoryBean.setArguments(new String[]{SecurityContextHolder.MODE_INHERITABLETHREADLOCAL});
return methodInvokingFactoryBean;
}

然后使用spring security工具获取当前人,类似这样(具体根据本系统具体情况定制):

public Optional<String> getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String reString=(String)((JwtAuthenticationToken)authentication).getToken().getClaims().get("preferred_username");
return Optional.of(reString);
}

代码实现:

1.添加自己的listener类,实现监控,存日志逻辑

package com.b.pos.quotation.listeners;

import java.time.Instant;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import javax.persistence.PostPersist;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import com.bmw.pos.quotation.domain.InterfaceLog;
import com.bmw.pos.quotation.domain.Quotation;
import com.bmw.pos.quotation.repository.InterfaceLogRepository;
import com.bmw.pos.quotation.security.SecurityUtils;
import com.bmw.pos.quotation.service.QuotationService;
import com.bmw.pos.quotation.util.ProfileUtil;
import com.google.common.base.Throwables; /**
* Application status Listener
*/
@Component
public class QuotationStausListener {
private static final Logger log = LoggerFactory.getLogger(QuotationStausListener.class); private static InterfaceLogRepository interfaceLogRepository; private static QuotationService quotationService; @Autowired
public synchronized void setInterfaceLogRepository(InterfaceLogRepository interfaceLogRepository) {
QuotationStausListener.interfaceLogRepository = interfaceLogRepository;
} @Autowired
public synchronized void setQuotationService(QuotationService quotationService) {
QuotationStausListener.quotationService = quotationService;
} /**
* after save success
*
* @param object
*/
@PostPersist
public void postpersist(Object object) {
} /**
* after update success
*
* @param object
*/
@PostUpdate
public void postUpdate(Object object) {
} @PreRemove
public void beforeRemove(Object object) {
log.info("@PreRemove");
Quotation quotation = (Quotation) object;
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
saveInterfaceLog(quotation.getId().toString(), quotation.toString(),"Quotation","Delete","Before");
}
});
} @PreUpdate
public void beforeUpdate(Object object) {
log.info("@PreUpdate");
try {
Quotation quotation = (Quotation) object;
log.info("@PreUpdate--------->Quotation: {}",quotation.toString());
Quotation oldData = quotationService.findOldData(quotation.getId());
log.info("oldData.get()------->oldData.get(): {}",oldData.toString());
if(oldData!=null&&(!quotation.equals(oldData))) {
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
saveInterfaceLog(quotation.getId().toString(), oldData.toString(),"Quotation","Update","Before");
saveInterfaceLog(quotation.getId().toString(), quotation.toString(),"Quotation","Update","After");
}
});
}
} catch (Exception e) {
log.info(Throwables.getStackTraceAsString(e));
} } @PrePersist
public void beforeSave(Object object) {
log.info("@PrePersist");
Quotation quotation = (Quotation) object;
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
saveInterfaceLog(quotation.getId().toString(), quotation.toString(),"Quotation","Add","");
}
});
} ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
private String getCurrentUserName() {
Optional<String> user=SecurityUtils.getCurrentUserLogin();
return user.isPresent() ? user.get() : null;
}
/**
* save log
*
* @param appNo
* @param requestData
* @param response
* @param exceptions
*/
private void saveInterfaceLog(String quotationNo, String curData,String entityName,String action,String condition) {
// save log
InterfaceLog interfaceLog = new InterfaceLog();
interfaceLog.setIntType(entityName);
interfaceLog.setReturnParam(curData);
interfaceLog.setCalledTime(Instant.now());
String channel = ProfileUtil.getChannelByProfile();
interfaceLog.setEntityType(channel);
interfaceLog.setAppGlobalId(quotationNo);
interfaceLog.setEntityType(action);
interfaceLog.setEntityCode(condition);
interfaceLog.setInputParam(getCurrentUserName());
log.info("InterfaceLog: {}",interfaceLog.toString());
QuotationStausListener.interfaceLogRepository.saveAndFlush(interfaceLog); } }

2.为实体添加监控注解:@EntityListeners({AuditingEntityListener.class,QuotationStausListener.class})

  重写equals方法,使用 eclipse自带generator生成即可

3.创建数据表

----------------------分割线------------------------------

看一下效果:

spring data JPA 使用EntityentiListeners实现数据审计功能设计的更多相关文章

  1. Spring Data JPA的Audit功能,审计数据库的变更

    我最新最全的文章都在南瓜慢说 www.pkslow.com,欢迎大家来喝茶! 1 数据库审计 数据库审计是指当数据库有记录变更时,可以记录数据库的变更时间和变更人等,这样以后出问题回溯问责也比较方便. ...

  2. 整合Spring Data JPA与Spring MVC: 分页和排序

    之前我们学习了如何使用Jpa访问关系型数据库.比较完整Spring MVC和JPA教程请见Spring Data JPA实战入门,Spring MVC实战入门. 通过Jpa大大简化了我们对数据库的开发 ...

  3. 干货|一文读懂 Spring Data Jpa!

    有很多读者留言希望松哥能好好聊聊 Spring Data Jpa!其实这个话题松哥以前零零散散的介绍过,在我的书里也有介绍过,但是在公众号中还没和大伙聊过,因此本文就和大家来仔细聊聊 Spring D ...

  4. Spring Data JPA(官方文档翻译)

    关于本书 介绍 关于这本指南 第一章 前言 第二章 新增及注意点 第三章 项目依赖 第四章 使用Spring Data Repositories 4.1 核心概念 4.2 查询方法 4.3 定义rep ...

  5. Spring Boot2 系列教程(二十三)理解 Spring Data Jpa

    有很多读者留言希望松哥能好好聊聊 Spring Data Jpa! 其实这个话题松哥以前零零散散的介绍过,在我的书里也有介绍过,但是在公众号中还没和大伙聊过,因此本文就和大家来仔细聊聊 Spring ...

  6. 整合Spring Data JPA与Spring MVC: 分页和排序pageable

    https://www.tianmaying.com/tutorial/spring-jpa-page-sort Spring Data Jpa对于分页以及排序的查询也有着完美的支持,接下来,我们来学 ...

  7. Spring MVC和Spring Data JPA之获取数据表数据放在List集合,显示在JSP页面

    涉及到很多xml配置没写:只写具体实现的所有类 1.实体类 对应数据表SYS_SBGL, 主键是SBBM,主键是自动生成的uuid 数据表内容如下(有图有真相): package com.jinhet ...

  8. springboot集成Spring Data JPA数据查询

    1.JPA介绍 JPA(Java Persistence API)是Sun官方提出的Java持久化规范.它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据.它的出现主要是为 ...

  9. 使用Spring Data JPA进行数据分页与排序

    一.导读 如果一次性加载成千上万的列表数据,在网页上显示将十分的耗时,用户体验不好.所以处理较大数据查询结果展现的时候,分页查询是必不可少的.分页查询必然伴随着一定的排序规则,否则分页数据的状态很难控 ...

随机推荐

  1. Java进阶:基于TCP通信的网络实时聊天室

    目录 开门见山 一.数据结构Map 二.保证线程安全 三.群聊核心方法 四.聊天室具体设计 0.用户登录服务器 1.查看当前上线用户 2.群聊 3.私信 4.退出当前聊天状态 5.离线 6.查看帮助 ...

  2. 一个小技巧助您减少if语句的状态判断

    作者:依乐祝 首发地址:https://www.cnblogs.com/yilezhu/p/14174990.html 在进行项目的开发的过程中, if 语句是少不了的,但我们始终要有一颗消灭 if  ...

  3. 【java学习笔记2】访问控制修饰符 public、protected、默认、private

    先写了一个User()类: package chapter01; public class User { // 私有的 private int id; // 受保护的 protected int ag ...

  4. Java基础进阶:多态与接口重点摘要,类和接口,接口特点,接口详解,多态详解,多态中的成员访问特点,多态的好处和弊端,多态的转型,多态存在的问题,附重难点,代码实现源码,课堂笔记,课后扩展及答案

    多态与接口重点摘要 接口特点: 接口用interface修饰 interface 接口名{} 类实现接口用implements表示 class 类名 implements接口名{} 接口不能实例化,可 ...

  5. matplotlib学习日记(十)-划分画布的主要函数

    (1)函数subplot()绘制网格区域中的几何形状相同的子区布局 import matplotlib.pyplot as plt import numpy as np '''函数subplot的介绍 ...

  6. python序列(十)字典

    字典是无序可变序列. 定义字典是,每个元素的键和值用冒号分隔,元素之间用逗号分隔,所有的元素放在一对大括号"{ }"中. 字典中的键可以为任意不可变数据,比如.整数.实数.复数.字 ...

  7. python初学者-计算小于100的最大素数

    for n in range(100,1,-1): for i in range(2,n): if n%i==0: break else: print(n,end=' ')

  8. mysql数据安全之利用二进制日志mysqlbinlog备份数据

    mysql数据安全之利用二进制日志mysqlbinlog备份数据 简介:如何利用二进制日志来备份数据 什么是二进制日志: 二进制日志就是记录着mysql数据库中的一些写入性操作,比如一些增删改,但是, ...

  9. 强大的动态SQL

    1 动态SQL# 那么,问题来了: 什么是动态SQL? 动态SQL有什么作用? 传统的使用JDBC的方法,相信大家在组合复杂的的SQL语句的时候,需要去拼接,稍不注意哪怕少了个空格,都会导致错误.My ...

  10. 冰河又一MySQL力作出版(文末送书)!!

    写在前面 继<海量数据处理与大数据技术实战>之后,冰河的又一力作<MySQL技术大全:开发.优化与运维实战>出版,相信这本书对任何想系统学习MySQL的小伙伴来说,都会带来实质 ...