什么是数据库类型跟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