⭐优雅解决数据库类型跟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
相关推荐
devlei5 小时前
从源码泄露看AI Agent未来:深度对比Claude Code原生实现与OpenClaw开源方案
android·前端·后端
pshdhx_albert5 小时前
AI agent实现打字机效果
java·http·ai编程
沉鱼.446 小时前
第十二届题目
java·前端·算法
努力的小郑6 小时前
Canal 不难,难的是用好:从接入到治理
后端·mysql·性能优化
赫瑞7 小时前
数据结构中的排列组合 —— Java实现
java·开发语言·数据结构
Victor3567 小时前
MongoDB(87)如何使用GridFS?
后端
Victor3567 小时前
MongoDB(88)如何进行数据迁移?
后端
小红的布丁7 小时前
单线程 Redis 的高性能之道
redis·后端
GetcharZp7 小时前
Go 语言只能写后端?这款 2D 游戏引擎刷新你的认知!
后端
周末也要写八哥8 小时前
多进程和多线程的特点和区别
java·开发语言·jvm