类型处理器 TypeHandler
一、什么是类型处理器?
用于 JavaType 与 JdbcType 之间的转换,用于 PreparedStatement 设置参数值和从 ResultSet 或 CallableStatement 中取出一个值。
MyBatis 内置了大部分基本类型的类型处理器,所以对于基本类型可以直接处理,当我们需要处理其他类型的时候就需要自定义类型处理器。
如:一个 List<String>
类型的 JavaType ,在存储时,需要转换为以逗号分隔的VARCHAR 类型,在查询的时候,又要返回 List<String>
。
二、MyBatis 内置的 TypeHandler
在 MyBatis 的 TypeHandlerRegistry 类型中,可以看到内置的类型处理器。内置处理器比较多,这里整理常见的一些。
TypeHandler | 描述 |
---|---|
BooleanTypeHandler | 用于 java 类型 boolean,jdbc 类型 bit、boolean |
ByteTypeHandler | 用于 java 类型 byte,jdbc 类型 TINYINT |
ShortTypeHandler | 用于 java 类型 short,jdbc 类型 SMALLINT |
IntegerTypeHandler | 用于 INTEGER 类型 |
LongTypeHandler | 用于 long 类型 |
FloatTypeHandler | 用于 FLOAT 类型 |
DoubleTypeHandler | 用于 double 类型 |
StringTypeHandler | 用于 java 类型 string,jdbc 类型 CHAR、VARCHAR |
ArrayTypeHandler | 用于 jdbc 类型 ARRAY |
BigDecimalTypeHandler | 用于 java 类型 BigDecimal,jdbc 类型 REAL、DECIMAL、NUMERIC |
DateTypeHandler | 用于 java 类型 Date,jdbc 类型 TIMESTAMP |
DateOnlyTypeHandler | 用于 java 类型 Date,jdbc 类型 DATE |
TimeOnlyTypeHandler | 用于 java 类型 Date,jdbc 类型 TIME |
注意: 对于常见的 Enum 类型,内置了 EnumTypeHandler 进行 Enum 名称的转换和 EnumOrdinalTypeHandler 进行 Enum 序数的转换。这两个类型处理器没有在 TypeHandlerRegistry 中注册,如果需要使用必须手动配置。
三、自定义 TypeHandler
自定义类型处理器是通过实现 org.apache.ibatis.type.TypeHandler 接口实现的。这个接口定义了类型处理器的基本功能,接口定义如下所示。
public interface TypeHandler<T> {
// 用于把 java 对象设置到 PreparedStatement 的参数中
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
// 用于从 ResultSet 中根据列名取出数据转换为 java 对象
T getResult(ResultSet rs, String columnName) throws SQLException;
// 用于从 ResultSet 中根据索引位置取出数据转换为 java 对象
T getResult(ResultSet rs, int columnIndex) throws SQLException;
// 用于从 CallableStatement 中根据存储过程取出数据转换为 java 对象
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
实际开发中,我们可以继承 org.apache.ibatis.type.BaseTypeHandler 类型来实现自定义类型处理器。这个类型是抽象类型,实现了 TypeHandler 的方法进行通用流程的封装,做了异常处理,并定义了几个类似的抽象方法,如下所示。继承 BaseTypeHandler 类型可以极大地降低开发难度。
类型转换器还可以通过注解配置 java 类型和 jdbc 类型:
@MappedTypes:注解配置 java 类型
@MappedJdbcTypes:注解配置 jdbc 类型
把TypeHandler配置到程序中有三种方法:
- 在 Mapper.xml 中声明
<resultMap id="BaseResultMap" type="com.lizq.springboot.web.bean.User">
<result column="phones" jdbcType="VARCHAR" property="phones" typeHandler="com.xxx.handler.ListTypeHandler" />
</resultMap>
- 在 mybatis 配置文件中设置
<typeHandlers>
<typeHandler handler="com.xxx.handler.ListTypeHandler"/>
</typeHandlers>
- 在 springboot 的 yml 配置文件中设置类型处理器所在的包名
mybatis:
type-handlers-package: com.xxx.handler
四、自定义List和VARCHAR相互转换的TypeHandler
实现List到VARCHAR的转换。
比如:User 中有个属性的类型是 List 类型,对应的保存到数据库中的字段为VARCHAR 类型。要通过 TypeHandler 在插入的时候把List类型数据转换为String 类型处理,在查询的时候要把查询得到的字符串转换为 List 类型。
1. 继承BaseTypeHandler实现自定义TypeHandler
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
public class ListTypeHandler extends BaseTypeHandler<List<String>> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, String.join(",", parameter));
}
@Override
public List<String> getNullableResult(ResultSet rs, String columnName) throws SQLException {
String s = rs.getString(columnName);
return s == null ? null : Arrays.asList(s.split(","));
}
@Override
public List<String> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String s = rs.getString(columnIndex);
return s == null ? null : Arrays.asList(s.split(","));
}
@Override
public List<String> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String s = cs.getString(columnIndex);
return s == null ? null : Arrays.asList(s.split(","));
}
}
2. 对应实体类
import lombok.Data;
import java.util.List;
@Data
public class User {
private String id;
private String name;
private int age;
private String sex;
// 电话号码
private List<String> phones;
}
3. 编写Mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.springboot.Mapper.UserMapper">
<resultMap id="user" type="com.springboot.bean.User">
<result column="id" jdbcType="VARCHAR" property="id" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="age" jdbcType="INTEGER" property="age" />
<result column="sex" jdbcType="VARCHAR" property="sex" />
<!--配置typeHandler-->
<result column="phones" jdbcType="VARCHAR" property="phones" typeHandler="com.lizq.springboot.web.typehandler.ListTypeHandler" />
</resultMap>
<insert id="insertUser">
INSERT INTO user(id, name, age, sex, phones
) VALUES(#{id}, #{name}, #{age}, #{sex},
/*配置typeHandler*/
#{phones, jdbcType=VARCHAR ,typeHandler=com.lizq.springboot.web.typehandler.ListTypeHandler})
</insert>
<select id="getUserById" resultMap="user">
SELECT * FROM user WHERE id = #{id}
</select>
</mapper>