Day05

目录

1、编写mybatis插件,实现字段自动填充

注意

2、ThreadLocal的简单使用

3、问题:添加员工语句执行成功,但数据库中未添加新员工

4、问题:foreach


1、编写mybatis插件,实现字段自动填充

如何编写插件

@Intercepts({@Signature(type = Executor.class, method = "update",
        args = {MappedStatement.class, Object.class})})
public class MybatisMetaInterceptor implements Interceptor {

    /**
     * invocation存有捕获dao方法的参数,以及sql语句等相关信息<br>
     * 实现自动填充功能
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //获取语句信息
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        //获取参数
        Object param = invocation.getArgs()[1];
        //如果dao方法上参数为空,则不进行填充
        if (param == null)
            return invocation.proceed();

        /**
         * 判断参数类型是否为ParamMap
         * 当dao参数为Collection类或者有两个及以上参数时,Mybatis会把参数封装进一个Map中
         *否则,Param的类型就是dao方法上的参数
         */
        if (param instanceof MapperMethod.ParamMap) {
            MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) param;
            List list = (List) paramMap.get("list");
            List<Field[]> fields = getAllFields(list.get(0));
            list.stream().forEach(item -> {
                try {
                    setValue(mappedStatement, fields, item);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        } else {
            //单个参数
            List<Field[]> fields = getAllFields(param);
            setValue(mappedStatement, fields, param);
        }

        return invocation.proceed();
    }

    /**
     * 获取参数字段
     * @param param
     * @return
     */
    private List<Field[]> getAllFields(Object param) {
        if (param == null)
            return null;
        ArrayList<Field[]> list = new ArrayList<>();
        Class<?> paramClass = param.getClass();
        while (paramClass != null) {
            list.add(paramClass.getDeclaredFields());
            paramClass = paramClass.getSuperclass();
        }
        return list;
    }

    /**
     * 对对象的字段进行赋值
     * @param mappedStatement
     * @param fields
     * @param param
     * @throws Exception
     */
    private void setValue(MappedStatement mappedStatement, List<Field[]> fields, Object param) throws Exception {
        Long user = BaseThreadLocal.get();
        LocalDateTime time = LocalDateTime.now();
        if (SqlCommandType.INSERT == mappedStatement.getSqlCommandType()) {
            for (Field[] f : fields) {
                for (Field field : f) {
                    field.setAccessible(true);
                    if (field.getName().equals("id")) {
                        field.set(param, YitIdHelper.nextId());
                        log.info(Long.toString((Long) field.get(param)));
                    }

                    if (field.getName().matches("(create|update)User")) {
                        field.set(param, user);
                    }
                    if (field.getName().matches("(create|update)Time")) {
                        field.set(param, time);
                    }

                    field.setAccessible(false);
                }
            }
        }else if (SqlCommandType.UPDATE == mappedStatement.getSqlCommandType()) {
            for (Field[] f : fields) {
                for (Field field : f) {
                    field.setAccessible(true);
                    if (field.getName().matches("updateUser")) {
                        field.set(param, user);
                    }
                    if (field.getName().matches("updateTime")) {
                        field.set(param, time);
                    }
                    field.setAccessible(false);
                }

            }
        }
    }
}

注意

       1、 Invocation.getArgs();会得到一个Object数组,其中的args[0]是MappedStatement对象,存放了被拦截SQL的类型及相关信息。args[1]存放了被拦截方法的参数,首先来看args[1]的类型。

args[1]的类型有两种ParamMap与参数的自身类型,或者null。

当参数个数只有一个且没有@param,并且不是集合类与数组时,我们可以直接强转。

当args[1]返回了ParamMap,我们需要从该Map中获取值,map的key已在图中标出

注意:在Java8及以上,可在JVM启动时添加参数-parameters,这样mybatis可以直接获取到参数名作为key。

2、将自定义拦截器加入mybatis

yaml:

mybatis:
  mapper-locations: classpath:/mapper/*.xml
  configuration:
    interceptors: //全类名

xml:

<plugins>
        <plugin interceptor="org.example.Utils.MybatisMetaInterceptor"/>
    </plugins>

yml的Configuration与xml定制配置,只能用一个

配置类方式加入

@Configuration
public class MyBatisConfig {
  @Bean
  ConfigurationCustomizer mybatisConfigurationCustomizer() {
    return new ConfigurationCustomizer() {
      @Override
      public void customize(Configuration configuration) {
        // customize ...
      }
    };
  }
}

相关文章:MyBatis运行原理(三) : 多参数封装Map的流程、查询实现原理 (源码分析)

                 启用 -parameters 编译选项简化 mybatis @Param 注解重复问题


2、ThreadLocal的简单使用

在看了许多文章后,ThreadLocal的原理应该是这样的:每个Thread内部都有一个Map,当使用threadLocal.set()方法时,会先获取当前线程的Map,在把(threadLocal, value)存入map中。

Threadlocal.get(),则是从当前线程的Map中,获取与threadLocal对应的value

ThreadLocal相当于一个套壳的工具类,本质上还是使用Thread中的Map进行存储。

相关文章

public class BaseThreadLocal {

    private static ThreadLocal<Long> local = new ThreadLocal<>();

    public static Long get() {
        return local.get();
    }

    public static void set(Long v) {
        local.set(v);
    }
}

3、问题:添加员工语句执行成功,但数据库中未添加新员工

表面原因:传给后端的Json对象的Id与数据库中的不一致

根本原因:这个id是Long型,前端解析Long的精度比后端小,当后端传给前端id时,前端会有精度丢失问题

解决:传给前端时,把Long转给String在转给前端

@Configuration
public class WebConfig extends WebMvcConfigurationSupport {


    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("扩展消息转换器...");
        //创建消息转换器对象
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        //设置对象转换器,底层使用Jackson将Java对象转为json
        messageConverter.setObjectMapper(objectMapper());
        //将上面的消息转换器对象追加到mvc框架的转换器集合中
        converters.add(0,messageConverter);

    }


    private ObjectMapper objectMapper() {
        final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
        ObjectMapper mapper = new ObjectMapper();
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        //添加序列化器
        javaTimeModule.addSerializer(Long.class, ToStringSerializer.instance);
        mapper.registerModule(javaTimeModule);
        return mapper;
    }


}

 注意,以上写法只针对Json对象,当java对象需要转成Json对象时,才会执行上述消息转换器的序列化方法。


4、问题:foreach

相关文章:Java中foreach循环两种实现原理

原理:集合类或者实现了Iterator接口的类的foreach本质上利用了Iterator迭代器

数组的foreach只是把for(var i : arr) ==> for (int i = 0; i < arr.length; i++)的模式

注意点:

 foreach循环不可以改变集合,原因:相关文章

public class Main{
    public static void main(String[] args) throws IOException {
        List<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");

        for (String s : list) {
            list.remove(s);
        }
    }
}

但是可以改变集合中的对象的属性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值