【解决方案】基于数据库驱动的自定义 TypeHandler 处理器

news/2024/10/9 8:37:43

目录
  • 前言
  • 一、TypeHandler 简介
    • 1.1转换步骤
    • 1.2转换规则
  • 二、JSON 转换
  • 三、枚举转换
  • 四、文章小结

前言

笔者在最近的项目开发中,频繁地遇到了 Java 类型与 JDBC 类型之间的2个转换问题:

  • 数据库的 varchar 类型字段,需要存储 Java 实体中的 JSON 字符串
  • 数据库的 int 类型字段,需要存储 Java 实体中的 Enum 枚举

其实要处理也不麻烦,可以在每次入库地方的手动将 Java Bean 调用 JSON.toJSONString() 即可,取出数据库数据的时候再 JSON.parseObject()解析。再说处理枚举类型也并不难,无非就是手动将枚举的 int 型属性取出后 set 到数据库的int中去。

而本文要介绍的自定义 TypeHandler 处理器的作用,就是自动处理 Java Bean 与数据库类型的转换,提高编码效率,通过全局的统一处理省去繁琐的手动转换。


一、TypeHandler 简介

如果我们使用的是 Mybatis 或者是 Mybatis Plus 的话,在 SQL 语句执行过程中,无论是设置参数还是获取结果集,都需要通过 TypeHandler 进行类型转换。

MyBatis 提供了丰富的内置 TypeHandler 实现,以支持常见的数据类型转换,如以下几种:

表1-1

1.1转换步骤

当 MyBatis 执行一个预编译的 SQL 语句(如 INSERT、UPDATE 等)时,它需要将 Java 对象中的属性值设置到 SQL 语句中对应的占位符上。这个过程就是通过TypeHandler 来实现的。

具体步骤如下:

  • MyBatis 会根据映射配置找到对应的 TypeHandle r实例,这个映射配置可以在 MyBatis 的配置文件或者 Mapper 的 XML 文件中定义;
  • TypeHandler 实例会接收到 Java 对象中的属性值,并将其转换为 JDBC 能够识别的类型,这个转换过程是根据两者之间的映射关系来实现的;
  • 转换后的值会被设置到 PreparedStatement 对象中对应的占位符上,以便数据库能够正确解析和执行 SQL 语句。

1.2转换规则

再次强调,TypeHandler 的核心功能是实现 Java 类型和 JDBC 类型之间的映射和转换,这个映射和转换规则是根据 Java 类型和 JDBC 类型的特性和语义来定义的。

  • 对于基本数据类型(如 int、long、float等),MyBatis 提供了内置的 TypeHandler 实现,这些实现能够直接将 Java 基本数据类型转换为对应的 JDBC 基本数据类型,反之亦然。
  • 对于复杂数据类型(如自定义对象、集合等),MyBatis 允许开发者自定义 TypeHandler 来实现复杂的类型转换逻辑。例如,开发者可以定义一个自定义的TypeHandler 来将数据库中的 JSON 字符串转换为 Java 中的对象,或者将 Java 对象转换为 JSON 字符串存储到数据库中。

下面两章就举两个例子来加以说明。


二、JSON 转换

应用的 .yml 配置文件新增以下:

mybatis-plus:type-handlers-package: #自定义 handler 类所在的包路径
/*** <p>作用:即 Java 实体属性可以直接使用 JSONObject 映射数据库的 varchar,方便入库、出库</p>* <p>注意:需要在 .yml 配置文件上加上 {@code mybatis:type-handlers-package: 本类所在包路径}</p>** @param <T> 该泛型即为需要转换成 varchar 的 Java 对象* @MappedTypes 注解很关键,指定了映射的类型*/
@MappedTypes({JSONObject.class, JSONArray.class})
public class JSONTypeHandler <T> extends BaseTypeHandler<T> {@Overridepublic void setNonNullParameter(PreparedStatement preparedStatement, int i, T param, JdbcType jdbcType) throws SQLException {//将指定的参数设置为给定的 Java String 值,数据库驱动程序及其转换成 varchar 类型preparedStatement.setString(i, JSON.toJSONString(param));}@Overridepublic T getNullableResult(ResultSet resultSet, String columnName) throws SQLException {//这里根据字段名去拿到之前放进来的 jsonStr 值String jsonStr = resultSet.getString(columnName);return StringUtils.isNotBlank(jsonStr) ? JSON.parseObject(jsonStr, getRawType()) : null;}@Overridepublic T getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException {//这里是根据位置来确定字段,进而拿到该字段的值(之前放进来的 jsonStr)String jsonStr = resultSet.getString(columnIndex);return StringUtils.isNotBlank(jsonStr) ? JSON.parseObject(jsonStr, getRawType()) : null;}@Overridepublic T getNullableResult(CallableStatement callableStatement, int columnIndex) throws SQLException {//这里是根据SQL存储过程里的字段位置来拿字段的值String jsonStr = callableStatement.getString(columnIndex);return StringUtils.isNotBlank(jsonStr) ? JSON.parseObject(jsonStr, getRawType()) : null;}}

三、枚举转换

/*** <p>作用:将实体类中的枚举 code 映射为数据库的 int</p>* <p>注意:需要在 .yml 配置文件上加上 {@code mybatis:type-handlers-package: 本类所在包路径}</p>** @param <E> 该泛型即为需要处理的枚举对象,使用上界通配符来保证类型安全*/
@MappedTypes(MyEnum.class)
public class EnumCodeTypeHandler <E extends MyEnum> extends BaseTypeHandler<E> {private final Class<E> type;/*** 记录枚举值和枚举的对应关系*/private final Map<Integer, E> enumMap = new ConcurrentHashMap<>();public EnumCodeTypeHandler(Class<E> type) {Assert.notNull(type, "argument cannot be null");this.type = type;E[] enums = type.getEnumConstants();if (Objects.nonNull(enums)) {//这里将枚举值和枚举类型存入 enumMapfor (E e : enums) {this.enumMap.put(e.toCode(), e);}}}@Overridepublic void setNonNullParameter(PreparedStatement preparedStatement, int index, E e, JdbcType jdbcType) throws SQLException {//这里将枚举的 code 转为数据库该字段的 int 类型preparedStatement.setInt(index, e.toCode());}@Overridepublic E getNullableResult(ResultSet resultSet, String columnName) throws SQLException {//这里根据字段名来将数据库的 int 转为 Java 的 IntegerInteger code = resultSet.getInt(columnName);if (resultSet.wasNull()){return null;}else {//取出对应的枚举值return enumMap.get(code);}}@Overridepublic E getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException {//这里根据字段位置来将数据库的 int 转为 Java 的 IntegerInteger code = resultSet.getInt(columnIndex);if (resultSet.wasNull()){return null;}else {//取出对应的枚举值return enumMap.get(code);}}@Overridepublic E getNullableResult(CallableStatement callableStatement, int columnIndex) throws SQLException {//这里根据SQL存储过程里的字段位置将字段的 int 转为 Java 的 IntegerInteger code = callableStatement.getInt(columnIndex);if (callableStatement.wasNull()){return null;}else {//取出对应的枚举值return enumMap.get(code);}}}
/*** <p>作用:该接口包含了两个枚举操作的抽象方法</p>*/
public interface MyEnum {/*** 根据 code 获取枚举实例* @param code*/MyEnum fromCode(int code);/*** 获取枚举中的 code*/int toCode();}
@Getter
@RequiredArgsConstructor
public enum StudyStatusEnum implements MyEnum{ONE(1, "枚举1"),TWO(2, 枚举2"),THREE(3, "枚举3"),FOUR(4, "枚举4"),FIVE(5, "枚举5");private final Integer code;private final String desc;/*** 根据 code 获取枚举实例*/@Overridepublic MyEnum fromCode(int code) {return Arrays.stream(StudyStatusEnum.values()).filter(val -> val.getCode().equals(code)).findFirst().orElse(null);}/*** 获取枚举中的 code*/@Overridepublic int toCode() {return this.getCode();}}

四、文章小结

通过内置和自定义的 TypeHandler,我们可以轻松处理各种数据类型转换需求,提高开发效率和代码可维护性。

在 Spring Boot 环境中使用自定义 TypeHandler 更是简化了配置和注册过程,使得我们能够更专注于业务逻辑的实现。

最后,文章如有不足和错误,还请大家指正。或者你有其它想说的,也欢迎大家在评论区交流!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/69253.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

VMware ESXi 8.0U3 xFusion (超聚变) 定制版更新 OEM BIOS 2.7 支持 Windows Server 2025

VMware ESXi 8.0U3 xFusion (超聚变) 定制版更新 OEM BIOS 2.7 支持 Windows Server 2025VMware ESXi 8.0U3 xFusion (超聚变) 定制版更新 OEM BIOS 2.7 支持 Windows Server 2025 VMware ESXi 8.0U3 macOS Unlocker & OEM BIOS xFusion (超聚变) 定制版 ESXi 8.0U3 标准版…

VMware ESXi 8.0U3 Huawei (华为) 定制版更新 OEM BIOS 2.7 支持 Windows Server 2025

VMware ESXi 8.0U3 Huawei (华为) 定制版更新 OEM BIOS 2.7 支持 Windows Server 2025VMware ESXi 8.0U3 Huawei (华为) 定制版更新 OEM BIOS 2.7 支持 Windows Server 2025 VMware ESXi 8.0U3 macOS Unlocker & OEM BIOS Huawei (华为) 定制版 ESXi 8.0U3 标准版,Dell (戴…

VMware ESXi 8.0U3 HPE (慧与) 定制版更新 OEM BIOS 2.7 支持 Windows Server 2025

VMware ESXi 8.0U3 HPE (慧与) 定制版更新 OEM BIOS 2.7 支持 Windows Server 2025VMware ESXi 8.0U3 HPE (慧与) 定制版更新 OEM BIOS 2.7 支持 Windows Server 2025 VMware ESXi 8.0U3 macOS Unlocker & OEM BIOS HPE (慧与) 定制版 ESXi 8.0U3 标准版,Dell (戴尔)、HPE…

ASP.NET Core OData 9的发布,放弃 .NET Framework

Microsoft 于 2024 年 8 月 30 日宣布推出 ASP.NET Core OData 9 包。 这个新包将ASP.NET Core与.NET 8 OData库保持一致,改变了OData格式中数据编码的内部细节,使其更符合OData 规范。在2024年8月早些时候,Microsoft 将 OData .NET 库更新到版本 8.0.0。其中最重要的更改是…

读数据工程之道:设计和构建健壮的数据系统03数据工程生命周期(上)

数据工程生命周期(上)1. 数据工程生命周期 1.1. 数据领域正在经历新数据技术和实践的爆炸式增长,抽象程度和易用性不断提高 1.2. 由于技术抽象程度的增加,数据工程师将越来越多地成为数据生命周期工程师,根据数据生命周期管理的原则来进行思考和操作 1.3. 数据工程生命周期…

2024年9月学习月报

一、学习目标学习 VLM 的基本原理和架构,理解视觉和语言信息的融合方式,掌握 VLM 的训练方式与评估方法。 学习 VLM 在遥感领域的应用(RemoteCLIP、ChangeCLIP),并尝试本地复现。二、学习内容 文献 An Introduction to Vision-Language Modeling VLM 按照训练方式可以分为…

31. 数据库基础

1. 数据库基础知识 1.1 关系型数据库与非关系型数据库1.2 关系型数据库的结构 库 Database 库,也称数据库,用于组织、存储和管理数据 类比于文件夹 表 Table 表,是数据库中基本的数据存储单位,由行(Row)和列(Column)组成 类比于excel文件 记录 Record 记录,是表中的一…

KeyShot基础操作2 - 材质篇

介绍了KeyShot的材质相关的内容:上材质、材质参数、贴图类型、映射类型、材质节点图等。​这部分基础操作,只是介绍KeyShot的操作方法,望知晓。 后续也会再更新材质、打光的案例,同时也会提供对应的工程文件。上材质基础操作 材质的通用参数 材质类型 纹理类型 多层材质 贴…