一、简介
不管是参数绑定,还是结果映射,mybatis 都要完成 Java 类型和 Jdbc 类型数据的相互转换,在框架中为了统一类型转换处理器的行为,定义了 TypeHandler
接口,并实现了多种不同的类型处理器。
二、TypeHandler 接口
TypeHandler
为所有类型转换器都要实现的接口,定义了 #setParameter()
方法来为SQL绑定参数,定义了重载的 #getResult()
方法来将结果映射为Java对象,接口定义如下:
public interface TypeHandler<T> {
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
【解析】
接口定义中的泛型 T 就表示 Java 类型,各个特定的转换器实现类根据转换的 Java 类型指定其具体类型。
#setParameter()
方法完成 javaType 到 jdbcType 的转换。PreparedStatement ps
表示一个预编译的SQL语句对象,int i
指定了绑定该语句中的参数的位置,T parameter
为要被转换的 Java 对象,JdbcType jdbcType
为目的转化类型,当传入的 Java 对象是空时,参数绑定需要调用 PreparedStatement .setNull()
方法,该方法的第二个参数即为 Jdbc 类型。
重载的三个 #getResult()
方法分别根据数据列名、列索引来将结果集中的数据转化为 Java 类型,完成 jdbcType 到 javaType 的转换。
public Role getRole(Long id) {
Connection connection = getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = connection.prepareStatement("select id, role_name, note from t_role where id = ?");
ps.setLong(1, id);
rs = ps.executeQuery();
while (rs.next()) {
Long roleId = rs.getLong("id");
String roleName = rs.getString("role_name");
String note = rs.getString("note");
Role role = new Role();
role.setId(id);
role.setRoleName(roleName);
role.setNote(note);
return role;
}
} catch (Exception e) {
Logger.getLogger(JdbcExample.class.getName()).log(Level.SEVERE, null, e);
} finally {
this.close(rs, ps, connection);
}
return null;
}
再次将 反射器模块 开头的这段JDBC代码拿出来回顾一下,前面的 ps.setLong(1, id)
就是将 javaType 转换为 jdbcType,而后面的 rs.getString("role_name")
就是将jdbcType 转换为 javaType,可以看出不管是 PreparedStatement 还是 ResultSet 的方法名都带有类型名,所以下面介绍的方法转换器的实现中要根据处理的不同目标类型,调用不同的 getType/setType
方法。
【注意】如果将一张数据表中的一行数据映射成一个Java对象,则Java对象中的每个成员属性跟表中的一行的每个列映射,上述的类型转换器,实际上是完成一个属性跟一个数据库字段列的映射,因为Java对象有多个成员,数据表有多个字段,所以实际映射一行数据的时候,要有多个类型转换器参与处理。上述的Java对象,实际是指一个成员属性对应的对象。
三、BaseTypeHandler
BaseTypeHandler
是一个承上启下的抽象类,它实现了 TypeHandler
接口,又被所有具体的转换器类所继承,因为SQL绑定参数时可能传入一个空的参数,结果映射时也可能获取到一个空的数据,这里需要一点特别的处理,这些都放在 BaseTypeHandler
中统一实现,简化定义类型转换器的工作。
BaseTypeHandler
封装了 Configuration
对象作为成员,该对象与 mybatis 全局配置中的配置信息映射,因为在 mybatis-config.xml 用户可以自定义类型转换器处理一些特殊的类型,比如枚举。
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
protected Configuration configuration;
public void setConfiguration(Configuration c) {
this.configuration = c;
}
// other code
}
BaseTypeHandler
还继承了一个带泛型的父类 TypeReference
,该类主要用来解析泛型的具体类型,后面会介绍。
1、void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)
【功能】使用 Java 类型对象为 SQL 绑定参数。
【源码与注解】
@Override
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
// (1)当Java对象为空时,调用 PreparedStatement.setNull() 绑定参数
if (parameter == null) {
// 上述方法需要确定JdbcType,所以如果jdbcType也为空,该方法无法执行,抛出异常
if (jdbcType == null) {
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
}
try {
ps.setNull(i, jdbcType.TYPE_CODE);
} catch (SQLException e) {
// 传入的JdbcType类型不合适,抛出异常
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
"Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
"Cause: " + e, e);
}
} else {
try {
// (2)调用类中定义的抽象方法,该方法在继承了BaseTypeHandler的子类中有具体且不同的实现
setNonNullParameter(ps, i, parameter, jdbcType);
} catch (Exception e) {
throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
"Try setting a different JdbcType for this parameter or a different configuration property. " +
"Cause: " + e, e);
}
}
}
public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
【解析】
- (1)当Java对象为空时,调用
PreparedStatement.setNull()
绑定参数,如果传入的 jdbcType 也为空则抛出异常,如果方法执行抛出异常,证明传入的 jdbcType 不合适。 - (2)调用类中定义的抽象方法,该方法在继承了BaseTypeHandler的子类中有具体且不同的实现。
2、T getResult(ResultSet rs, String columnName)
【功能】根据列名对 SQL 执行结果 ResultSet 对象进行映射成 Java 对象。
【源码与注解】
@Override
public T getResult(ResultSet rs, String columnName) throws SQLException {
T result;
try {
// 调用子类中实现的抽象方法获取结果值
result = getNullableResult(rs, columnName);
} catch (Exception e) {
throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e);
}
// 判断映射的结果集是否为空
if (rs.wasNull()) {
return null;
} else {
return result;
}
}
public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
【解析】
这里代码第一眼看有点费解,为什么先尝试从结果集中获取结果,再来判断结果集是否空呢?从 ResetSet.wasNull()
的注释中可以看出,需要调用 get 方法,才能使用该方法,所以这里要先尝试先调用 #getNullableResult()
获取结果,该方法的具体实现由各具体的转换器子类实现。
其他两个重名的重载方法跟上述方法类似,不再赘述。
四、各种类型转换器
TypeHandler
有很多类型转化器,下面分类说明。
1、基本数据类型对应的包装类型转换处理器
Java 中八种基本数据类型分别是 short、int、long、float、double、boolean、byte、char
,对应的包装类型分别为 Short、Integer、Long、Float、Double、Boolean、Byte、Character
,对应的类型转换器别分为 ShortTypeHandler、IntegerTypeHandler、LongTypeHandler、FloatTypeHandler、DoubleTypeHandler、BooleanTypeHandler、ByteTypeHandler、CharacterTypeHandler
,这几个转换器的实现都是类似的,只是调用 PreparedStatement/ResultSet/CallableStatement 对应的 set/get 方法有差异,以 IntegerTypeHandler 为例,源码如下:
public class IntegerTypeHandler extends BaseTypeHandler<Integer> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
throws SQLException {
ps.setInt(i, parameter);
}
@Override
public Integer getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return rs.getInt(columnName);
}
@Override
public Integer getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return rs.getInt(columnIndex);
}
@Override
public Integer getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return cs.getInt(columnIndex);
}
}
其他的 TypeHandler 比如 ShortTypeHandler 就是将 #getInt
改成 #getShort
,将 #setInt
改成 #setShort
,其他类型转换器以此类推。
2、字节、字符串及相关数组对应的类型转换处理器
在介绍这些转换处理器之前,有必要先了解关于字符、字符串相关的通用 SQL 类型,列表如下:
类型 | 用途 |
---|---|
char(n)/character(n) |
字符/字符串。固定长度n |
varchar(n)/character varying(n) |
字符/字符串。可变长度,最大长度n |
blob |
二进制形式的长文本数据 |
clob/text |
以字节为单位保存文本大对象 |
nclob |
以 unicode 字符为单位保存文本大对象 |
不同数据库数据类型的定义略有不同,不过大同小异,一般都有定长和变长字符类型可选,两者的区别在于,定长类型的数据存储前数据库就分配一块固定的存储空间了,而变长是实际用到多少分配多少。
mysql 和 oracle 都有 blob
的数据类型,用来存储二进制形式的长文本数据,最大存储空间不同数据库设计略有差异,为了满足不同大小的数据库存储的需求,mysql 还有 tinyblob
、mediumblob
、longblob
的数据类型,oracle 还有 bfile
满足超大数据块的存储需求。
对于存储文本形式的大数据块,mysql 对应的数据类型为 tinytext
、text
、mediumtext
、longtext
,而 oracle 则设计了 clob
、nclob
,clob
使用数据库编码的字符的定长字节存储文本,nclob
使用 unicode 编码的字符的定长字节存储文本。
(1)与 blob 相关的类型转换处理器
BlobTypeHandler / BlobByteObjectArrayTypeHandler / BlobInputStreamTypeHandler
public class BlobTypeHandler extends BaseTypeHandler<byte[]> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, byte[] parameter, JdbcType jdbcType)
throws SQLException {
// 将字节数组转化为流对象
ByteArrayInputStream bis = new ByteArrayInputStream(parameter);
ps.setBinaryStream(i, bis, parameter.length);
}
@Override
public byte[] getNullableResult(ResultSet rs, String columnName)
throws SQLException {
Blob blob = rs.getBlob(columnName);
byte[] returnValue = null;
if (null != blob) {
returnValue = blob.getBytes(1, (int) blob.length());
}
return returnValue;
}
// 另外两个方法类似
【解析】
参数绑定时,先将字节数组转化为流对象,再绑定参数,这样数据库在接收数据时是从流对象中读取,直到到达文件末尾,性能更佳;结果映射都是从结果集中获取到一个 Blob
对象,再从该对象中读取字节数组。
BlobByteObjectArrayTypeHandler
跟 BlobTypeHandler 的处理基本一样,只不过处理的java对象类型为 Byte[]
,所以处理之前将 Byte[] 转换为 byte[],结果映射时反过来将 byte[] 转换为 Byte[]。
BlobInputStreamTypeHandler
处理的java数据类型是 InputStream
,不过用的不是 #setBinaryStream()
,而是 #setBlob()
,这里有点疑惑,看 Javadoc 的解释是 #setBlob()
方法会通知驱动发送 Blob
形式的参数值给服务端;而 #setBinaryStream()
方法驱动处理时会有额外的工作决定发送 LONGVARBINARY
还是 Blob
形式的参数值给服务端。至于 LONGVARBINARY 和 Blob 有什么区别?这些类型在 Java 端可能就都是字节数组,但是在数据库驱动中应该进行了封装处理转化为不同的数据类型,但是我们无法得知是怎么处理的,因为驱动一般只对外提供接口。
(2)与 clob 相关的类型转换处理器
ClobTypeHandler / NClobTypeHandler / ClobReaderTypeHandler
public class ClobTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
throws SQLException {
StringReader reader = new StringReader(parameter);
ps.setCharacterStream(i, reader, parameter.length());
}
@Override
public String getNullableResult(ResultSet rs, String columnName)
throws SQLException {
String value = "";
Clob clob = rs.getClob(columnName);
if (clob != null) {
int size = (int) clob.length();
value = clob.getSubString(1, size);
}
return value;
}
// 另外两个方法类似
【解析】
参数绑定时,先将字符串转化为 Reader 对象,再绑定参数,该对象为给定的字符数。 当将非常大的 UNICODE 值输入到 LONGVARCHAR 参数时,通过 java.io.Reader 对象发送它可能更实际。 数据将根据需要从流中读取,直到到达文件末尾。 JDBC 驱动程序将执行从 UNICODE 到数据库char格式的所有必要转换。
NClobTypeHandler
的实现跟 ClobTypeHandler 完全一样,不知道为什么要定义这样一个多余的类型转换处理器?
ClobReaderTypeHandler
处理的java数据类型是 Reader
,不过用的不是 #setCharacterStream()
,而是 #setClob()
,原理跟上面一样。
(3)与字节相关的类型转换处理器
ByteTypeHandler / ByteArrayTypeHandler / ByteObjectArrayTypeHandler
代码跟上面类似,不赘述,这里会用到一个用具类,用于 byte[]
与 Byte[]
之间的互相转换,源码如下:
class ByteArrayUtils {
private ByteArrayUtils() {
// Prevent Instantiation
}
static byte[] convertToPrimitiveArray(Byte[] objects) {
final byte[] bytes = new byte[objects.length];
for (int i = 0; i < objects.length; i++) {
bytes[i] = objects[i];
}
return bytes;
}
static Byte[] convertToObjectArray(byte[] bytes) {
final Byte[] objects = new Byte[bytes.length];
for (int i = 0; i < bytes.length; i++) {
objects[i] = bytes[i];
}
return objects;
}
}
(4)与字符串相关的类型转换处理器
StringTypeHandler / NStringTypeHandler
两个类的实现完全一样,java类型是 String,调用 PreparedStatement 的 setString、getString
方法处理。
3、日期时间相关的类型转换处理器
先了解一下日期时间相关通用 SQL 数据类型,列表如下:
类型 | 格式 | 用途 |
---|---|---|
DATE |
YYYY-MM-DD | 日期 |
TIME |
HH:MM:SS | 时间值 |
YEAR |
YYYY | 年份值 |
DATETIME |
YYYY-MM-DD HH:MM:SS | 混合日期和时间值 |
TIMESTAMP |
YYYY-MM-DD HH:MM:SS | 混合日期和时间值,时间戳 |
(1)日期相关类型转换处理器
DateTypeHandler / DateOnlyTypeHandler / SqlDateTypeHandler
public class DateTypeHandler extends BaseTypeHandler<Date> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType)
throws SQLException {
// 将 java.util.Date 转化为 java.sql.Timestamp,再设置到 ps 中
ps.setTimestamp(i, new Timestamp((parameter).getTime()));
}
@Override
public Date getNullableResult(ResultSet rs, String columnName)
throws SQLException {
// 从结果集中获得 Timestamp 的值
Timestamp sqlTimestamp = rs.getTimestamp(columnName);
// 将 Timestamp 转化为 Date 类型
if (sqlTimestamp != null) {
return new Date(sqlTimestamp.getTime());
}
return null;
}
// 另外两个方法类似
【解析】
DateTypeHandler 将 java.util.Date
的 JavaType 转化为 timestamp
的 JdbcType,绑定参数时先转化为 java.sql.Timestamp
,再设置到 ps 中,结果映射反过来。
DateOnlyTypeHandler 是 java.util.Date
和 java.sql.Date
之间的转换,处理类似。
SqlDateTypeHandler 更简单了,传入返回的参数类型就是 java.sql.Date
无需做额外的类型转换。
(2)时间相关类型转换处理器
TimeOnlyTypeHandler / SqlTimeTypeHandler / SqlTimestampTypeHandler
-
TimeOnlyTypeHandler
:javaType 是java.util.Date
,jdbcType 是time
,中间通过java.sql.Time
转换。 -
SqlTimeTypeHandler
:javaType 是java.sql.Time
,jdbcType 是time
。 -
SqlTimestampTypeHandler
:javaType 是java.sql.Timestamp
,jdbcType 是timestamp
。
4、枚举相关的类型转换处理器
MyBatis 内置了两种枚举类型转换处理器 EnumTypeHandler
、EnumOrdinalTypeHandler
。
EnumTypeHandler
【功能】将枚举转化为对应的枚举类型字符串,再绑定参数;结果映射时拿到代表枚举的字符串,再转化为枚举类型。
【源码与注解】
public class EnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
private Class<E> type;
public EnumTypeHandler(Class<E> type) {
if (type == null) {
throw new IllegalArgumentException("Type argument cannot be null");
}
this.type = type;
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
// 将 Enum 转换成 String 类型
if (jdbcType == null) {
ps.setString(i, parameter.name());
} else {
ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE); // see r3589
}
}
@Override
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
// 获得 String 的值
String s = rs.getString(columnName);
// 将 String 转换成 Enum 类型
return s == null ? null : Enum.valueOf(type, s);
}
// 另外两个重载方法处理类似
}
【解析】
假设有一个这样的枚举类,定义如下:
public enum SexEnum {
FEMALE(1, "女"),
MALE(0, "男");
private int code;
private String name;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
上面 parameter.name()
得到的就是一个 "FEMALE"
或 "MALE"
的字符串,存入数据表列中也是存储这么一个值,结果映射时也是用这么一个字符串调用 Enum.valueOf()
转化为对应的枚举类型返回。
EnumOrdinalTypeHandler
【功能】根据枚举类型获取在枚举数组中的索引位置,再将其作为一个整数存储在数据库中。
【源码与注解】
public class EnumOrdinalTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
private Class<E> type;
private final E[] enums;
public EnumOrdinalTypeHandler(Class<E> type) {
if (type == null) {
throw new IllegalArgumentException("Type argument cannot be null");
}
this.type = type;
this.enums = type.getEnumConstants();
if (this.enums == null) {
throw new IllegalArgumentException(type.getSimpleName() + " does not represent an enum type.");
}
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
// 将 Enum 转换成 int 类型
ps.setInt(i, parameter.ordinal());
}
@Override
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
// 获得 int 的值
int i = rs.getInt(columnName);
if (rs.wasNull()) {
return null;
} else {
try {
// 将 int 转换成 Enum 类型
return enums[i];
} catch (Exception ex) {
throw new IllegalArgumentException("Cannot convert " + i + " to " + type.getSimpleName() + " by ordinal value.", ex);
}
}
}
【解析】
还是上面的枚举类为例子,SexEnum.FEMALE.ordinal()
得到的是 0,保存到数据库中的就是一个整数,结果映射反过来。
5、对象类型转换处理器
ObjectTypeHandler
【功能】数据库驱动自动根据传入的Java对象类型和数据表字段类型,自动转换。
【源码】
public class ObjectTypeHandler extends BaseTypeHandler<Object> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
throws SQLException {
ps.setObject(i, parameter);
}
@Override
public Object getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return rs.getObject(columnName);
}
@Override
public Object getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return rs.getObject(columnIndex);
}
@Override
public Object getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return cs.getObject(columnIndex);
}
}
【解析】
如果数据类型不明确,可以调用 setObject、getObject
方法处理,如果知道数据类型,优先使用指明处理类型的方法。
6、UnknownTypeHandler
如果转换时,参数绑定传入的参数类型或者结果集数据类型不清晰,则调用 UnknownTypeHandler
先处理解析出具体的类型,再调用对应的 TypeHandler 处理。
【源码分析】
public class UnknownTypeHandler extends BaseTypeHandler<Object> {
// 静态变量,单例共享
private static final ObjectTypeHandler OBJECT_TYPE_HANDLER = new ObjectTypeHandler();
// TypeHandler 注册表
private TypeHandlerRegistry typeHandlerRegistry;
public UnknownTypeHandler(TypeHandlerRegistry typeHandlerRegistry) {
this.typeHandlerRegistry = typeHandlerRegistry;
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
throws SQLException {
// 根据传入的参数和jdbcType获得对应 TypeHandler
TypeHandler handler = resolveTypeHandler(parameter, jdbcType);
// 使用处理器绑定参数
handler.setParameter(ps, i, parameter, jdbcType);
}
@Override
public Object getNullableResult(ResultSet rs, String columnName)
throws SQLException {
// 根据结果集和列名获得对应的处理器
TypeHandler<?> handler = resolveTypeHandler(rs, columnName);
// 使用处理器处理结果映射
return handler.getResult(rs, columnName);
}
@Override
public Object getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
// 根据结果集和列名获得对应的处理器
TypeHandler<?> handler = resolveTypeHandler(rs.getMetaData(), columnIndex);
// 从注册表中可能找不到合适的处理器
if (handler == null || handler instanceof UnknownTypeHandler) {
handler = OBJECT_TYPE_HANDLER;
}
// 使用处理器处理结果映射
return handler.getResult(rs, columnIndex);
}
@Override
public Object getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return cs.getObject(columnIndex);
}
private TypeHandler<? extends Object> resolveTypeHandler(Object parameter, JdbcType jdbcType) {
TypeHandler<? extends Object> handler;
// 若参数值为空,则使用默认的 ObjectTypeHandler
if (parameter == null) {
handler = OBJECT_TYPE_HANDLER;
// 若参数非空,根据参数类型从注册表中获取处理器
} else {
handler = typeHandlerRegistry.getTypeHandler(parameter.getClass(), jdbcType);
// check if handler is null (issue #270)
// 若没有获得明确类型的处理器,则使用默认的 ObjectTypeHandler
if (handler == null || handler instanceof UnknownTypeHandler) {
handler = OBJECT_TYPE_HANDLER;
}
}
return handler;
}
private TypeHandler<?> resolveTypeHandler(ResultSet rs, String column) {
try {
// 获得结果集中的元数据信息,并将所有的数据列名和位置索引的映射关系保存在 columnIndexLookup 中
Map<String,Integer> columnIndexLookup;
columnIndexLookup = new HashMap<String,Integer>();
ResultSetMetaData rsmd = rs.getMetaData();
int count = rsmd.getColumnCount();
for (int i=1; i <= count; i++) {
String name = rsmd.getColumnName(i);
columnIndexLookup.put(name,i);
}
// 从 columnIndexLookup 中找到列名对应的位置索引
Integer columnIndex = columnIndexLookup.get(column);
TypeHandler<?> handler = null;
if (columnIndex != null) {
handler = resolveTypeHandler(rsmd, columnIndex);
}
// 若没有找到列名对应的索引,则使用默认的 ObjectTypeHandler
if (handler == null || handler instanceof UnknownTypeHandler) {
handler = OBJECT_TYPE_HANDLER;
}
return handler;
} catch (SQLException e) {
throw new TypeException("Error determining JDBC type for column " + column + ". Cause: " + e, e);
}
}
private TypeHandler<?> resolveTypeHandler(ResultSetMetaData rsmd, Integer columnIndex) throws SQLException {
TypeHandler<?> handler = null;
JdbcType jdbcType = safeGetJdbcTypeForColumn(rsmd, columnIndex);
Class<?> javaType = safeGetClassForColumn(rsmd, columnIndex);
// 根据 JdbcType 和 JavaType 从注册表中调用对应的方法获得处理器
if (javaType != null && jdbcType != null) {
handler = typeHandlerRegistry.getTypeHandler(javaType, jdbcType);
} else if (javaType != null) {
handler = typeHandlerRegistry.getTypeHandler(javaType);
} else if (jdbcType != null) {
handler = typeHandlerRegistry.getTypeHandler(jdbcType);
}
return handler;
}
private JdbcType safeGetJdbcTypeForColumn(ResultSetMetaData rsmd, Integer columnIndex) {
try {
// 从 ResultSetMetaData 中,获得字段类型的 JdbcType
return JdbcType.forCode(rsmd.getColumnType(columnIndex));
} catch (Exception e) {
return null;
}
}
private Class<?> safeGetClassForColumn(ResultSetMetaData rsmd, Integer columnIndex) {
try {
// 从 ResultSetMetaData 中,获得字段类型的 JavaType
return Resources.classForName(rsmd.getColumnClassName(columnIndex));
} catch (Exception e) {
return null;
}
}
}
网友评论