中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何使用SpringAop動態獲取mapper執行的SQL并保存SQL到Log表中

發布時間:2023-03-08 10:06:51 來源:億速云 閱讀:111 作者:iii 欄目:開發技術

本文小編為大家詳細介紹“如何使用SpringAop動態獲取mapper執行的SQL并保存SQL到Log表中”,內容詳細,步驟清晰,細節處理妥當,希望這篇“如何使用SpringAop動態獲取mapper執行的SQL并保存SQL到Log表中”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。

    1.背景

    工作的時候遇到一個這樣的需要,在多機環境下,使用Mysql作為參數庫。因為某些原因不能使用Mysql自帶的數據同步,所以需要自己實現一個多節點的Mysql數據同步程序。

    所以打算人為的設定主Mysql可讀可寫,備Mysql只能讀。為了保證各個Mysql數據的同步,有一個Log表,用于記錄操作主Mysql的SQL語句,從而其他備Mysql只需要通過Log表來進行數據同步。

    2.難點

    (1)由于項目使用的是Mybatis,不是使用原生的jdbc,所以需要在不影響其他人使用Mybatis開發的同時,獲取SQL語句并寫入log表

    (2)需要保證mapper的操作和log的insert在同一個事務中

    3.實現

    3.1ModelSumbit.java

    自定義注解,用于Aop切入點

    package com.yjy.annotation;
     
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
     
     
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ModelSumbit{
       
        String value() default "";
    }

    3.2LogAdvice.java

    主要看環繞通知方法

    package com.lyf.aspect;
     
    import com.lyf.service.LogService;
    import com.lyf.utils.SqlUtils;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
     
     
     
    @Aspect
    @Component
    public class MyAdvice {
     
        @Autowired
        private LogService logService;
     
        @Autowired
        private SqlSessionFactory sqlSessionFactory;
     
     
        @Pointcut("@annotation(com.yjy.annotation.ModelSumbit)")
        private void pc(){
     
        }
     
        //前置通知
        //指定該方法是前置通知,并指定切入點
        @Before("MyAdvice.pc()")
        public void before(){
    //        System.out.println("這是前置通知!!!!!");
        }
     
        //后置通知
        @AfterReturning("MyAdvice.pc()")
        public void afterReturning(){
    //        System.out.println("這是后置通知!(如果出現異常,將不會調用)!!!!");
        }
     
        //環繞通知
        @Around("MyAdvice.pc()")
        public Object around(ProceedingJoinPoint pjp) throws Throwable{
            //1.從redis中獲取主數據庫,若獲取不到直接退出,否則判斷當前數據源是會否為主,若不為主,則切換到主數據源
            //2.調用目標方法
            Object proceed = pjp.proceed();
            //3.獲取SQL
            String sql = SqlUtils.getMybatisSql(pjp,sqlSessionFactory);
            System.out.println(sql);
            //4.插入日志
            logService.insert(sql);
            //5.通知同步程序
            return proceed;
        }
     
        //異常通知
        @AfterThrowing("MyAdvice.pc()")
        public void afterException(){
    //        System.out.println("出事了,拋異常了!!!!");
        }
     
        //后置通知
        @After("MyAdvice.pc()")
        public void after(){
    //        System.out.println("這是后置通知!(無論是否出現異常都會調用)!!!!");
        }
    }

    3.3SqlUtils.java

    用于獲取SQL語句

    package com.lyf.utils;
     
    import com.sun.deploy.util.ArrayUtil;
    import org.apache.ibatis.annotations.Param;
    import org.apache.ibatis.mapping.BoundSql;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.mapping.ParameterMapping;
    import org.apache.ibatis.reflection.MetaObject;
    import org.apache.ibatis.session.Configuration;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.type.TypeHandlerRegistry;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.reflect.MethodSignature;
     
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.lang.reflect.Parameter;
    import java.text.DateFormat;
    import java.util.*;
     
    public class SqlUtils {
     
        /**
         * 獲取aop中的SQL語句
         * @param pjp
         * @param sqlSessionFactory
         * @return
         * @throws IllegalAccessException
         */
        public static String getMybatisSql(ProceedingJoinPoint pjp, SqlSessionFactory sqlSessionFactory) throws IllegalAccessException {
            Map<String,Object> map = new HashMap<>();
            //1.獲取namespace+methdoName
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            Method method = signature.getMethod();
            String namespace = method.getDeclaringClass().getName();
            String methodName = method.getName();
            //2.根據namespace+methdoName獲取相對應的MappedStatement
            Configuration configuration = sqlSessionFactory.getConfiguration();
            MappedStatement mappedStatement = configuration.getMappedStatement(namespace+"."+methodName);
    //        //3.獲取方法參數列表名
    //        Parameter[] parameters = method.getParameters();
            //4.形參和實參的映射
            Object[] objects = pjp.getArgs(); //獲取實參
            Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            for (int i = 0;i<parameterAnnotations.length;i++){
                Object object = objects[i];
                if (parameterAnnotations[i].length == 0){ //說明該參數沒有注解,此時該參數可能是實體類,也可能是Map,也可能只是單參數
                    if (object.getClass().getClassLoader() == null && object instanceof Map){
                        map.putAll((Map<? extends String, ?>) object);
                        System.out.println("該對象為Map");
                    }else{//形參為自定義實體類
                        map.putAll(objectToMap(object));
                        System.out.println("該對象為用戶自定義的對象");
                    }
                }else{//說明該參數有注解,且必須為@Param
                    for (Annotation annotation : parameterAnnotations[i]){
                        if (annotation instanceof Param){
                            map.put(((Param) annotation).value(),object);
                        }
                    }
                }
            }
            //5.獲取boundSql
            BoundSql boundSql = mappedStatement.getBoundSql(map);
            return showSql(configuration,boundSql);
        }
     
        /**
         * 解析BoundSql,生成不含占位符的SQL語句
         * @param configuration
         * @param boundSql
         * @return
         */
        private  static String showSql(Configuration configuration, BoundSql boundSql) {
            Object parameterObject = boundSql.getParameterObject();
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
            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();
                        s.toString();
                        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 obj
         * @return
         */
        private static String getParameterValue(Object obj) {
            String value = null;
            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;
        }
     
        /**
         * 獲取利用反射獲取類里面的值和名稱
         *
         * @param obj
         * @return
         * @throws IllegalAccessException
         */
        private static Map<String, Object> objectToMap(Object obj) throws IllegalAccessException {
            Map<String, Object> map = new HashMap<>();
            Class<?> clazz = obj.getClass();
            System.out.println(clazz);
            for (Field field : clazz.getDeclaredFields()) {
                field.setAccessible(true);
                String fieldName = field.getName();
                Object value = field.get(obj);
                map.put(fieldName, value);
            }
            return map;
        }
    }

    4.注意事項

    • Mapper接口的增刪改方法上面加上@ModelSumbit注解,才會進入模型數據提交AOP

    • Mapper接口方法的形參,可以有如下三種形式

    ①形參為自定義實體類

    @ModelSumbit
    void insert(User user);

    ②形參為Map

    @ModelSumbit
    void insert(Map<String,Object> map);

    ③形參為單個或多個參數,需要使用@Param注解

    @ModelSumbit
    void insert(@Param("userName") String userName, @Param("age")Integer age);

    注意:即便只有一個參數,采用第③方式的時候,仍然需要使用@Param注解

    讀到這里,這篇“如何使用SpringAop動態獲取mapper執行的SQL并保存SQL到Log表中”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    曲麻莱县| 德昌县| 凌源市| 玛沁县| 大洼县| 吉林省| 崇明县| 武安市| 临洮县| 景洪市| 炉霍县| 阿拉善右旗| 安龙县| 石狮市| 玛纳斯县| 犍为县| 乌拉特前旗| 综艺| 葫芦岛市| 正安县| 红桥区| 姜堰市| 雷州市| 措美县| 镇原县| 精河县| 车致| 中超| 达日县| 黄平县| 甘洛县| 邓州市| 绥化市| 新民市| 华阴市| 从化市| 萝北县| 天祝| 哈巴河县| 肇庆市| 隆德县|