常用JAVA工具类:基于mybtais的用户DML操作日子记录入库,兼容多数据源

实现过程:

第一步:自定义拦截器

第二步:sql处理的工具类,请求头中获取当前用户信息处理类,枚举类,以供在自定义拦截器中使用

第三步:设定开关,在yml配置文件中设定开关值,不设定,默认为不开启。

第三步: 使用,在数据源配置中,注册自定义的拦截器插件。

自定义拦截器,实现 Interceptor 接口以及相关注解,增加一个JdbcTemplate 属性,可传参的构造方法,参数为JdbcTemplate ,为的是在多数据源的情况下,可以明确指定操作数据源。获取相关的数据库信息。

代码实现:

@Component
@ConditionalOnBean({DabaiDataSourceConfig.class})
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class,
                Object.class, RowBounds.class, ResultHandler.class})})
@AutoConfigureAfter({PageHelperAutoConfiguration.class})
@Service
@Data
public class MybatisInterceptor implements Interceptor {

    private JdbcTemplate jdbcTemplate ;
    public MybatisInterceptor(){

    }
    //该构造器为了在创建数据源时,注册插件,传入指定的数据源,以便对应的数据库操作。
    public MybatisInterceptor(JdbcTemplate jdbcTemplate){
        this.jdbcTemplate =jdbcTemplate;
    }
    private static final Logger log = LoggerFactory.getLogger(MybatisInterceptor.class);



    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object returnValue = null;
        // 执行结果
        returnValue = invocation.proceed();
        try {
            // 获取拦截方法的参数
            Object[] args = invocation.getArgs();
            MappedStatement mappedStatement = (MappedStatement) args[0];
            Configuration configuration = mappedStatement.getConfiguration();
            Object parameter = null;
            if (invocation.getArgs().length > 1) {
                parameter = invocation.getArgs()[1];
            }
            BoundSql boundSql = mappedStatement.getBoundSql(parameter);
            /**
             *以下可以写相关的sql操作
             **/
            。。。

        } catch (Exception e) {
            log.info("拦截器异常", e.getMessage());
        }
        return returnValue;
    }


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

    @Override
    public void setProperties(Properties properties) {

    }
}

sql处理的工具类

@Slf4j
@Service
public class SqlTools {

    private SqlTools() {
    }

    /**
     * 解析BoundSql,生成不含占位符的SQL语句
     * @DHL
     * @param configuration
     * @param boundSql
     * @return java.lang.String
     */
    public static String showSql(Configuration configuration, BoundSql boundSql) {
        Object parameterObject = boundSql.getParameterObject();
        // 获取方法参数
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        // 格式化SQL
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
            if (parameterMappings.size() > 0 && parameterObject != null) {
                // 类型解析器
                TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
                if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                    sql = sql.replaceFirst("\\?", getParameterValue(parameterObject));
                } else {
                    MetaObject metaObject = configuration.newMetaObject(parameterObject);
                    for (ParameterMapping parameterMapping : parameterMappings) {
                        String propertyName = parameterMapping.getProperty();
                        String[] s = metaObject.getObjectWrapper().getGetterNames();
                        Arrays.toString(s);
                        if (metaObject.hasGetter(propertyName)) {
                            Object obj = metaObject.getValue(propertyName);
                            sql = sql.replaceFirst("\\?", getParameterValue(obj));
                        } else if (boundSql.hasAdditionalParameter(propertyName)) {
                            Object obj = boundSql.getAdditionalParameter(propertyName);
                            sql = sql.replaceFirst("\\?", getParameterValue(obj));
                        }
                    }
                }
        }
        return sql;
    }

    /**
     * 获取表名的注释
     * @param map
     * @param jdbcTemplate
     * @return
     */
    public static  String getTableNameComment(Map map,JdbcTemplate jdbcTemplate){

        StringBuffer sb = new StringBuffer();
        sb.append(" SELECT TABLE_COMMENT FROM information_schema.TABLES  WHERE ");
        sb.append(" table_schema = '"+map.get("databaseName").toString()+"'");
        sb.append(" AND TABLE_NAME ='"+map.get("tableName").toString()+"'");
        String sql = sb.toString();
        Map<String, Object> resultMap = jdbcTemplate.queryForMap(sql);
        if(Objects.nonNull(resultMap)&&!resultMap.isEmpty()){
            String tableNameComment = resultMap.get("TABLE_COMMENT").toString();
            return tableNameComment;
        }
        return "";
    }

    /**
     * 若为字符串或者日期类型,则在参数两边添加''
     *
     * @param obj
     * @return java.lang.String
     */
    private static String getParameterValue(Object obj) {
        String value;
        if (obj instanceof String) {
            value = "'" + obj.toString() + "'";
        } else if (obj instanceof Date) {
            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
            value = "'" + formatter.format(new Date()) + "'";
        } else {
            if (obj != null) {
                value = obj.toString();
            } else {
                value = "";
            }
        }
        return value;
    }


    /**
     * @return
     * @desc 操作前获取数据(修改或者删除的时候需要查询操作前的数据)
     */
    public static List getDataBeforeOperate(MappedStatement mappedStatement, BoundSql boundSql, Object parameter, JdbcTemplate jdbcTemplate) throws SQLException {

        List resultList = new ArrayList();

        //SELECT和INSERT操作不用记录修改前的值,直接返回
        if (parameter == null || SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())
                || SqlCommandType.INSERT.equals(mappedStatement.getSqlCommandType())) {
            return resultList;

        }

        String operateType = SqlTools.SqlCommandType(mappedStatement);

        boolean batchFlag = SqlTools.getbatchFlag(parameter);
        String tableName = SqlTools.getTableName(boundSql, operateType);

        // 如果动态数据源不存在,则尝试从数据源重载
        // 通过数据源名称获取数据源



        String primarykeyName = SqlTools.getFieldName(boundSql, operateType, batchFlag);

        List<Map> primarykeyList = SqlTools.reflectGetListValue(primarykeyName, parameter, batchFlag);
        //遍历主键列表
        for (Map map : primarykeyList) {
            for (Object obj : map.keySet()) {
                //获取主键值
                Object primarykeyValue = map.get(obj);
                queryData(jdbcTemplate, tableName, primarykeyName, primarykeyValue, resultList);
            }
        }
        return resultList;
    }


    /**
     * 判断sql操作类型 :update和delete的时候才需要去查询修改前的值
     *
     * @param mappedStatement
     * @return java.lang.String
     */
    public static String SqlCommandType(MappedStatement mappedStatement) {

        if (SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
            return OperateTypeCode.SELECT.getCode();
        }

        if (SqlCommandType.INSERT.equals(mappedStatement.getSqlCommandType())) {
            return OperateTypeCode.INSERT.getCode();
        }

        if (SqlCommandType.UPDATE.equals(mappedStatement.getSqlCommandType())) {
            return OperateTypeCode.UPDATE.getCode();
        }
        if (SqlCommandType.DELETE.equals(mappedStatement.getSqlCommandType())) {
            return OperateTypeCode.DELETE.getCode();
        }
        return null;
    }

    /**
     * 获取批次标记
     *
     * @param parameter
     * @return boolen
     */
    private static boolean getbatchFlag(Object parameter) {

        if (null != parameter && parameter.toString().contains("param")) {
            return true;
        }
        return false;
    }

    /**
     * 获取表名
     * 截取规则 :
     * 1.替换sql命令类型标签(select delete insert update)
     * 2.替换掉from标签
     * 3.截取从0开始,\n结束
     */
    public static String getTableName(BoundSql boundSql, String sqlType) {

        if (StringUtils.isBlank(sqlType) || StringUtils.isBlank(boundSql.getSql())) {
            return "";
        }
        //根据sqlType 去除update标签,delete标签,from标签,去除前后空格
        String tableStr = boundSql.getSql().toLowerCase();
        String tableName =null;
        if(OperateTypeCode.INSERT.getCode().equals(sqlType)){
            tableStr = tableStr.substring(tableStr.indexOf("into") + 5, tableStr.indexOf("(")).trim();
        }else if(OperateTypeCode.DELETE.getCode().equals(sqlType)){
            tableStr = tableStr.substring(tableStr.indexOf("from") + 5, tableStr.indexOf(" where")).trim();
        }else if(OperateTypeCode.UPDATE.getCode().equals(sqlType)){
            tableStr = tableStr.substring(tableStr.indexOf("update") + 7, tableStr.indexOf(" set")).trim();
        }

        int blank = tableStr.indexOf(" ");
        if(blank!=-1){
             tableStr.substring(0, blank);
        }
        tableName=tableStr;
        tableStr = boundSql.getSql().toLowerCase();
        if(StringUtil.isNotEmpty(tableName)){
            int length =tableName.length()+tableStr.indexOf(tableName);
            tableName = boundSql.getSql().substring(tableStr.indexOf(tableName),length);
        }

        return tableName;
    }


    /**
     * 获取sql获取FieldName
     * 截取规则 :
     * (1)去掉\n ,?,=
     * (2)从where索引位置开始截取, 到sqlStr最后一个位置 ,替换掉where操作符,去除空格
     * (3) batchFlag=true代表批量,需要从新解析(截取fieldName,从beginIndex=0开始截取,endIndex=;结束
     */
    private static String getFieldName(BoundSql boundSql, String sqlType, boolean batchFlag) {

        if (StringUtils.isBlank(sqlType) || StringUtils.isBlank(boundSql.getSql())) {
            return "";
        }

        String sqlStr = boundSql.getSql().replaceAll("\n", "").replaceAll("\\?", "").replaceAll("=", "");
        String fieldName = sqlStr.substring(sqlStr.indexOf("where"), sqlStr.length()).replace("where", "").trim();

        //解析批量
        if (batchFlag) {
            fieldName = fieldName.substring(0, fieldName.indexOf(";")).trim();
        }
        return fieldName;
    }

    /**
     * 根据属性名获取属性值
     */
    private static Object getFieldValueByName(String fieldName, Object o) {

        if (StringUtils.isBlank(fieldName) || o == null) {
            return null;
        }

        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + fieldName.substring(1);
            Method method = o.getClass().getMethod(getter, new Class[]{});
            Object value = method.invoke(o, new Object[]{});
            return value;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * 根据属性名获取属性值
     *
     * @param fieldName
     * @param object
     * @return
     */
    private static Object getFieldValueByFieldName(String fieldName, Object object) {
        try {
            Field field = object.getClass().getDeclaredField(fieldName);
            //设置对象的访问权限,保证对private的属性的访问
            field.setAccessible(true);
            return field.get(object);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }


    /**
     * 反射获取  @Param注解标记list属性值
     *
     * @param primarykeyName 主键名称
     * @param object         对象
     * @param batchFlag      batchFlag=true批量 ,batchFlag=false代表不是批量操作
     * @return
     */
    private static List<Map> reflectGetListValue(String primarykeyName, Object object, boolean batchFlag) {

        List<Map> resultList = new ArrayList<>();
        if (null == object) {
            return resultList;
        }

        //单个参数解析
        Map argsValueMap = new HashMap();
        if (!batchFlag) {
            Map argsMaps = beanToMap(object);
            for (Object obj : argsMaps.keySet()) {
                if (obj.equals(primarykeyName)) {
                    argsValueMap.put("obj", argsMaps.get(obj));
                }
            }
            resultList.add(argsValueMap);
            return resultList;
        }

        //批量解析
        Map<String, Object> parameterMap = (Map) object;
        for (Map.Entry<String, Object> entery : parameterMap.entrySet()) {
            String key = entery.getKey();
            if (key.indexOf("param") < 0) {
                List<Object> objList = (List) entery.getValue();
                if (objList != null) {
                    for (Object obj : objList) {
                        Map<String, Object> listChild = new HashMap<>();
                        Class<?> clz = obj.getClass();
                        Field[] declaredFields = clz.getDeclaredFields();
                        for (int i = 0; i < declaredFields.length; i++) {
                            Field field = declaredFields[i];
                            field.setAccessible(true);
                            try {
                                //Object value = field.get(obj);
                                if (StringUtils.equals(primarykeyName, field.getName())) {
                                    Object value = field.get(obj);
                                    listChild.put(field.getName(), value);
                                }
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            }
                        }
                        resultList.add(listChild);
                    }
                }
            }
        }
        return resultList;
    }


    /**
     * bean转map
     *
     * @param bean
     * @return map
     */
    private static <T> Map<String, Object> beanToMap(T bean) {

        Map<String, Object> map = new HashMap<>();
        if (bean != null) {
            BeanMap beanMap = BeanMap.create(bean);
            for (Object key : beanMap.keySet()) {
                map.put(String.valueOf(key), beanMap.get(key));
            }
        }
        return map;
    }

    /**
     * @param jdbcTemplate    操作类型
     * @param tableName       表名
     * @param primarykeyName  主键名称
     * @param primarykeyValue 主键值
     * @param resultList      查询结果集
     * @return
     * @desc 操作前获取数据(修改或者删除的时候需要查询操作前的数据)
     */
    private static void queryData(JdbcTemplate jdbcTemplate, String tableName, String primarykeyName, Object primarykeyValue, List resultList) {

        if (StringUtils.isNotEmpty(tableName) && StringUtils.isNotEmpty(primarykeyName) && primarykeyValue != null) {
            StringBuffer sb = new StringBuffer();
            sb.append(" select * from ");
            sb.append(tableName);
            sb.append(" where ");
            sb.append(primarykeyName);
            sb.append(" = ? ");
            String sql = sb.toString();
            List<Map<String, Object>> resultMaps = jdbcTemplate.queryForList(sql, primarykeyValue);
            resultList.add(resultMaps);
        }
    }
}

枚举静态变量类

public enum OperateTypeCode {

    SELECT("select","查询"),

    INSERT("insert","新增"),

    UPDATE("update","修改"),

    DELETE("delete","删除"),

    FLUSH("flush","flush");


    private static Map<String, OperateTypeCode> lookup = new HashMap<>();

    static {
        for (OperateTypeCode s : EnumSet.allOf(OperateTypeCode.class)) {
            lookup.put(s.code, s);
        }
    }

    @Getter
    private final String code;

    @Getter
    private final String value;

    OperateTypeCode(String code, String value) {
        this.code = code;
        this.value = value;
    }

    public static Optional<OperateTypeCode> getFromCode(String code) {
        return Optional.ofNullable(lookup.getOrDefault(code, null));
    }

    /**
     * 根据编码获取描述信息
     *
     * @param code 枚举字符串
     * @return true or false
     */
    public static String getDesc(String code) {
        for (OperateTypeCode flag : OperateTypeCode.values()) {
            if (code.contains(flag.code)) {
                return flag.value;
            }
        }
        return null;
    }
}

请求信息处理类 :  

 阿咚这边的项目是按下边方式处理,通过网关去内网调用,登陆人信息放到header 中,同学可以根据自己的项目情况去调整或则自己定制。

@Slf4j
@Component
public class HttpRequestUtil {

    private HttpRequestUtil(){}
/**
     * 获取head中用户信息
     * @return
     */
    private static String getUserinfoStr(){
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if(servletRequestAttributes == null ){
            throw new BusinessException("error.00010", HttpRequestUtil.class);
        }
        HttpServletRequest request = servletRequestAttributes.getRequest();

        //获取用户信息
        String userInfoStr = request.getHeader("userInfo");//用户信息节点  可以根据自己的项目信息实际情况去替换 

        //判断为空
        if(StringUtils.isEmpty(userInfoStr) || "undefined".equals(userInfoStr)){
            return ""; 
        }
        log.info("获取resultObject信息解密前:"+JSON.toJSONString(userInfoStr));
        //对用户信息进行编码
        String userinfoStr = new String(new Base64().decode(userInfoStr));
        return userinfoStr;
    }
 /**
     * 获取用户信息
     * @return
     */
    public static UserInfo getUserInfoNotThrowException(){
        //将String字符串信息转化为JSONObject数据格式
        JSONObject userinfoObject = JSON.parseObject(getUserinfoStr());
        JSONObject resultObject = userinfoObject.getJSONObject("result");
        log.info("获取resultObject信息解密后:"+JSON.toJSONString(resultObject));
        //判断返回结果数据是否为空,或者手机号是否为空
        if(resultObject == null){
            return null;
        }
        //用户信息
        JSONObject userinfoJson =resultObject.getJSONObject("userInfo");//用户信息节点  可以根据自己的项目信息实际情况去替换 
        UserInfo userInfo = JSON.toJavaObject(userinfoJson,UserInfo.class);

        log.info("获取后台运营用户信息:"+userinfoJson);
        return operateUserInfo;
    }
}

根据SqlTools 和HttpRequestUtil 两个类,自行实现自己想要的功能。以下举例阿咚的实现功能,

实现将当前登陆人员的信息和操作的sql信息记录入库,在SqlTools 还标有可以查询DML操作之前的数据是什么状态。可以根据情况自行调用。

  //数据库操作类型
            String operateType = SqlTools.SqlCommandType(mappedStatement);

            String tableName = SqlTools.getTableName(boundSql, operateType);

            if(!Objects.isNull(tableName) && !tableName.contains("AA,BB,CC")){ //不想记录的表操作
                Map<String,String> map =new HashMap<>();
                map.put("databaseName","你的数据库库名");
                map.put("tableName",tableName);
                String tableNameComment = SqlTools.getTableNameComment(map,jdbcTemplate);
                String userId ="admin";
                String name ="admin";
                String category ="管理员";
                UserInfo userInfo = HttpRequestUtil.getUserInfoNotThrowException();
                if(Objects.nonNull(userInfo)){
                    userId=userInfo.getId();
                    name=userInfo.getName();
                }
                StringBuffer sb = new StringBuffer();
                sb.append("INSERT INTO `manage_operation_record` (`id`, `category`, `operation`, `remarks`, `status`, `reviser_id`, `reviser`, `table_name`, `table_desc`, `data_id`, `content`, `createdAt`, `updatedAt`) VALUES (");
                sb.append("'"+ UuidUtils.base58Uuid() +"',");//主键
                sb.append("'"+ category+"',");//category 分类
                sb.append("'"+ OperateTypeCode.getDesc(operateType).toString() +"',");//operation 操作类型
                sb.append("'"+name+":操作"+tableNameComment+"',");//remarks 备注
                if(Objects.nonNull(returnValue)){
                    sb.append("1,");//status 状态 成功
                }else{
                    sb.append("0,");//status 状态 失败
                }
                sb.append("'"+ userId+"',");//reviser_id 操作用户ID
                sb.append("'"+ name +"',");//reviser 操作用户名 或手机号
                sb.append("'"+ tableName +"',");//table_name 表明
                sb.append("'"+ tableNameComment +"',");//table_desc 表注释
                sb.append("'',");//data_id 数据id
                // 获取sql语句
                String sql = SqlTools.showSql(configuration, boundSql);
                if(StringUtil.isEmpty(sql)&&sql.length()>200){
                    sql = sql.substring(0,200);
                }
                JSONObject json = new JSONObject();
                json.put("sql",sql.replaceAll("\"",""));
                sb.append("'"+ json.toJSONString() +"',");//content sql 内容 但是长度如果大于200 截断
                sb.append("'"+ LocalDateTime.now() +"',");//createdAt 创建时间
                sb.append("'"+ LocalDateTime.now() +"')");//updatedAt 更新时间
                String  sqlInsert =sb.toString();
                jdbcTemplate.execute(sqlInsert);

                log.info("Sql :{}", JSON.toJSONString(sql));
            }

设定开关,在yml配置文件中设定开关值,不设定,默认为不开启。

阿咚使用的yml文件的形式

interceptor-switch:
  MybatisInterceptorSwitch: 1

使用,在数据源配置中,注册自定义的拦截器插件。

数据源配置中添加

@Autowired
private Environment env;

数据源注册插件的位置

 if(StringUtil.isNotEmpty(mybatisInterceptorSwitch)&&"1".equals(mybatisInterceptorSwitch)){
            bean.setPlugins(new Interceptor[]{
                    new PaginationInterceptor(),new MybatisInterceptor(new JdbcTemplate(dataSource))
            });
        }else{
            bean.setPlugins(new Interceptor[]{
                    new PaginationInterceptor()
            });
        }

以上为全部的实现,如果项目中没有多数据源的情况,则不需要这后两步,创建自定义的 MybatisInterceptor 拦截器时,不需要可穿参的构造器。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
视图是虚拟表,它的存在完全建立在对其他表的查询语句之上。视图DML操作就是对视图进行数据操作,包括插入、更新、删除等操作。下面是一些示例: 1. 创建视图 ``` CREATE VIEW my_view AS SELECT column1, column2 FROM my_table WHERE column3 = 'value'; ``` 2. 视图DML操作 插入数据: ``` INSERT INTO my_view (column1, column2) VALUES ('value1', 'value2'); ``` 更新数据: ``` UPDATE my_view SET column1 = 'new_value' WHERE column2 = 'value2'; ``` 删除数据: ``` DELETE FROM my_view WHERE column1 = 'value1'; ``` 3. 获取用户的所有表名或视图名 查询所有表: ``` SELECT table_name FROM information_schema.tables WHERE table_schema = 'your_database_name' AND table_type = 'BASE TABLE'; ``` 查询所有视图: ``` SELECT table_name FROM information_schema.tables WHERE table_schema = 'your_database_name' AND table_type = 'VIEW'; ``` 4. 视图插入数据 虽然可以通过视图插入数据,但是需要注意以下几点: - 视图必须满足以下条件之一:包含所有非空列,或者包含唯一标识列。 - 视图不能包含以下元素:GROUP BY、HAVING、DISTINCT、TOP、ORDER BY、UNION、EXCEPT 和 INTERSECT 子句。 示例: ``` CREATE TABLE my_table ( column1 INT PRIMARY KEY, column2 VARCHAR(50) NOT NULL, column3 DATE NOT NULL ); CREATE VIEW my_view AS SELECT column1, column2 FROM my_table WHERE column3 = '2022-01-01'; INSERT INTO my_view (column1, column2) VALUES (1, 'value1'); ``` 在这个示例中,我们创建了一个名为 `my_table` 的表,并添加了三个列。接下来,我们创建了一个名为 `my_view` 的视图,该视图仅显示 `my_table` 中满足 `column3 = '2022-01-01'` 的行的 `column1` 和 `column2` 列。最后,我们尝试向视图中插入数据,由于我们没有满足视图必须满足的条件,插入操作将失败。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值