Mybatis interceptor

package com.pab.bloan.capv.server.common.changemonitor.enumerate;


public enum DBActionTypeEnum {
    
    UPDATE("UPDATE","更新"),
    INSERT("INSERT","新增"),
    DELETE("DELETE","删除");
    
    private String key;
    private String value;
    
    private DBActionTypeEnum(String key, String value) {
        this.key = key;
        this.value = value;
    }
    
    public String getKey() {
        return key;
    }

    public String getValue() {
        return value;
    }
    
}
package com.pab.bloan.capv.server.common.changemonitor.interceptor;

import com.alibaba.druid.pool.DruidPooledPreparedStatement;
import com.alibaba.druid.proxy.jdbc.JdbcParameter;
import com.alibaba.druid.proxy.jdbc.PreparedStatementProxy;
import com.alibaba.druid.sql.SQLUtils;
import com.pab.bloan.capv.server.common.changemonitor.listener.DBObjectChangeEventListener;
import com.pab.bloan.capv.server.common.changemonitor.model.ChangeMonitorBo;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.logging.jdbc.PreparedStatementLogger;
import org.apache.ibatis.plugin.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.*;

@Slf4j
@Component
@Intercepts({
    @Signature(type = StatementHandler.class, method = "update", args = {Statement.class})
})
public class ChangeMinitorInterceptor implements Interceptor {

    @Autowired
    private DBObjectChangeEventListener changeEventListener;
    @Value("${capv.minitor.plugin.enable:true}")
    private Boolean changeMinitorSwitch;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object proceedResult = null;

        // 解析拦截到的sql 并进行更新前处理
        ChangeMonitorBo changeMonitorBo = null;
        try {
            String realSql = parseRealSql((Statement) invocation.getArgs()[0]);
            log.info("解析后的sql为: {}", realSql);
            changeMonitorBo = new ChangeMonitorBo(realSql);

            // 更新前处理
            changeEventListener.beforeChange(changeMonitorBo);
        }catch (Throwable err){
            log.error(err.getMessage());
        }

        // 执行拦截的方法
        proceedResult = invocation.proceed();

        // 更新后处理
        try {
            if(changeMonitorBo != null) {
                changeEventListener.afterChange(changeMonitorBo);
            }
        }catch (Throwable err){
            log.error(err.getMessage());
        }

        // 返回执行结果
        return proceedResult;
    }

    @Override
    public Object plugin(Object target) {
        if(changeMinitorSwitch) {
            if (target instanceof StatementHandler) {
                return Plugin.wrap(target, this);
            }
        }
        return target;
    }

    @Override
    public void setProperties(Properties arg0) {
    }

    private Object getField(Object obj, String fieldName) {
        Field field = ReflectionUtils.findField(obj.getClass(),fieldName);
        ReflectionUtils.makeAccessible(field);
        return ReflectionUtils.getField(field,obj);
    }

    public String parseRealSql(Statement statement) {
        DruidPooledPreparedStatement druidPooledPreparedStatement = null;
        if(statement instanceof DruidPooledPreparedStatement) {
            druidPooledPreparedStatement = (DruidPooledPreparedStatement) statement;
        }else{
            PreparedStatementLogger preparedStatementLogger = (PreparedStatementLogger) getField(statement, "h");
            druidPooledPreparedStatement = (DruidPooledPreparedStatement) preparedStatementLogger.getPreparedStatement();
        }
        PreparedStatementProxy preparedStatementProxy = (PreparedStatementProxy)druidPooledPreparedStatement.getStatement();

        int parametersSize = preparedStatementProxy.getParametersSize();
        List<Object> parameters = new ArrayList(parametersSize);

        for(int i = 0; i < parametersSize; ++i) {
            JdbcParameter jdbcParam = preparedStatementProxy.getParameter(i);
            parameters.add(jdbcParam != null ? jdbcParam.getValue() : null);
        }

        String dbType = preparedStatementProxy.getConnectionProxy().getDirectDataSource().getDbType();
        return SQLUtils.format(preparedStatementProxy.getSql(), dbType, parameters);
    }

}
package com.pab.bloan.capv.server.common.changemonitor.listener;

import com.pab.bloan.capv.server.common.changemonitor.model.ChangeMonitorBo;
import net.sf.jsqlparser.JSQLParserException;

public interface DBObjectChangeEventListener {
    void beforeChange(ChangeMonitorBo changeMonitorBo) throws JSQLParserException;
    void afterChange(ChangeMonitorBo changeMonitorBo);
}
package com.pab.bloan.capv.server.common.changemonitor.listener;

import com.alibaba.fastjson.JSON;
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfig;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.pab.bloan.capv.server.common.changemonitor.enumerate.DBActionTypeEnum;
import com.pab.bloan.capv.server.common.changemonitor.model.ChangeMonitorBo;
import com.pab.bloan.capv.server.common.util.LoginUtils;
import com.pab.bloan.capv.server.model.pojo.rcpmdata.DataObjectLogPojo;
import com.pab.bloan.capv.server.model.pojo.rcpmlog.ChangeObjectDetailPojo;
import com.pab.bloan.capv.server.model.pojo.rcpmlog.ChangeObjectPojo;
import com.pab.framework.google.common.collect.Lists;
import com.pab.halo.component.idgenerator.KeyGenerator;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.*;

@Slf4j
@Component
public class DefaultDBObjectChangeEventListenerImpl implements DBObjectChangeEventListener {
    private static final String MONITOR_CHANGE_STORE_TABLE = "CHANGE_OBJECT";
    private static final String MONITOR_CHANGE_STORE_TABLE_DETAIL = "CHANGE_OBJECT_DETAIL";
    private Set<String> needMonitorTables = Sets.newHashSet();

    @Value("${capv.monitor.need-monitor-tables:}")
    private String monitorTables;
    @Autowired
    @Qualifier("defaultJdbcTemplate")
    private JdbcTemplate jdbcTemplate;
    @Autowired
    @Qualifier("logJdbcTemplate")
    private JdbcTemplate logJdbcTemplate;
    @Autowired
    private KeyGenerator keyGenerator;
//    @Autowired
//    private ChangeObjectAndDetailService changeObjectAndDetailService; //这里不能间接注入mybatis相关的Mapper对象,执行openSession直接报错
//    @Autowired
//    private SqlSessionFactory sqlSessionFactory; //这里不能注入mybatis相关的对象,执行openSession直接报错

    @ApolloConfig
    private Config config;

    @ApolloConfigChangeListener
    private void configChangeListter(ConfigChangeEvent changeEvent) {
        Set<String> keyNames = config.getPropertyNames();
        for (String key : keyNames) {
            if (key.equals("capv.monitor.need-monitor-tables")) {
                String strMonitorTables = config.getProperty(key, "");
                this.monitorTables = strMonitorTables;
                log.info("{}:{}", key, strMonitorTables);
                initNeedMonitorTables();
                break;
            }
        }
    }

    @PostConstruct
    public void initNeedMonitorTables() {
        String[] tables = monitorTables.toUpperCase().split(",");
        log.info("当前拦截的表清单: {}", JSON.toJSONString(this.needMonitorTables));
        synchronized (needMonitorTables) {
            needMonitorTables.clear();
            needMonitorTables.addAll(Arrays.asList(tables));
        }
        log.info("刷新后拦截的表清单: {}", JSON.toJSONString(this.needMonitorTables));
    }

    public void fillSql(ChangeMonitorBo changeMonitorBo) throws JSQLParserException {
        Statement statement = CCJSqlParserUtil.parse(changeMonitorBo.getMonitorSql());
        changeMonitorBo.setStatement(statement);
        //初始化表信息
        prepareTable(changeMonitorBo);
        if(!changeMonitorBo.isNeedMonitor()){
            return;
        }
        // 初始化主键信息
        fetchTablePkColumns(changeMonitorBo);
        // 初始化字段注释
        fetchTableColumnComments(changeMonitorBo);
        // 拼装查询sql
        prepareSelectSql(changeMonitorBo);
    }

    @Override
    public void beforeChange(ChangeMonitorBo changeMonitorBo) throws JSQLParserException {
        fillSql(changeMonitorBo);
        if(!changeMonitorBo.isNeedMonitor()
                || null == changeMonitorBo.getSelectSql() ){
            return;
        }
        if(changeMonitorBo.getChageType().equals(DBActionTypeEnum.UPDATE)) {
            Date curDateTime = new Date();
            List<Map<String, Object>> lastData = jdbcTemplate.queryForList(changeMonitorBo.getSelectSql());

            //保存修改前数据串
            changeMonitorBo.setBeforeJsonString(JSON.toJSONString(lastData));

            for(Map<String, Object> map : lastData){
                ChangeObjectPojo changeObject = new ChangeObjectPojo();
//                changeObject.setPkColumnName(StringUtils.join(changeMonitorBo.getPkColumns().toArray(), ","));
                changeObject.setUserUm(getLoginUserUm());
                changeObject.setTableName(changeMonitorBo.getTable().getName());
                changeObject.setCreateTime(curDateTime);
                changeObject.setChangeType(changeMonitorBo.getChageType().getValue());
                changeObject.setSerialNo(String.valueOf(keyGenerator.generateKey()));

                StringBuilder sbPkComments = new StringBuilder();
                changeMonitorBo.getPkColumns().forEach(pkColumn->{
                    sbPkComments.append(changeMonitorBo.getColumnCommentsMap().get(pkColumn));
                });
                changeObject.setPkColumnComments(sbPkComments.toString());

                log.info("threadNo={},oldValue={}",Thread.currentThread().getId(), map.toString());
                Map<String,Object> pkValueMap = Maps.newHashMap();
                map.forEach((key, value) -> {
                    ChangeObjectDetailPojo detail = new ChangeObjectDetailPojo();
                    detail.setSerialNo(String.valueOf(keyGenerator.generateKey()));
                    detail.setObjSerialNo(changeObject.getSerialNo());
                    detail.setColumnName(key);
                    detail.setColumnComments(changeMonitorBo.getColumnCommentsMap().get(key));
                    detail.setLastValue(value==null?null:String.valueOf(value));
                    detail.setCreateTime(curDateTime);
                    changeMonitorBo.getChangeObjectDetailPojoList().add(detail);

                    // 判断当前处理字段是否主键字段
                    if(changeMonitorBo.getPkColumns().contains(key)){
                        pkValueMap.put(key,value);
                    }
                });
                //解决主键顺序不一致问题
                StringBuilder sbPkColumn = new StringBuilder();
                StringBuilder sbPkValue = new StringBuilder();
                pkValueMap.forEach((key,value)->{
                    if(StringUtils.isBlank(sbPkColumn.toString())) {
                        sbPkColumn.append(key);
                        sbPkValue.append(value);
                    }else{
                        sbPkColumn.append(",").append(key);
                        sbPkValue.append(",").append(value);
                    }
                });
                changeObject.setPkColumnName(sbPkColumn.toString());
                changeObject.setPkValue(sbPkValue.toString());
                changeMonitorBo.getChangeObjectPojoList().add(changeObject);
            }
        }else if(changeMonitorBo.getChageType().equals(DBActionTypeEnum.INSERT)) {

        }else if(changeMonitorBo.getChageType().equals(DBActionTypeEnum.DELETE)) {
            Date curDateTime = new Date();
            List<Map<String, Object>> lastData = jdbcTemplate.queryForList(changeMonitorBo.getSelectSql());
            for(Map<String, Object> map : lastData){
                ChangeObjectPojo changeObject = new ChangeObjectPojo();
//                changeObject.setPkColumnName(StringUtils.join(changeMonitorBo.getPkColumns().toArray(), ","));
                changeObject.setUserUm(getLoginUserUm());
                changeObject.setTableName(changeMonitorBo.getTable().getName());
                changeObject.setCreateTime(curDateTime);
                changeObject.setChangeType(changeMonitorBo.getChageType().getValue());
                changeObject.setSerialNo(String.valueOf(keyGenerator.generateKey()));

                StringBuilder sbPkComments = new StringBuilder();
                changeMonitorBo.getPkColumns().forEach(pkColumn->{
                    sbPkComments.append(changeMonitorBo.getColumnCommentsMap().get(pkColumn));
                });
                changeObject.setPkColumnComments(sbPkComments.toString());

                log.info("threadNo={},oldValue={}",Thread.currentThread().getId(), map.toString());
                Map<String,Object> pkValueMap = Maps.newHashMap();
                map.forEach((key, value) -> {
                    ChangeObjectDetailPojo detail = new ChangeObjectDetailPojo();
                    detail.setSerialNo(String.valueOf(keyGenerator.generateKey()));
                    detail.setObjSerialNo(changeObject.getSerialNo());
                    detail.setColumnName(key);
                    detail.setColumnComments(changeMonitorBo.getColumnCommentsMap().get(key));
                    detail.setLastValue(value==null?null:String.valueOf(value));
                    detail.setCurValue(null);
                    detail.setCreateTime(curDateTime);
                    changeMonitorBo.getChangeObjectDetailPojoList().add(detail);

                    // 判断当前处理字段是否主键字段
                    if(changeMonitorBo.getPkColumns().contains(key)){
                        pkValueMap.put(key,value);
                    }
                });
                //解决主键顺序不一致问题
                StringBuilder sbPkColumn = new StringBuilder();
                StringBuilder sbPkValue = new StringBuilder();
                pkValueMap.forEach((key,value)->{
                    if(StringUtils.isBlank(sbPkColumn.toString())) {
                        sbPkColumn.append(key);
                        sbPkValue.append(value);
                    }else{
                        sbPkColumn.append(",").append(key);
                        sbPkValue.append(",").append(value);
                    }
                });
                changeObject.setPkColumnName(sbPkColumn.toString());
                changeObject.setPkValue(sbPkValue.toString());
                changeMonitorBo.getChangeObjectPojoList().add(changeObject);
            }
        }
    }

    @Override
    public void afterChange(ChangeMonitorBo changeMonitorBo) {
        if(!changeMonitorBo.isNeedMonitor()
                || null == changeMonitorBo.getSelectSql()){
            return;
        }
        if(changeMonitorBo.getChageType().equals(DBActionTypeEnum.UPDATE)) {
            List<Map<String, Object>> curData = jdbcTemplate.queryForList(changeMonitorBo.getSelectSql());

            //变更前后数据完全相等
            if(changeMonitorBo.getBeforeJsonString().equals(JSON.toJSONString(curData))){
                return;
            }

            for(Map<String, Object> map : curData){
                for(ChangeObjectPojo changeObject : changeMonitorBo.getChangeObjectPojoList()) {
                    String strPkColumnName = changeObject.getPkColumnName();
                    StringBuilder sbPkValue = new StringBuilder();
                    //可能是联合索引
                    if(StringUtils.isNotBlank(strPkColumnName)){
                        String[] arrPkColumnName = strPkColumnName.split(",");
                        Arrays.asList(arrPkColumnName).forEach(column->{
                            if(StringUtils.isBlank(sbPkValue.toString())) {
                                sbPkValue.append(map.get(column));
                            }else{
                                sbPkValue.append(",").append(map.get(column));
                            }
                        });
                    }
                    if(null != changeObject.getPkValue() && changeObject.getPkValue().equals(sbPkValue.toString())) {
                        log.info("threadNo={},newValue={}", Thread.currentThread().getId(), map.toString());
                        changeMonitorBo.getChangeObjectDetailPojoList().forEach(detail -> {
                            if(detail.getObjSerialNo().equals(changeObject.getSerialNo())){
                                Object curValue = map.get(detail.getColumnName());
                                detail.setCurValue(curValue==null?null:String.valueOf(curValue));
                            }
                        });
                    }
                }
            }
        }else if(changeMonitorBo.getChageType().equals(DBActionTypeEnum.INSERT)) {
            Date curDateTime = new Date();
            List<Map<String, Object>> curData = jdbcTemplate.queryForList(changeMonitorBo.getSelectSql());
            for(Map<String, Object> map : curData){
                ChangeObjectPojo changeObject = new ChangeObjectPojo();
//                changeObject.setPkColumnName(StringUtils.join(changeMonitorBo.getPkColumns().toArray(), ","));
                changeObject.setUserUm(getLoginUserUm());
                changeObject.setTableName(changeMonitorBo.getTable().getName());
                changeObject.setCreateTime(curDateTime);
                changeObject.setChangeType(changeMonitorBo.getChageType().getValue());
                changeObject.setSerialNo(String.valueOf(keyGenerator.generateKey()));

                StringBuilder sbPkComments = new StringBuilder();
                changeMonitorBo.getPkColumns().forEach(pkColumn->{
                    sbPkComments.append(changeMonitorBo.getColumnCommentsMap().get(pkColumn));
                });
                changeObject.setPkColumnComments(sbPkComments.toString());

                log.info("threadNo={},oldValue={}",Thread.currentThread().getId(), map.toString());
                Map<String,Object> pkValueMap = Maps.newHashMap();
                map.forEach((key, value) -> {
                    ChangeObjectDetailPojo detail = new ChangeObjectDetailPojo();
                    detail.setSerialNo(String.valueOf(keyGenerator.generateKey()));
                    detail.setObjSerialNo(changeObject.getSerialNo());
                    detail.setColumnName(key);
                    detail.setColumnComments(changeMonitorBo.getColumnCommentsMap().get(key));
                    detail.setLastValue(null);
                    detail.setCurValue(value==null?null:String.valueOf(value));
                    detail.setCreateTime(curDateTime);
                    changeMonitorBo.getChangeObjectDetailPojoList().add(detail);

                    // 判断当前处理字段是否主键字段
                    if(changeMonitorBo.getPkColumns().contains(key)){
                        pkValueMap.put(key,value);
                    }
                });
                //解决主键顺序不一致问题
                StringBuilder sbPkColumn = new StringBuilder();
                StringBuilder sbPkValue = new StringBuilder();
                pkValueMap.forEach((key,value)->{
                    if(StringUtils.isBlank(sbPkColumn.toString())) {
                        sbPkColumn.append(key);
                        sbPkValue.append(value);
                    }else{
                        sbPkColumn.append(",").append(key);
                        sbPkValue.append(",").append(value);
                    }
                });
                changeObject.setPkColumnName(sbPkColumn.toString());
                changeObject.setPkValue(sbPkValue.toString());
                changeMonitorBo.getChangeObjectPojoList().add(changeObject);
            }
        }else if(changeMonitorBo.getChageType().equals(DBActionTypeEnum.DELETE)) {

        }

        log.info("threadNo={},changeObject={}", Thread.currentThread().getId(), changeMonitorBo);

        // 保存数据
        saveChangeObject(changeMonitorBo.getChangeObjectPojoList());
        saveChangeObjectDetail(changeMonitorBo.getChangeObjectDetailPojoList());

        // 兼容老个贷 DATAOBJECT_LOG 表保存数据
        saveDataobjectLog(changeMonitorBo);

    }

    private String getLoginUserUm() {
        try{
            return LoginUtils.getLoginUserUM();
        }catch (RuntimeException e){
        }
        return null;
    }
    private String getLoginUserBelongOrgId() {
        try{
            return LoginUtils.getLoginUserBelongOrgId();
        }catch (RuntimeException e){
        }
        return null;
    }

    private void saveDataobjectLog(ChangeMonitorBo changeMonitorBo) {
        List<DataObjectLogPojo> list = Lists.newArrayList();
        changeMonitorBo.getChangeObjectPojoList().forEach(item->{
            DataObjectLogPojo pojo = new DataObjectLogPojo();
            pojo.setOpTime(DateFormatUtils.format(item.getCreateTime(),"yyyy/MM/dd HH:mm:ss:SSS"));
            pojo.setOpTable(item.getTableName());
            pojo.setOpKeys(item.getPkColumnName());
            pojo.setOpValues(item.getPkValue());
            pojo.setOpActions(item.getChangeType());
            pojo.setOrgId(getLoginUserBelongOrgId());
            pojo.setUserId(item.getUserUm());

            StringBuilder sb = new StringBuilder();
            changeMonitorBo.getChangeObjectDetailPojoList().forEach(detail->{
                if(detail.getObjSerialNo().equals(item.getSerialNo())){
                    if( ( StringUtils.isBlank(detail.getLastValue()) && StringUtils.isBlank(detail.getCurValue()) )
                        || (detail.getLastValue() != null && detail.getLastValue().equals(detail.getCurValue()) )
                            ) {

                    }else {
                        sb.append(detail.getColumnComments()).append("(").append(detail.getColumnName()).append(")")
                                .append(":").append("原值=").append(detail.getLastValue())
                                .append(",").append("新值=").append(detail.getCurValue()).append("。\n");
                    }
                }
            });
            String strAttr1 = sb.toString();
            if(strAttr1.length()>2000){
                strAttr1 = strAttr1.substring(0, 2000);
            }
            pojo.setAttribute1(strAttr1);

            if(StringUtils.isNotBlank(strAttr1)) {
                list.add(pojo);
            }
        });
        if(CollectionUtils.isEmpty(list)){
            return;
        }

        StringBuilder sb = new StringBuilder();
        sb.append("insert into DATAOBJECT_LOG (OPTIME, OPTABLE, OPKEYS, OPVALUES, OPACTIONS, ORGID, ORGNAME,")
                .append(" USERID, USERNAME, ATTRIBUTE1)")
                .append(" values (?,?,?,?, ?,?,?,?, ?,?)");

        final List<DataObjectLogPojo> tmpList = list;
        jdbcTemplate.batchUpdate(sb.toString(), new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                ps.setString(1, tmpList.get(i).getOpTime());
                ps.setString(2, tmpList.get(i).getOpTable());
                ps.setString(3, tmpList.get(i).getOpKeys());
                ps.setString(4, tmpList.get(i).getOpValues());
                ps.setString(5, tmpList.get(i).getOpActions());
                ps.setString(6, tmpList.get(i).getOrgId());
                ps.setString(7, tmpList.get(i).getOrgName());
                ps.setString(8, tmpList.get(i).getUserId());
                ps.setString(9, tmpList.get(i).getUserName());
                ps.setString(10, tmpList.get(i).getAttribute1());
            }

            @Override
            public int getBatchSize() {
                return tmpList.size();
            }
        });
    }

    private void saveChangeObject(List<ChangeObjectPojo> list) {
        if(CollectionUtils.isEmpty(list)){
            return;
        }

        StringBuilder sb = new StringBuilder();
        sb.append("insert into CHANGE_OBJECT (SERIALNO, TABLENAME, PKCOLUMNNAME,")
                .append(" PKCOLUMNCOMMENTS, PKVALUE, CHANGETYPE, CREATETIME, USERUM)")
                .append(" values (?,?,?,?,?,?,?,?)");

        final List<ChangeObjectPojo> tmpList = list;
        logJdbcTemplate.batchUpdate(sb.toString(), new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                ps.setString(1, tmpList.get(i).getSerialNo());
                ps.setString(2, tmpList.get(i).getTableName());
                ps.setString(3, tmpList.get(i).getPkColumnName());
                ps.setString(4, tmpList.get(i).getPkColumnComments());
                ps.setString(5, tmpList.get(i).getPkValue());
                ps.setString(6, tmpList.get(i).getChangeType());
                ps.setTimestamp(7, new java.sql.Timestamp(tmpList.get(i).getCreateTime().getTime()));
                ps.setString(8, tmpList.get(i).getUserUm());
            }

            @Override
            public int getBatchSize() {
                return tmpList.size();
            }
        });

    }

    private void saveChangeObjectDetail(List<ChangeObjectDetailPojo> list) {
        if(CollectionUtils.isEmpty(list)){
            return;
        }

        StringBuilder sb = new StringBuilder();
        sb.append("insert into CHANGE_OBJECT_DETAIL (SERIALNO, OBJSERIALNO, COLUMNNAME, COLUMNCOMMENTS, LASTVALUE, CURVALUE, CREATETIME)")
                .append(" values (?,?,?,?,?,?,?)");

        final List<ChangeObjectDetailPojo> tmpList = list;
        logJdbcTemplate.batchUpdate(sb.toString(), new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                ps.setString(1, tmpList.get(i).getSerialNo());
                ps.setString(2, tmpList.get(i).getObjSerialNo());
                ps.setString(3, tmpList.get(i).getColumnName());
                ps.setString(4, tmpList.get(i).getColumnComments());
                ps.setString(5, tmpList.get(i).getLastValue());
                ps.setString(6, tmpList.get(i).getCurValue());
                ps.setTimestamp(7, new java.sql.Timestamp(tmpList.get(i).getCreateTime().getTime()));
            }

            @Override
            public int getBatchSize() {
                return tmpList.size();
            }
        });
    }

    /**
     * 根据本次更新的sql拼装查询本次更新前的数据的sql
     * @param
     * @param changeMonitorBo
     * @return
     * @throws JSQLParserException
     */
    private void prepareSelectSql(ChangeMonitorBo changeMonitorBo) {
        if(changeMonitorBo.getStatement() instanceof Update) {
            prepareSelectSqlFromUpdateSql((Update) changeMonitorBo.getStatement(), changeMonitorBo);
        }else if(changeMonitorBo.getStatement() instanceof Insert){
            prepareSelectSqlFromInsertSql((Insert) changeMonitorBo.getStatement(), changeMonitorBo);
        }else if(changeMonitorBo.getStatement() instanceof Delete) {
            prepareSelectSqlFromDeleteSql((Delete) changeMonitorBo.getStatement(), changeMonitorBo);
        }
    }

    private void prepareTable(ChangeMonitorBo changeMonitorBo) {
        if(changeMonitorBo.getStatement() instanceof Update) {
            changeMonitorBo.setTable(((Update)changeMonitorBo.getStatement()).getTables().get(0));
            changeMonitorBo.setChageType(DBActionTypeEnum.UPDATE);
        }else if(changeMonitorBo.getStatement() instanceof Insert){
            changeMonitorBo.setTable(((Insert)changeMonitorBo.getStatement()).getTable());
            changeMonitorBo.setChageType(DBActionTypeEnum.INSERT);
        }else if(changeMonitorBo.getStatement() instanceof Delete) {
            changeMonitorBo.setTable(((Delete)changeMonitorBo.getStatement()).getTable());
            changeMonitorBo.setChageType(DBActionTypeEnum.DELETE);
        }

        // 剔除本监控插入的两张表,避免死循环
        if(changeMonitorBo.getTable().getName().equals(MONITOR_CHANGE_STORE_TABLE)
                || changeMonitorBo.getTable().getName().equals(MONITOR_CHANGE_STORE_TABLE_DETAIL)){
            changeMonitorBo.setNeedMonitor(false);
        }else if(needMonitorTables.contains(changeMonitorBo.getTable().getName())
                || needMonitorTables.contains("**")){
            changeMonitorBo.setNeedMonitor(true);
        }
    }

    private void prepareSelectSqlFromUpdateSql(Update statement, ChangeMonitorBo changeMonitorBo) {
        StringBuilder sb = new StringBuilder();
        sb.append("select ");
        Iterator<Column> iterator = statement.getColumns().iterator();
        boolean bFirst = true;
        while (iterator.hasNext()) {
            if (!bFirst) {
                sb.append(",");
            } else {
                bFirst = false;
            }
            sb.append(iterator.next().getColumnName());
        }
        //增加主键字段
        changeMonitorBo.getPkColumns().forEach(pkColumn -> {
            if(!sb.toString().contains(pkColumn)){
                sb.append(",").append(pkColumn);
            }
        });

        sb.append(" from ").append(statement.getTables().get(0));
        sb.append(" where ").append(statement.getWhere());
        log.info("查询语句为: {}", sb.toString());
        changeMonitorBo.setSelectSql(sb.toString());
    }

    private void prepareSelectSqlFromInsertSql(Insert statement, ChangeMonitorBo changeMonitorBo) {
        StringBuilder sb = new StringBuilder();
        sb.append("select * from ").append(statement.getTable());
        Iterator<Column> iterator = statement.getColumns().iterator();
        InsertItemsListVisitor visitor = new InsertItemsListVisitor();
        statement.getItemsList().accept(visitor);
        int index = 0;
        while (iterator.hasNext()) {
            String column = iterator.next().getColumnName();
            // 判断当前处理字段是否主键字段
            if(changeMonitorBo.getPkColumns().contains(column)){
                if(index == 0){
                    sb.append(" where ");
                }else{
                    sb.append(" and ");
                }
                String value = visitor.getColumnValue(index);
                sb.append(column).append(" = ").append(value);
            }
            index++;
        }
        log.info("查询语句为: {}", sb.toString());
        changeMonitorBo.setSelectSql(sb.toString());
    }

    private void prepareSelectSqlFromDeleteSql(Delete statement, ChangeMonitorBo changeMonitorBo) {
        StringBuilder sb = new StringBuilder();
        sb.append("select * from ").append(statement.getTable());
        sb.append(" where ").append(statement.getWhere());
        log.info("查询语句为: {}", sb.toString());
        changeMonitorBo.setSelectSql(sb.toString());
    }

    private void fetchTablePkColumns(ChangeMonitorBo changeMonitorBo) {
        StringBuilder sbPkSql = new StringBuilder();
        sbPkSql.append("select col.COLUMN_NAME ")
                .append( "  from all_constraints con, all_cons_columns col ")
                .append( " where con.constraint_name = col.constraint_name ")
                .append( "   and con.owner='RCPMDATA' ")
                .append( "   and con.owner=col.owner ")
                .append( "   and con.constraint_type = 'P' ")
                .append( "   and col.table_name = upper( ? ) ")
                .append( " order by position");
        List<Map<String, Object>> pkData = jdbcTemplate.queryForList(sbPkSql.toString(), new Object[]{changeMonitorBo.getTable().getName()});
        pkData.forEach(data->{
            changeMonitorBo.getPkColumns().add(String.valueOf(data.get("COLUMN_NAME")));
        });
    }

    private void fetchTableColumnComments(ChangeMonitorBo changeMonitorBo) {
        String commentsSql = "SELECT COLUMN_NAME, COMMENTS FROM all_col_comments WHERE table_name=upper( ? ) and owner='RCPMDATA' ";
        List<Map<String, Object>> commentsData = jdbcTemplate.queryForList(commentsSql, new Object[]{changeMonitorBo.getTable().getName()});
        commentsData.forEach(data->{
            changeMonitorBo.getColumnCommentsMap().put(String.valueOf(data.get("COLUMN_NAME")),String.valueOf(data.get("COMMENTS")));
        });
    }

}
package com.pab.bloan.capv.server.common.changemonitor.listener;

import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.ItemsListVisitor;
import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList;
import net.sf.jsqlparser.statement.select.SubSelect;

@Slf4j
public class InsertItemsListVisitor implements ItemsListVisitor {
    private ExpressionList expressionList;
    @Override
    public void visit(SubSelect subSelect) {

    }

    @Override
    public void visit(ExpressionList expressionList) {
        this.expressionList = expressionList;
    }

    @Override
    public void visit(MultiExpressionList multiExpressionList) {

    }

    public String getColumnValue(int index) {
        return expressionList.getExpressions().get(index).toString();
    }
}
package com.pab.bloan.capv.server.common.changemonitor.model;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.pab.bloan.capv.server.common.changemonitor.enumerate.DBActionTypeEnum;
import com.pab.bloan.capv.server.model.pojo.rcpmlog.ChangeObjectDetailPojo;
import com.pab.bloan.capv.server.model.pojo.rcpmlog.ChangeObjectPojo;
import com.pab.framework.google.common.collect.Maps;
import lombok.Data;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;

import java.util.List;
import java.util.Map;

@Data
public class ChangeMonitorBo {
    private String monitorSql;
    private boolean needMonitor = false;
    private String beforeJsonString;

    private Statement statement;
    private String selectSql;
    private DBActionTypeEnum chageType;
    private Table table;
    private List<String> pkColumns = Lists.newArrayList();
    private Map<String,String> columnCommentsMap = Maps.newHashMap();
    private List<ChangeObjectPojo> changeObjectPojoList = Lists.newArrayList();
    private List<ChangeObjectDetailPojo> changeObjectDetailPojoList = Lists.newArrayList();

    public ChangeMonitorBo(String sql){
        this.monitorSql = sql;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(JSON.toJSONString(this.changeObjectPojoList,true))
                .append("\n")
                .append(JSON.toJSONString(this.changeObjectDetailPojoList, true));
        return sb.toString();
    }
}
package com.pab.bloan.capv.server.common.configuration;

import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallFilter;
import com.github.pagehelper.PageHelper;
import com.pab.bloan.capv.server.common.changemonitor.interceptor.ChangeMinitorInterceptor;
import com.pab.bloan.capv.server.common.exception.BusinessException;
import com.pab.common.utils.PasswordCodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * 
 */
@Slf4j
public abstract class AbstractMyBatisConfig {

    @Autowired
    ChangeMinitorInterceptor changeMinitorInterceptor;

    abstract DruidConfig druidConfig();

    protected DruidDataSource getDataSource() {
        DruidConfig config = druidConfig();
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(config.getDriverClassName());
        dataSource.setUrl(config.getUrl());
        dataSource.setUsername(config.getUsername());
        try {
            dataSource.setPassword(PasswordCodeUtils.decrypt(config.getPassword()));
        }catch (Exception ex){
            log.error("decrypt", ex);
            dataSource.setPassword(config.getPassword());
        }
        if(config.getInitialSize() != null) {
            dataSource.setInitialSize(config.getInitialSize());
        }
        if(config.getMaxActive() != null) {
            dataSource.setMaxActive(config.getMaxActive());
        }
        if(config.getMinIdle() != null) {
            dataSource.setMinIdle(config.getMinIdle());
        }
        if(config.getMaxWaite() != null){
            dataSource.setMaxWait(config.getMaxWaite());
        }
        dataSource.setTestOnBorrow(config.isTestOnBorrow());
        dataSource.setTestWhileIdle(config.isTestWhileIdle());

        List<Filter> filters = new ArrayList<>();
        filters.add(statFilter());
        filters.add(wallFilter());
        dataSource.setProxyFilters(filters);

        return dataSource;
    }

    protected SqlSessionFactory getSqlSessionFactory(
            final DataSource dataSource,
            final String mapperFolder,
            final String basePackage) {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setTypeAliasesPackage(basePackage);
        bean.setPlugins(new Interceptor[]{pagePlugin(), changeMinitorInterceptor});
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        try {
            bean.setMapperLocations(resolver.getResources("classpath:mapping/" + mapperFolder + "/*.xml"));
            return bean.getObject();
        } catch (Exception e) {
            log.error("setMapperLocations", e);
            throw new BusinessException(e.getMessage(), e);
        }
    }

    protected StatFilter statFilter(){
        StatFilter statFilter = new StatFilter();
        statFilter.setLogSlowSql(true);
        statFilter.setMergeSql(true);
        statFilter.setSlowSqlMillis(1000);

        return statFilter;
    }

    protected WallFilter wallFilter(){
        WallFilter wallFilter = new WallFilter();

        //允许执行多条SQL
        WallConfig config = new WallConfig();
        config.setMultiStatementAllow(true);
        wallFilter.setConfig(config);

        return wallFilter;
    }

    public static PageHelper pagePlugin() {
        PageHelper pageHelper = new PageHelper();
        Properties properties = new Properties();
        properties.setProperty("dialect", "Oracle");
        properties.setProperty("reasonable", "true");
        properties.setProperty("rowBoundsWithCount", "true");
        pageHelper.setProperties(properties);
        return pageHelper;
    }
}
package com.pab.bloan.capv.server.common.configuration;

import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

/**
 * 
 */
@Configuration
@MapperScan(basePackages = {"com.pab.bloan.capv.server.dao.rcpmdata"},
        sqlSessionTemplateRef = "defaultSqlSessionTemplate")
@EnableTransactionManagement
@Slf4j
public class RcpmMyBatisConfig extends AbstractMyBatisConfig {

    @Bean("defaultConfig")
    @ConfigurationProperties(prefix = "jdbc.default")
    @Override
    public DruidConfig druidConfig() {
        return new DruidConfig();
    }

    @Primary
    @Bean(name = "defaultDataSource")
    public DataSource defaultDataSource() {
        return this.getDataSource();
    }

    @Bean
    public ServletRegistrationBean statViewServlet() {
        ServletRegistrationBean reg = new ServletRegistrationBean();
        reg.setServlet(new StatViewServlet());
        reg.addUrlMappings("/druid/*");
        reg.addInitParameter("loginUsername", "capv");
        reg.addInitParameter("loginPassword", "20083");
        return reg;
    }

    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new WebStatFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        return filterRegistrationBean;
    }

    @Primary
    @Bean(name = "defaultSqlSessionFactory")
    public SqlSessionFactory primarySqlSessionFactory(@Qualifier("defaultDataSource") DataSource dataSource) {
        return this.getSqlSessionFactory(dataSource,
                "rcpmdata", "com.pab.bloan.capv.server.model.pojo.rcpmdata");
    }

    @Bean(name = "defaultSqlSessionTemplate")
    public SqlSessionTemplate defaultSqlSessionTemplate(
            @Qualifier("defaultSqlSessionFactory") SqlSessionFactory sessionFactory) {
        return new SqlSessionTemplate(sessionFactory);
    }

    @Bean(name = "defaultJdbcTemplate")
    public JdbcTemplate defaultJdbcTemplate(@Qualifier("defaultDataSource")DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }

}

 

package com.pab.bloan.capv.server.model.pojo.rcpmlog;

import lombok.Data;

import java.util.Date;

@Data
public class ChangeObjectPojo {
    private String serialNo;

    private String tableName;

    private String pkColumnName;

    private String pkColumnComments;

    private String pkValue;

    private String changeType;

    private Date createTime;

    private String userUm;
}

 

package com.pab.bloan.capv.server.model.pojo.rcpmlog;

import lombok.Data;

import java.util.Date;

@Data
public class ChangeObjectDetailPojo {
    private String serialNo;
    private String objSerialNo;
    private String columnName;
    private String columnComments;
    private String lastValue;
    private String curValue;
    private Date createTime;
}

 

posted @ 2020-07-23 18:09  嘟神子  阅读(1245)  评论(0编辑  收藏  举报