版权声明:本文为博主原创文章,未经博主允许不得转载 https://blog.csdn.net/qq_27948811/article/details/88918021
SpringDataJPA中有个注解@Audited
只需在Entity中加上注解@Audited,就会自动帮你记录下Entity对应的表的所有操作记录insert,update,delete,会在数据库帮你生成一张表xxx_AUD;
这是在SpringDataJPA框架才能使用的一个注解,如果使用mybatis怎么办呢?
废话不多说,下面直接上代码:
需要添加的依赖主要有下面这些:
compile('org.springframework.boot:spring-boot-starter-web')
compile('com.baomidou:mybatis-plus-boot-starter:2.2.0')
compile('org.apache.logging.log4j:log4j-1.2-api')
compile('org.springframework.boot:spring-boot-starter-aop')
compile('com.alibaba:druid:1.1.6')
compile('org.projectlombok:lombok:1.16.10')
compile('com.alibaba:fastjson:1.2.47')
compile('org.springframework.boot:spring-boot-starter-undertow')
compile('mysql:mysql-connector-java:5.1.21')
数据库用的sql server,然后新建一张表来记录其他表的操作日志:
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@TableName("SqlOperateLog")
public class SqlOperationLog implements Serializable {
private static final long serialVersionUID = 1L;
@TableField("ID")
private Long id;
@TableField("Type")
private String type;
@TableField("AfterValue")
private String afterValue;
@TableField("BeforeValue")
private String beforeValue;
@TableField("CreateTime")
private Date createTime;
@TableField("TableName")
private String tableName;
@TableField("UserID")
private Integer userId;
}
与之对应的mapper:
@Repository
@Mapper
public interface SqlOperationLogMapper extends BaseMapper<SqlOperationLog> {
}
注意: SqlOperationLog 和他的mapper不要放mapper包下了,下面aop会扫描这个包;
新建一个类实现ApplicationContextAware,方便从spring容器中获取bean
@Component
public class RecordBeanFactory implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (this.applicationContext == null) {
this.applicationContext = applicationContext;
}
}
public static Object getBean(Class<?> clazz) {
if (applicationContext == null) {
return null;
}
return applicationContext.getBean(clazz);
}
}
拦截器记录操作用户信息:
public class MyInterceptor implements HandlerInterceptor {
public static final ThreadLocal<UserInfo> USER_INFO = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 从security的context中获取登陆用户信息
UserVO vo = (UserVO) SecurityContextHolder
.getContext()
.getAuthentication()
.getPrincipal();
UserInfo userInfo = UserInfo.builder()
.userId(vo.getUserId())
.userName(vo.getNickName())
.userIp(request.getRemoteAddr())
.build();
USER_INFO.set(userInfo);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
USER_INFO.remove();
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
因为这里用了Spring security框架,可以直接从SecurityContextHolder上下文中获取登陆用户的信息;如果你用了别的框架,换别的实现也是一样的;这里不再细说
最后就是最重要的aop的代码:
@Aspect
@Component
public class SqlAspect {
private SqlOperationLog sqlOperationLog;
@Autowired
private SqlOperationLogMapper sqlOperationLogMapper;
/**
* mybatis-plus表操作都返回的是integer,表达式根究自己项目包结构来写,这里不展示我的包结构了
*/
@Pointcut("execution(Integer com.demo.demo.mapper.*.*(..))")
private void mapper() {
}
/**
* 记录表的操作日志,insert,update,delete(逻辑删除)
*
* @param joinPoint
*/
@Around("mapper()")
public Object recordOperate(ProceedingJoinPoint joinPoint) throws Exception {
// 获取被代理对象
Object target = joinPoint.getTarget();
MapperProxy invo = (MapperProxy) Proxy.getInvocationHandler(target);
Class<? extends MapperProxy> mapperProxy = invo.getClass();
Field mapperInterface1 = mapperProxy.getDeclaredField("mapperInterface");
mapperInterface1.setAccessible(true);
Class proxyClass = (Class) mapperInterface1.get(invo);
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
// 被代理方法的参数
Object[] args = joinPoint.getArgs();
sqlOperationLog = new SqlOperationLog();
sqlOperationLog.setType(method.getName());
sqlOperationLog.setUserId(MyInterceptorConfig.USER_INFO.get().getUserId());
String beforeValue = beforeOperate(args, proxyClass);
sqlOperationLog.setBeforeValue(beforeValue);
// 执行sql
Integer result = 0;
try {
result = (Integer) joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
// 执行成功
if (result > 0) {
String afterValue = afterOperate(args);
sqlOperationLog.setAfterValue(afterValue);
}
sqlOperationLogMapper.insert(sqlOperationLog);
return result;
}
/**
* 操作前的值
*
* @param args 参数
* @param proxyClass 被代理类
* @return 修改器的值, json格式
* @throws Exception
*/
private String beforeOperate(Object[] args, Class proxyClass) throws Exception {
for (Object arg : args) {
Class<?> clazz = arg.getClass();
String entityName = clazz.getName().substring(clazz.getName().lastIndexOf(".") + 1);
Object bean = RecordBeanFactory.getBean(proxyClass);
// 获取mapper的selectById方法
Method selectById = proxyClass.getMethod("selectById", Serializable.class);
Method[] methods = clazz.getDeclaredMethods();
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
// 每个entity对应表的主键ID都有@TableId注解
if (declaredField.getAnnotation(TableId.class) != null) {
System.out.println("主键:" + declaredField.getName());
// 获取主键的值,因为每个表的主键名字不一定都是id
String primaryKeyMethod = "get" + declaredField.getName();
for (Method method : methods) {
if (primaryKeyMethod.equalsIgnoreCase(method.getName())) {
// 调用getId方法
Object id = method.invoke(arg);
// 调用对应mapper的selectById
Object invoke = selectById.invoke(bean, id);
System.out.println("修改前==" + JSON.toJSONString(invoke));
return JSON.toJSONString(invoke);
}
}
}
}
}
return null;
}
/**
* 数据库操作后
*
* @param args 修改的对象
* @return 修改后的对象json字符串
*/
public String afterOperate(Object[] args) throws Exception {
if (args == null) {
return null;
}
// 遍历参数对象
for (Object arg : args) {
// 获取对象类型
Class<?> clazz = arg.getClass();
TableName annotation = clazz.getAnnotation(TableName.class);
if (annotation == null) {
return null;
}
String tableName = annotation.value();
sqlOperationLog.setTableName(tableName);
sqlOperationLog.setCreateTime(new Date());
return JSON.toJSONString(arg);
}
return null;
}
}
以上就是全部的相关代码,如有问题,欢迎交流