Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >自定义MyBatis通用枚举类型处理器 --- 是真的通用

自定义MyBatis通用枚举类型处理器 --- 是真的通用

原创
作者头像
用户9867745
修改于 2024-02-04 02:56:13
修改于 2024-02-04 02:56:13
5050
举报

自定义 MyBatis 通用枚举类型解析器

在使用MyBatis的过程中,我们经常会使用到枚举类型的数据, 一般在保存数据时只是想将枚举类型的code值存入到数据库中,查询时希望能自动根据code值映射出对应的枚举对象出现,而不是查询出code值然后再手动根据code值找到对应的枚举对象的转换

官方注册方案

官方方案:https://mybatis.org/mybatis-3/zh_CN/configuration.html#typeHandlers 无法对所有枚举类型进行通用注册(有可能是没找到正确的方式,如果有,恳请大家指导)

自动注册方案

实现思路如下:

1. 自定义注解用于标识枚举字段code值(可以使用Jackson自带的@JsonValue注解,也可以单独自定义注解),注解标识的字段类型非固定类型,可为`Integer`、`Long`、`String`等其他基本类型或其他类型(其他类型请多测试) 2. 自定义枚举类型处理器MyBatisEnumTypeHandler.java 继承自org.apache.ibatis.type.BaseTypeHandler,用于处理枚举类型数据的保存和查询使用

代码语言:java
AI代码解释
复制
@Slf4j
public class MyBatisEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {

    private final Class<E> type;
    public MyBatisEnumTypeHandler(Class<E> type) {
        this.type = type;
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
        try {
            Field field = EnumValueMarkerFinder.find(type);
            Object val = field.get(parameter);
            if (jdbcType == null) {
                ps.setObject(i, val);
            } else {
                ps.setObject(i, val, jdbcType.TYPE_CODE);
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
        Object s = rs.getObject(columnName);
        return findTargetEnum(s, type);
    }

    @Override
    public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        Object s = rs.getObject(columnIndex);
        return findTargetEnum(s, type);
    }

    @Override
    public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        Object s = cs.getObject(columnIndex);
        return findTargetEnum(s, type);
    }

    private E findTargetEnum(Object val, Class<E> type) {
        if (val == null) {
            return null;
        }
        try {
            Field field = EnumValueMarkerFinder.find(type);
            for (E enumConstant : type.getEnumConstants()) {
                Object o = field.get(enumConstant);
                if (val.equals(o)) {
                    return enumConstant;
                }
            }
        } catch (IllegalAccessException e) {
            log.error("Handle enum failed...", e);
        }
        return null;
    }
}

3. 接下来,怎么将自定义的枚举类型处理器用于处理所有枚举类型的数据?

4. 为了实现所有的枚举都自动注册通用类型转换器,这里需要自定义一个配置类CustomizeMyBatisConfiguration.java并实现org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer接口

1. 实现该接口后,可以获取到org.apache.ibatis.session.Configuration配置类, 2. 使用Configuration配置类获取到`TypeHandlerRegistry`注册器, 3. 再使用TypeHandlerRegistry注册器将需要处理的枚举类类型解析器注册进去

代码语言:java
AI代码解释
复制
public class CustomizeMyBatisConfiguration implements ConfigurationCustomizer{ 
    public void customize(Configuration configuration) {
        // 将自定义的通用枚举类型处理器`MyBatisEnumTypeHandler`注册进去
        // Class clazz = null; // 怎么获取到需要处理的枚举类,即字段中标了@JsonValue注解或自定义注解的枚举类? 
        configuration.getTypeHandlerRegistry().register(clazz, new MyBatisEnumTypeHandler<>(clazz));
    }
}

5. 获取所有需要注册到通用枚举类型处理器中的枚举类 1. 在 customize 方法中通过Spring框架中ClassPathScanningCandidateComponentProvider扫描器在`classpath`下扫描出指定包下的枚举类 2. 自定义一个类型过滤器com.kws.mybatis.config.CustomizeMyBatisConfiguration.EnumTypeFilter,用于在类路径扫描时,过滤出需要处理的枚举类(1.枚举类型 2.枚举类型中含有自定义注解字段)

代码语言:java
AI代码解释
复制
public static class EnumTypeFilter implements TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) {
        String typeName = metadataReader.getClassMetadata().getSuperClassName();
        if (!ENUM_TYPE.equals(typeName)) {
            return false;
        }
        try {
            Class<?> clazz = ClassUtils.forName(metadataReader.getClassMetadata().getClassName(), getClass().getClassLoader());
            return EnumValueMarkerFinder.hasAnnotation(clazz);
        } catch (ClassNotFoundException e) {
            log.error("EnumTypeFilter match failed. Class not found: " + metadataReader.getClassMetadata(), e);
        }
        return false;
    }
}

3. 过滤出需要处理的枚举类后,通过`TypeHandlerRegistry`将当前枚举类型使用通用的枚举类型处理器注册到类型处理器中 4. 具体注册代码如下

代码语言:java
AI代码解释
复制
@Slf4j
@Component
public class CustomizeMyBatisConfiguration implements ConfigurationCustomizer {
    /**
    * 可改成读取配置文件包路径.
    * 注意:
    * 如果需要从配置文件读取,直接通过@Value注解注入不会生效,
    * 需要实现EnvironmentAware接口,通过EnvironmentAware接口获取配置
    */
    private static final String BASE_SCAN_PACKAGE = "com.kws";
    public static final String ENUM_TYPE = "java.lang.Enum";

    @Override
    @SuppressWarnings({"unchecked", "rawtypes"})
    public void customize(Configuration configuration) {
        ClassPathScanningCandidateComponentProvider classPathScanning = new ClassPathScanningCandidateComponentProvider(false);
        classPathScanning.addIncludeFilter(new EnumTypeFilter());
        Set<BeanDefinition> enumsBeanDefinitions = classPathScanning.findCandidateComponents(BASE_SCAN_PACKAGE);
        if (CollectionUtils.isEmpty(enumsBeanDefinitions)) {
            return;
        }

        for (BeanDefinition bd : enumsBeanDefinitions) {
            try {
                log.info("====== register TypeHandler for Enum ======【{}】", bd.getBeanClassName());
                Class clazz = ClassUtils.forName(Objects.requireNonNull(bd.getBeanClassName()), getClass().getClassLoader());
                configuration.getTypeHandlerRegistry().register(clazz, new MyBatisEnumTypeHandler<>(clazz));
            } catch (Exception e) {
                log.error("====== Register Mybatis TypeHandler Failed. Enum:【{}】", bd.getBeanClassName(), e);
            }
        }
    }

    /**
    * 自定义枚举类型过滤器 <p>
    * 1.过滤枚举类型 <p>
    * 2.枚举类型字段必须打了枚举类型注解(或自定义注解) <p>
    *
    * @author kws
    * @date 2024-01-14 17:19
    */
    public static class EnumTypeFilter implements TypeFilter {
        @Override
        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) {
            String typeName = metadataReader.getClassMetadata().getSuperClassName();
            if (!ENUM_TYPE.equals(typeName)) {
                return false;
            }
            try {
                Class<?> clazz = ClassUtils.forName(metadataReader.getClassMetadata().getClassName(), getClass().getClassLoader());
                return EnumValueMarkerFinder.hasAnnotation(clazz);
            } catch (ClassNotFoundException e) {
                log.error("EnumTypeFilter match failed. Class not found: " + metadataReader.getClassMetadata(), e);
            }
            return false;
        }
    }
}

完整代码已发布github

github: enum-mapping 模块:`enum-mapping-mybatis`

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
LeetCode-面试题14-1-剪绳子
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m] 。请问 k[0]k[1]...k[m] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
benym
2022/07/14
2550
笑不活了,银行赞助的 LeetCode 周赛,题目是适合打劫银行的日子
今天 LeetCode 的每日一题是第 2100 号问题:适合打劫银行的日子,我打开评论区一看,我也笑不活了。
五分钟学算法
2022/04/08
4310
笑不活了,银行赞助的 LeetCode 周赛,题目是适合打劫银行的日子
剑指 Offer 14- I. 剪绳子
大家好,我是程序员吴师兄,欢迎来到 图解剑指 Offer 结构化专栏,在这个专栏里我将和大家一起学习如何用结构化的思维来思考、解题、写代码,希望能帮助你即使在面试的时候紧张也能做对。
五分钟学算法
2021/04/20
7890
剑指 Offer 14- I. 剪绳子
【好书推荐】《剑指Offer》之硬技能(编程题12~16)
题目:请设计一个函数,用来判断一个矩阵中是否存在一条包含其字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。
用户1148394
2019/06/21
3770
ETAP软件–可靠性计算
各元件可靠性参数如下: 架空线路故障停运率(次/百公里) 55.865 架空线路停电平均持续时间(小时) 4.1622 断路器故障停运率(次/百台) 1.699 断路器停电平均持续时间(小时) 4.8864 开关故障停运率(次/百台) 54.677 开关停电平均持续时间(小时) 1.9361
全栈程序员站长
2022/09/15
5830
ETAP软件–可靠性计算
【TCP/IP】图解TCP的通信机制
TCP(Transmission Control Protocol)是传输控制协议,其作用于传输层,是一种提供了面向连接通信服务的协议
@零一
2021/01/29
1.5K0
LeetCode 160: 相交链表 Intersection of Two Linked Lists
Write a program to find the node at which the intersection of two singly linked lists begins.
爱写bug
2019/07/19
2970
新160个CrackMe分析-第2组:11-20(下)
Delphi程序,截图不方便注释,之后用IDR直接复制代码到everEdit里写注释了:
极安御信安全研究院
2022/09/08
5550
新160个CrackMe分析-第2组:11-20(下)
数据结构与算法 --- 复杂度分析专题(一)
算法复杂度分析的意义在于评估算法的执行效率,找出最优解决方案,是优化算法和改进程序性能的基础。通过对算法的时间复杂度和空间复杂度进行分析,可以帮助我们预估该算法运行所需的资源,从而提高程序的性能。
Niuery Diary
2023/10/22
3730
数据结构与算法 --- 复杂度分析专题(一)
我 AK 了一场欧洲的算法竞赛,直呼过瘾!
The Benelux Algorithm Programming Contest (BAPC) 是一项集中了来自卢森堡、比利时和荷兰的顶尖大学的约 50 支队伍的算法竞赛。
ACM算法日常
2021/11/17
7680
Android绘图最终篇之大战贝塞尔三次曲线
零、前言 1.可以说贝塞尔曲线是一把 "石中剑",能够拔出它,会让你的绘图如虎添翼。 2.今天要与贝塞尔曲线大战三百回合,将它加入我的绘图大军麾下。 3.自此Android绘图五虎将:Canvas,Path,Paint,Color,贝塞尔便集结完成。 4.本项目源码见文尾捷文规范第一条,视图源码在view包,分析工具在analyze包 ---- 一、贝塞尔三次曲线初体验 1.无网格,不曲线,废话不多说,上网格+坐标系 /** * 作者:张风捷特烈<br/> * 时间:2018/11/16 0
张风捷特烈
2018/11/21
8080
2023-09-01:用go语言编写。给出两个长度均为n的数组, A = { a1, a2, ... ,an }, B = {
第四行有4个整数La,Ra,Lb,Rb,范围在0到10^18之间,代表题目描述中的参数。
福大大架构师每日一题
2023/09/02
2610
2023-09-01:用go语言编写。给出两个长度均为n的数组, A = { a1, a2, ... ,an }, B = {
AcWing 562. 壁画(每日一题)
在接下来的每一天,他只能选择与绘制完成的墙面相邻的墙段进行作画,因为他不想分开壁画。
摆烂小白敲代码
2024/09/23
950
查表得省一!
空间、时间都是 O(1) 级别,打表法 YYDS,比赛必备的神器,想在比赛中得奖,还真得用这种技巧。
五分钟学算法
2022/04/08
5000
查表得省一!
https下不加www的301强制跳转
不少浏览器都开始逐渐更新至只支持https的网站,所以很多http网站都需要添加对https的支持,这时就需要涉及到www和不加www的跳转问题,由于www和不加www使用的是不同的证书,所以需要做301跳转处理,方案如下:
星哥玩云
2022/07/13
1.4K0
2015年ccf计算机职业认证资格考试第一题数列分段
  输入的第一行包含一个整数n,表示数列中整数的个数。   第二行包含n个整数a1, a2, …, an,表示给定的数列,相邻的整数之间用一个空格分隔。
glm233
2020/09/28
4280
一道非常经典的队列题
今天发一道剑指offer上面的经典题目,主要考察面试者的知识迁移能力,下面我们先来看一下题目描述。
灵魂画师牧码
2020/11/13
6070
一道非常经典的队列题
前端学数据结构与算法(一):不会复杂度分析,算法等于白学
兜兜转转了这么久,数据结构与算法始终是逃不过命题。曾几何时,前端学习数据结构与算法,想必会被认为不务正业,但现今想必大家已有耳闻与经历,面试遇到链表、树、爬楼梯、三数之和等题目已经屡见不鲜。想进靠谱大厂算法与数据结构应该不止是提上日程那么简单,可能现在已经是迫在眉睫。这次决定再写一个系列也只是作为我这段时间的学习报告,也不绝对不会再像我之前的vue原理解析那般断更了,欢迎大家监督~
飞跃疯人院
2020/10/07
9350
算法基础之复杂度表示
今天聊聊算法,算法作为开发过程中重要的一份子,是我们编码的基础,遇到问题如果没有好的算法解决,程序也就没有好的性能可言了。所以好的算法,能让代码更省时间和空间,那怎么去计算算法所占用的时间和空间呢?这也就是我们今天要重点说的东西了——空间复杂度和时间复杂度。
码上积木
2021/01/11
5550
丫丫考了100分_小明考了0分
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/135113.html原文链接:https://javaforall.cn
全栈程序员站长
2022/08/18
2030
推荐阅读
相关推荐
LeetCode-面试题14-1-剪绳子
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档