mybaits-plus实现自定义字典转换

mybaits,plus · 浏览次数 : 0

小编点评

```java @Component public class DictionaryInterceptor implements InnerInterceptor { private final DictionaryService dictionaryService; public DictionaryInterceptor(@Lazy DictionaryService dictionaryService) { this.dictionaryService = dictionaryService; } @Override public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { try { transClassFieldToValue(parameter); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException { try { transClassFieldToValue(parameter); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } private void transClassFieldToValue(Object param) throws IllegalAccessException { if (param == null) { return; } Object obj; Field[] fields; if (param instanceof MapperMethod.ParamMap<?>) { handleParamMap((MapperMethod.ParamMap<?>) param); return; } else { obj = param; fields = param.getClass().getDeclaredFields(); } for (Field field : fields) { if (!field.isAnnotationPresent(Dictionary.class)) { continue; } Dictionary annotation = field.getAnnotation(Dictionary.class); field.setAccessible(true); if (annotation != null && field.get(obj) != null) { field.set(obj, dictionaryService.getByCode(annotation.dictionaryType(), (String) field.get(obj)).getValue()); } } } private void handleParamMap(MapperMethod.ParamMap<?> param) throws IllegalAccessException { for (Object value : param.values()) { transClassFieldToValue(value); } } } ``` **Explanation:** * The `DictionaryInterceptor` implements the `InnerInterceptor` interface. * It is a bean that is registered with the `MybatisPlusInterceptor` bean. * The `beforeQuery`, `beforeUpdate` and `afterQuery` methods handle the query, update and result retrieval events, respectively. * The `transClassFieldToValue` method transposes the value of each property in the parameter to the corresponding property in the `Dictionary` object. * The `handleParamMap` method iterates over the values of the parameter and sets the corresponding property in the `Dictionary` object. * The `DictionaryType` enum defines the different types of dictionaries and their corresponding codes and descriptions.

正文

需求:字典实现类似mybatis-plus中@EnumValue的功能,假设枚举类中应用使用code,数据库存储对应的value
思路:Mybatis支持对Executor、StatementHandler、PameterHandler和ResultSetHandler进行拦截,也就是说会对这4种对象进行代理。mybatis-plus实际上也是通过mybatis提供的拦截功能进行封装,我们在对数据库进行insert\query\update操作时,利用mybatis提供的拦截器对字典做转换

@Intercepts(
    {
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),
        @Signature(type = StatementHandler.class, method = "getBoundSql", args = {}),
        @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}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
    }
)
public class MybatisPlusInterceptor implements Interceptor {
//....
}

写入、查询时参数作转换

@Component
public class DictionaryInterceptor implements InnerInterceptor {

    private final DictionaryService dictionaryService;

    public DictionaryInterceptor(@Lazy DictionaryService dictionaryService) {
        this.dictionaryService = dictionaryService;
    }

    /**
     * {@link Executor#query(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql)} 操作前置处理
     * <p>
     *
     * @param executor      Executor(可能是代理对象)
     * @param ms            MappedStatement
     * @param parameter     parameter
     * @param rowBounds     rowBounds
     * @param resultHandler resultHandler
     * @param boundSql      boundSql
     */
    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {

        try {
            transClassFieldToValue(parameter);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * {@link Executor#update(MappedStatement, Object)} 操作前置处理
     * <p>
     *
     * @param executor  Executor(可能是代理对象)
     * @param ms        MappedStatement
     * @param parameter parameter
     */
    @Override
    public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
        try {
            transClassFieldToValue(parameter);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    private void transClassFieldToValue(Object param) throws IllegalAccessException {
        if (param == null) {
            return;
        }
        Object obj;
        Field[] fields;
        if (param instanceof MapperMethod.ParamMap<?>) {
            handleParamMap((MapperMethod.ParamMap<?>) param);
            return;
        } else {
            obj = param;
            fields = param.getClass().getDeclaredFields();
        }

        for (Field field : fields) {
            if (!field.isAnnotationPresent(Dictionary.class)) {
                continue;
            }

            Dictionary annotation = field.getAnnotation(Dictionary.class);
            field.setAccessible(true);
            if (annotation != null && field.get(obj) != null) {
                field.set(obj, dictionaryService.getByCode(annotation.dictionaryType(), (String) field.get(obj)).getValue());
            }

        }
    }

    private void handleParamMap(MapperMethod.ParamMap<?> param) throws IllegalAccessException {
        for (Object value : param.values()) {
            transClassFieldToValue(value);
        }
    }

}

返回结果转译

@Slf4j
@Component
@Intercepts({@Signature(
        type = ResultSetHandler.class,
        method = "handleResultSets",
        args = {Statement.class})})
public class DictionaryResultInterceptor implements Interceptor {

    private final DictionaryService dictionaryService;

    public DictionaryResultInterceptor(@Lazy DictionaryService dictionaryService) {
        this.dictionaryService = dictionaryService;
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object result = invocation.proceed();

        if (result instanceof List) {
            for (Object line : (List) result) {
                transClassFieldToCode(line);
            }
        } else {
            transClassFieldToCode(result);
        }

        return result;
    }

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

    private Object transClassFieldToCode(Object parameter) throws Exception {

        Field[] fields = parameter.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(Dictionary.class)) {
                continue;
            }
            field.setAccessible(true);
            Object value = field.get(parameter);

            Dictionary annotation = field.getAnnotation(Dictionary.class);
            if (value != null) {
                field.set(parameter, dictionaryService.getByValue(annotation.dictionaryType(), (String) value).getCode());
            }
        }
        return parameter;
    }

最后一步别忘了把自定义拦截器注册到mybaits-plus

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(DictionaryInterceptor dictionaryInterceptor) {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(dictionaryInterceptor);
        //...其他插件注册

        return mybatisPlusInterceptor;
    }
@TableName("dictionary")
@Data
public class Dictionary {

    @Id
    private Long id;

    /**
     * 编码,编码+类型唯一
     */
    @NotBlank
    private String code;

    /**
     * 字典值
     */
    @NotBlank
    private String value;

    /**
     * 类型
     */
    @NotNull
    private DictionaryType type;

    /**
     * 描述,用于展示
     */
    @TableField(value = "`desc`")
    private String desc;

}
public interface DictionaryService {

    /**
     * 获取分类下所有kv
     *
     * @param type 分类
     * @return
     */
    List<Dictionary> listByType(DictionaryType type);

    /**
     * code转换字典
     *
     * @param type 分类
     * @param code 编码
     * @return
     */
    Dictionary getByCode(DictionaryType type, String code) throws NoSuchElementException;

    /**
     * value转换字典
     *
     * @param type  分类
     * @param value 字典值
     * @return
     */
    Dictionary getByValue(DictionaryType type, String value);
public enum DictionaryType  {

    USER_ROLE("000001", "用户角色");

    @EnumValue
    final String code;
    final String desc;

    DictionaryType(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public String getCode() {
        return code;
    }

    public String getDesc() {
        return desc;
    }
}

与mybaits-plus实现自定义字典转换相似的内容:

mybaits-plus实现自定义字典转换

需求:字典实现类似mybatis-plus中@EnumValue的功能,假设枚举类中应用使用code,数据库存储对应的value 思路:Mybatis支持对Executor、StatementHandler、PameterHandler和ResultSetHandler进行拦截,也就是说会对这4种对

基于SpringBoot实现操作GaussDB(DWS)的项目实战

摘要:本文就使用springboot结合mybatis plus在项目中实现对GaussDB(DWS)的增删改查操作。 本文分享自华为云社区《基于SpringBoot实现操作GaussDB(DWS)的项目实战【玩转PB级数仓GaussDB(DWS)】》,作者:清雨小竹。 GaussDB(DWS) 数

Mybatis Plus 3.X版本的insert填充自增id的IdType.ID_WORKER策略源码分析

总结/朱季谦 某天同事突然问我,你知道Mybatis Plus的insert方法,插入数据后自增id是如何自增的吗? 我愣了一下,脑海里只想到,当在POJO类的id设置一个自增策略后,例如@TableId(value = "id",type = IdType.ID_WORKER)的注解策略时,就能实

基于EasyCode定制Mybatisplus全自动单表实现:新增/批量新增/修改/批量删除/分页查询/ID查询

基于EasyCode定制Mybatisplus全自动单表实现CRUD接口 分页查询 ID查询 新增 批量新增 修改 批量删除 注意使用了MybatisPlus的自动填充功能,和insertBatchSomeColumn扩展批量插入功能,分页插件 需要几个增加插件实现类 自动填充 package co

MyBatis-Plus 实现多租户管理的实践

本文主要讲解使用Mybatis-Plus结合dynamic-datasource来实现多租户管理 在现代企业应用中,多租户(Multi-Tenant)架构已经成为一个非常重要的设计模式。多租户架构允许多个租户共享同一应用程序实例,但每个租户的数据彼此隔离。实现这一点可以大大提高资源利用率并降低运营成

多数据源管理:掌握@DS注解的威力

大家在日常后端开发过程,不可避免的会接触到需要用到配置多个数据源的场景,在这里,小编介绍一种简单方便的,只需要简单的配置和一个@DS注解就能实现动态数据源的方式,这种动态数据源底层原理是基于Mybatis-plus来实现的。

我的第一个项目(十四) :完成数据保存功能(前端,增查改接口)

好家伙,天天拖,终于写完了 代码已开源(Gitee) PH-planewar: 个人开发的全栈小游戏 前端:vue2 + element-ui 后端: Springboot + mybatis-plus 数据库: mysql 目前实现功能: 1.注册登陆 2.游戏数据保存 3.游戏运行 (gitee

我的第一个项目(十五) :完成数据保存功能(后端,改update)

好家伙, 代码已开源(Gitee) PH-planewar: 个人开发的全栈小游戏 前端:vue2 + element-ui 后端: Springboot + mybatis-plus 数据库: mysql 目前实现功能: 1.注册登陆 2.游戏数据保存 3.游戏运行 (gitee.com) 后端这

SpringBoot2.7升级到3.0的实践分享

背景 最近把项目中的技术框架做一次升级,最重要的就是SpringBoot从2.7.x升级到3.0.x,当然还会有一些周边的框架也会连带着升级,比如Mybatis Plus,SpringCloud等,话不多说直接看看有哪些事情要做。 具体事项 主要分两类,第一类是单纯的提升版本,主要如下: 1.jdk

多租户基于Springboot+MybatisPlus实现使用一个数据库一个表 使用字段进行数据隔离

# 多租户实现方式 ```properties 多租户在数据存储上主要存在三种方案,分别是: 1. 独立数据库 即一个租户一个数据库,这种方案的用户数据隔离级别最高,安全性最好,但成本较高。 优点:为不同的租户提供独立的数据库,有助于简化数据模型的扩展设计,满足不同租户的独特需求;如果出现故障,恢复