Java程序<-->数据库,javaType<-->jdbcType,Mybatis使用类型处理器完成上述转换。
一、TypeHandler
TypeHandler是类型转换器,BaseTypeHandler是抽象基类,用于实现javaType和jdbcType的转换,有众多的实现类。主要方法是设置参数和获取查询结果集,底层实现还是基于PrepareStatement、ResultSet。TypeHandler可以通过mybatis-config配置文件typeHandler节点自定义。
设置参数值:
//BaseTypeHandler类
@Override
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
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) {
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 {
//设置参数值
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);
}
}
}
//StringTypeHandler继承了BaseTypeHandler类
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
throws SQLException {
//本质还是调用PreparedStatement的设置值的方法
ps.setString(i, parameter);
}
获取结果集:
@Override
public T getResult(ResultSet rs, String columnName) throws SQLException {
try {
return getNullableResult(rs, columnName);
} catch (Exception e) {
throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e);
}
}
//IntegerTypeHandler继承了BaseTypeHandler
@Override
public Integer getNullableResult(ResultSet rs, String columnName)
throws SQLException {
//本质还是调用了ResultSet方法
int result = rs.getInt(columnName);
return result == 0 && rs.wasNull() ? null : result;
}
二、TypeHandlerRegistry
mybatis如何管理众多的TypeHandler实现类呢,TypeHandlerRegistry就是TypeHandler的管理者,它决定着在何时选择何种实现类完成类型转换工作。TypeHandler实现类在mybatis初始化时注册到了TypeHandlerRegistry中。
//MybatisAutoConfiguration中创建Configuration对象时创建的TypeHandlerRegistry对象
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
//...
Configuration configuration = this.properties.getConfiguration();
if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
//创建Configuration对象
configuration = new Configuration();
}
//...
}
//Configuration类
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
//TypeHandlerRegistry类
private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
//key为javaType
private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
//在构造方法中注册类型转换器
public TypeHandlerRegistry() {
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
register(Byte.class, new ByteTypeHandler());
register(byte.class, new ByteTypeHandler());
register(JdbcType.TINYINT, new ByteTypeHandler());
register(Short.class, new ShortTypeHandler());
register(short.class, new ShortTypeHandler());
register(JdbcType.SMALLINT, new ShortTypeHandler());
//...
}
三、TypeAliasRegistry
指定别名,mybatis默认指定了一些别名,也可以通过配置文件自定义别名。
public TypeAliasRegistry() {
registerAlias("string", String.class);
registerAlias("byte", Byte.class);
registerAlias("long", Long.class);
registerAlias("short", Short.class);
registerAlias("int", Integer.class);
registerAlias("integer", Integer.class);
registerAlias("double", Double.class);
registerAlias("float", Float.class);
registerAlias("boolean", Boolean.class);
//...
}