zoukankan      html  css  js  c++  java
  • 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;
        }
    View Code

    注意:这里存在事务问题,如果直接使用resposity查询数据不对,应该这样:

    Quotation oldData = quotationService.findOldData(quotation.getId());
    findOldData方法:
    //@Transactional(propagation=Propagation.REQUIRES_NEW) --不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
        public Quotation findOldData(Long id) {
            Optional<Quotation> optional = quotationRepository.findById(id);
            if (optional.isPresent()) {
                return optional.get();
            }
            return null;
    
        }
    View Code

    然后使用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);
          }
    View Code

    代码实现:

    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);
    
        }
    
        
    
    }
    View Code

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

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

    3.创建数据表

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

    看一下效果:

  • 相关阅读:
    工具类---xlsx文件读写
    2021上半年第二次作业总结
    2021上半年第一次作业总结
    C语言II博客作业04
    C语言II—作业03
    C语言II博客作业02
    C语言II博客作业01
    win7开启snmp服务实现监控过程展现
    台湾某医学会sql注入漏洞
    测试面试题(持续总结中)
  • 原文地址:https://www.cnblogs.com/miketwais/p/EntityentiListeners.html
Copyright © 2011-2022 走看看