⭐优雅解决数据库类型跟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
相关推荐
prinrf('千寻)2 分钟前
MyBatis-Plus 的 updateById 方法不更新 null 值属性的问题
java·开发语言·mybatis
老华带你飞8 分钟前
实习记录小程序|基于SSM+Vue的实习记录小程序设计与实现(源码+数据库+文档)
java·数据库·spring boot·小程序·论文·毕设·实习记录小程序
在未来等你36 分钟前
互联网大厂Java求职面试:AI与大模型应用集成及云原生挑战
java·微服务·ai·kubernetes·大模型·embedding·spring ai
my_styles42 分钟前
docker-compose部署项目(springboot服务)以及基础环境(mysql、redis等)ruoyi-ry
spring boot·redis·后端·mysql·spring cloud·docker·容器
源码技术栈1 小时前
SaaS基于云计算、大数据的Java云HIS平台信息化系统源码
java·大数据·云计算·云his·his系统·云医院·区域his
编程、小哥哥1 小时前
互联网大厂Java面试:从Spring Boot到微服务架构的技术深挖
java·spring boot·redis·微服务·prometheus·面试技巧
揽你·入怀1 小时前
数据结构:ArrayList简单实现与常见操作实例详解
java·开发语言
okok__TXF1 小时前
SpringBoot3+AI
java·人工智能·spring
AA-代码批发V哥1 小时前
Math工具类全面指南
java·开发语言·数学建模
免檒2 小时前
go语言协程调度器 GPM 模型
开发语言·后端·golang