⭐优雅解决数据库类型跟Java对象属性类型不对应!

什么是数据库类型跟Java对象属性类型不对应

举例说一下: 数据库表中有一个名为 "age" 的列,其类型为 VARCHAR,存储了年龄信息,但在 Java 对象的类定义中,本应该对应的属性类型是String或者object,但是实际对应属性的类型是 Integer,就会出现类型转换错误的问题。

关于数据库类型跟Java对象属性类型不对应的解决办法: 通过设置一个处理类继承BaseTypeHandler这个类,然后配置相对应的转换方法。

其中,BaseTypeHandler实现了TypeHandler(字段类型处理器)接口,

什么是TypeHandler接口呢?

TypeHandler

TypeHandler就是应用程序和数据库之间的拦截器,所有的操作,都会先通过TypeHandler处理。

作用:处理 Java 对象和数据库类型之间的转换,通过实现这个接口来完成自定义类型处理器

BaseTypeHandler

BaseTypeHandler定义了一些默认的转换逻辑,属于类型转换类型实现需要的基础东西。比如:

arduino 复制代码
setParameter :将 Java 对象转换为数据库参数。

getResult :从数据库结果集中提取并转换数据为 Java 对象。

通过继承 BaseTypeHandler,通过继承 BaseTypeHandler,你只需关注具体类型的转换逻辑,无需实现所有的方法,可以更便捷地实现自定义的类型处理器。

举例了解一下

先定义一个拓展BaseTypeHandler的处理器,担当的角色是抽象的数组类型处理器, 负责数组对象到 JSON 字符串的转换。

ArrayObjectJsonTypeHandler

typescript 复制代码
@Slf4j
//@MappedJdbcTypes 是 MyBatis 中的注解,指定了该类型处理器与数据库的关联。
@MappedJdbcTypes(value = {JdbcType.OTHER}, includeNullJdbcType = true)
//用于处理数组对象的类型。
public class ArrayObjectJsonTypeHandler<E> extends BaseTypeHandler<E[]> {
    //创建一个 Jackson 的 ObjectMapper 对象,用于进行对象与 JSON 字符串的序列化和反序列化
    private static final ObjectMapper MAPPER = new ObjectMapper();
    // 定义一个字符串常量,表示空数组的 JSON 字符串
    private static final String STRING_JSON_ARRAY_EMPTY = "[]";

    //类加载时执行
    static {
        // 未知字段忽略
        MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 不使用科学计数
        MAPPER.configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true);
        // null 值不输出(节省内存)
        MAPPER.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL);
    }
//数组的类型
    private final Class<E[]> type;

    //类的构造函数,接收一个数组的类型参数,并将其赋值给 type 变量
    public ArrayObjectJsonTypeHandler(Class<E[]> type) {
        Objects.requireNonNull(type);
        this.type = type;
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, E[] parameter, JdbcType jdbcType) throws SQLException {
       //转为JSON字符串
        ps.setString(i, toJson(parameter));
    }

    @Override
    public E[] getNullableResult(ResultSet rs, String columnName) throws SQLException {
        //JSON字符串转对应Java类型,然后返回(取结果的过程)
        return toObject(rs.getString(columnName), type);
    }

    @Override
    public E[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return toObject(rs.getString(columnIndex), type);
    }

    @Override
    public E[] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return toObject(cs.getString(columnIndex), type);
    }

    /**
     * object 转 json
     *
     * @param obj 对象
     * @return String json字符串
     */
    private String toJson(E[] obj) {
        if (ArrayUtils.isEmpty(obj)) {
            return STRING_JSON_ARRAY_EMPTY;
        }

        try {
            //将给定的 Java 对象 obj 序列化为对应的 JSON 字符串。
            return MAPPER.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("mybatis column to json error,obj:" + Arrays.toString(obj), e);
        }
    }

    /**
     * 转换对象
     *
     * @param json  json数据
     * @param clazz 类
     * @return E
     */
    private E[] toObject(String json, Class<E[]> clazz) {
        if (json == null) {
            return null;
        }

        if (!StringUtils.hasText(json)) {
            return newArray(clazz);
        }

        try {
            //将给定的 JSON 字符串 json 反序列化为指定类型 clazz 的对象
            return MAPPER.readValue(json, clazz);
        } catch (JsonProcessingException e) {
            log.error("mybatis column json to object error,json:{}", json, e);
            return newArray(clazz);
        }
    }

    //@SuppressWarnings("unchecked") 注解用于压制类型转换警告
    //就是告诉编译器我这些类型转换是安全的,可以忽略调用 Array.newInstance 方法时的类型转换警告。
    @SuppressWarnings("unchecked")
    private E[] newArray(Class<E[]> clazz) {
        return (E[]) Array.newInstance(clazz.getComponentType(), 0);
    }
}

主要作用是数组类型转换json

大致解释下:首先定义了Jackson的ObjectMapper对象,用来进行对象与JSON字符串的序列化和反序列化

静态代码块进行了一些mapper的配置。

然后写了toObject和toJSON这两个方法(为当前类的自定义方法,不是重写方法),是JSON字符 串和数组类型的相互转换。

之后在重写方法里面进行使用,其中:

setNonNullParameter的作用是将 Java 对象类型的值转换为数据库可接受的类型,以便进行数据存储。

getNullableResult 的作用是将从数据库中获取的特定类型的值转换为 Java 对象类型。

@MappedJdbcTypes 注解在 ArrayObjectJsonTypeHandler 类上的作用是取未知或不常见的数据库类型数据时,会使用这个处理器。

这样,一个数组类型转json的类型处理器就弄好了,这时如果我们想把Integer数组转json,就很简单了

看下面代码:

IntegerArrayJsonTypeHandler

less 复制代码
@Slf4j
@Component
@MappedTypes(value = {Integer[].class})
@MappedJdbcTypes(value = {JdbcType.VARCHAR}, includeNullJdbcType = true)
public class IntegerArrayJsonTypeHandler extends ArrayObjectJsonTypeHandler<Integer> {
    public IntegerArrayJsonTypeHandler() {
        super(Integer[].class);
    }
}

主要看这两个注解:@MappedJdbcTypes(value = {JdbcType.VARCHAR}, includeNullJdbcType = true)指定数据库类型和varchar类型的映射关系。就是说Mybatis根据注解指定的varchar类型,当从数据库中获取这种类型的值,会使用这个处理器进行转换。

@MappedTypes(value = {Integer[].class}) 指定Integeter[] 类型对象和相应的数据库类型之间的映射关系。就是说把这个Integer[]类型对象存入数据库时,会使用这个处理器进行转换。

总结

自定义类型转换器继承BaseTypeHandler解决类型转换问题,其中

  • setNonNullParameter是进行转换为对应数据库类型
  • getNonNullParameter是取数据库数值时转换为Java对象类型
  • @mapperType是用于setNonNullParameter
  • @mapperJDBCType用于getNonNullParameter
相关推荐
ok!ko3 小时前
设计模式之原型模式(通俗易懂--代码辅助理解【Java版】)
java·设计模式·原型模式
2401_857622663 小时前
SpringBoot框架下校园资料库的构建与优化
spring boot·后端·php
2402_857589364 小时前
“衣依”服装销售平台:Spring Boot框架的设计与实现
java·spring boot·后端
吾爱星辰4 小时前
Kotlin 处理字符串和正则表达式(二十一)
java·开发语言·jvm·正则表达式·kotlin
哎呦没5 小时前
大学生就业招聘:Spring Boot系统的架构分析
java·spring boot·后端
_.Switch5 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
编程、小哥哥5 小时前
netty之Netty与SpringBoot整合
java·spring boot·spring
IT学长编程6 小时前
计算机毕业设计 玩具租赁系统的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·课程设计·毕业论文·计算机毕业设计选题·玩具租赁系统
莹雨潇潇6 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
杨哥带你写代码7 小时前
足球青训俱乐部管理:Spring Boot技术驱动
java·spring boot·后端