spring data JPA 使用EntityentiListeners实现数据审计功能设计
当系统中有审计需求时,特别是需要对某些数据进行动态监控时,我们可以使用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实现数据审计功能设计的更多相关文章
- Spring Data JPA的Audit功能,审计数据库的变更
我最新最全的文章都在南瓜慢说 www.pkslow.com,欢迎大家来喝茶! 1 数据库审计 数据库审计是指当数据库有记录变更时,可以记录数据库的变更时间和变更人等,这样以后出问题回溯问责也比较方便. ...
- 整合Spring Data JPA与Spring MVC: 分页和排序
之前我们学习了如何使用Jpa访问关系型数据库.比较完整Spring MVC和JPA教程请见Spring Data JPA实战入门,Spring MVC实战入门. 通过Jpa大大简化了我们对数据库的开发 ...
- 干货|一文读懂 Spring Data Jpa!
有很多读者留言希望松哥能好好聊聊 Spring Data Jpa!其实这个话题松哥以前零零散散的介绍过,在我的书里也有介绍过,但是在公众号中还没和大伙聊过,因此本文就和大家来仔细聊聊 Spring D ...
- Spring Data JPA(官方文档翻译)
关于本书 介绍 关于这本指南 第一章 前言 第二章 新增及注意点 第三章 项目依赖 第四章 使用Spring Data Repositories 4.1 核心概念 4.2 查询方法 4.3 定义rep ...
- Spring Boot2 系列教程(二十三)理解 Spring Data Jpa
有很多读者留言希望松哥能好好聊聊 Spring Data Jpa! 其实这个话题松哥以前零零散散的介绍过,在我的书里也有介绍过,但是在公众号中还没和大伙聊过,因此本文就和大家来仔细聊聊 Spring ...
- 整合Spring Data JPA与Spring MVC: 分页和排序pageable
https://www.tianmaying.com/tutorial/spring-jpa-page-sort Spring Data Jpa对于分页以及排序的查询也有着完美的支持,接下来,我们来学 ...
- Spring MVC和Spring Data JPA之获取数据表数据放在List集合,显示在JSP页面
涉及到很多xml配置没写:只写具体实现的所有类 1.实体类 对应数据表SYS_SBGL, 主键是SBBM,主键是自动生成的uuid 数据表内容如下(有图有真相): package com.jinhet ...
- springboot集成Spring Data JPA数据查询
1.JPA介绍 JPA(Java Persistence API)是Sun官方提出的Java持久化规范.它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据.它的出现主要是为 ...
- 使用Spring Data JPA进行数据分页与排序
一.导读 如果一次性加载成千上万的列表数据,在网页上显示将十分的耗时,用户体验不好.所以处理较大数据查询结果展现的时候,分页查询是必不可少的.分页查询必然伴随着一定的排序规则,否则分页数据的状态很难控 ...
随机推荐
- 推荐一款最强Python自动化神器!再也不用写代码了!
本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理 搞过自动化测试的小伙伴,相信都知道,在Web自动化测试中,有一款自动化测试神器工具: seleniu ...
- Docker Container 就是一个进程,多新鲜啊?
大家对 Docker 都应该有了或多或少的认识了,相信大家都是从这两张图来粗旷的理解 Docker 及容器概念的 那我们如何更轻松的理解容器 Container 呢?说白了 Container 就是一 ...
- js中Tabs插件打开的标签页过多自动关闭
js方法 function addTab(ResourceID, ResourceName, Url) { if (Url != "" && Url != null ...
- SQLServer访问WebServices提示:SQL Server 阻止了对组件 'Ole Automation Procedures' 的 过程'sys.sp_OACreate' 的访问
问题描述 在数据库中调用webservices, 提示:SQLServer访问WebServices提示:SQL Server 阻止了对组件 'Ole Automation Procedures' 的 ...
- H3C路由器配置——静态路由
一.网络畅通条件及排错思路 1.网络畅通的条件 网络畅通的条件:数据包能去能回,也是我们排除网络故障的理论依据. 2.网络不畅通示列 ①.目标主机不可达 原因分析:可能是数据包没有到达目的地,在中途就 ...
- [EF] - Code First处理Clustered Index
Clustered Index <=>集群索引: http://msdn.microsoft.com/en-us/library/ms177443.aspx 由于其特殊性,使得每个tabl ...
- 使用OpenOffice实现文档预览
概述 使用OpenOffice将 office文档转为pdf,然后再将pdf转为图片,实现文档预览的功能. 依赖组件 OpenOffice.org或者LibreOffice JODConverter ...
- 找到数组中最小的k个数
/*输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个数字, 则最小的4个数字是1.2.3.4. 示例 1: 输入:arr = [3,2,1], k = ...
- Omega System Trading and Development Club内部分享策略Easylanguage源码
更多精彩内容,欢迎关注公众号:数量技术宅.关于本期分享的任何问题,请加技术宅微信:sljsz01 关于 Omega System Trading and Development Club " ...
- Oracel 修改字段类型(有数据的情况)
1 /*修改原字段名bh为bh_tmp*/ 2 alter table Tab_Name rename column bh to bh_tmp; 3 /*增加一个和原字段名同名的字段bh*/ 4 al ...