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

需求:字典实现类似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<?>) {
            obj = ((MapperMethod.ParamMap<?>) param).getOrDefault(Constants.ENTITY, null);
            if (obj == null) {
                return;
            }
        } else {
            obj = param;
        }

        fields = obj.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());
            }

        }
    }

}

返回结果转译

@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;
    }
}
posted @ 2024-05-15 10:09  IAyue  阅读(58)  评论(0编辑  收藏  举报