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

x33g5p2x  于6个月前 转载在 AI智能  
字(6.4k)|赞(0)|评价(0)|浏览(188)

需求:字典实现类似mybatis-plus中@EnumValue的功能,假设枚举类中应用使用code,数据库存储对应的value

公用的字典相关定义

@TableName("t_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;
    }
}

对请求参数的处理

做法一:Mybatis支持对Executor、StatementHandler、PameterHandler和ResultSetHandler进行拦截,也就是说会对这4种对象进行代理。mybatis-plus实际上也是通过mybatis提供的拦截功能进行封装,我们在对数据库进行insert\query\update操作时,利用mybatis提供的拦截器对字典做转换

这种实现方式有种问题,在进行insert\query\update操作后,entity内被标记的字段会被替换成对应的字典值,如果后续直接使用操作后返回的entity对象会出现异常

@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());
            }

        }
    }

}

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

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

        return mybatisPlusInterceptor;
    }

做法二:通过typehandler做转换,这种方式需要在entity中使用@TableField(typeHandler = UserRoleHandler.class)对字段进行标记

public class UserRoleHandler implements TypeHandler<String> {

    @SneakyThrows
    @Override
    public void setParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {
        preparedStatement.setObject(i, transClassFieldToValue(s));
    }

    @SneakyThrows
    @Override
    public String getResult(ResultSet resultSet, String s) throws SQLException {
        return transClassFieldToCode(s);
    }

    @SneakyThrows
    @Override
    public String getResult(ResultSet resultSet, int i) throws SQLException {
        return transClassFieldToCode(resultSet.getString(i));
    }

    @SneakyThrows
    @Override
    public String getResult(CallableStatement callableStatement, int i) throws SQLException {
        return transClassFieldToCode(callableStatement.getString(i));
    }

    private String transClassFieldToCode(String parameter) throws Exception {
        DictionaryService dictionaryService = SpringUtil.getBean(DictionaryService.class);

        return dictionaryService.getByValue(DictionaryType.USER_ROLE, parameter).getCode();
    }

    private String transClassFieldToValue(String param) throws IllegalAccessException {
        DictionaryService dictionaryService = SpringUtil.getBean(DictionaryService.class);

        return dictionaryService.getByCode(DictionaryType.USER_ROLE, param).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;
    }

本文来自博客园,作者:IAyue,转载请注明原文链接:https://www.cnblogs.com/zmj-pr/p/18176623

相关文章