typeHandler 类型转换器
在传统的JDBC中,我们需要在PreparedStatement对象中设置我们的SQL语句,在执行SQL后通过Res0ultet对象获取从数据库得到的结果。然而在Mybatis中的数据转换是通过typeHandler来实现的。其中分为jdbcType和javaType。我们的typeHandler就是负责数据库类型和java类型之间的转换。在很多时候,我们不需要去设置这些配置,mybatis会自动探测。当遇到了某些特殊情况时,就必须用自定义的typeHandler处理转换问题。
和别名一样,有系统定义的typeHandler和自定义的typeHandler。这里我们直接讨论如何自定义typeHandler。
在mybatis中typeHandler都要实现接口 org.apache.ibatis.type.TypeHandler。下面是这个接口的源代码
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是泛型,指javaType,比如我们需要String的时候,实现类可以写成 XXX implements TypeHandler。
- setParameter方法,是通过PreparedStatement对象设置SQL参数使用的具体方法,i为在sql中参数的位置,parameter是参数,jdbcType为数据库类型。
- 剩余的三个 getResult方法,是从JDBC结果集中获取数据进行类型转换。从结果集中获取数据分为columnName 列名方式 和 columnIndex 下标方式。最后一个是存储过程专用。
了解了接口后,通过源码得知所有的系统定义TypeHandler都实现了抽象类BaseTypeHandler。在这里我不展示BaseTypeHandle的源代码,简要的介绍一下
- BaseTypeHandle实现了接口中的方法,并尽力自动解决并将无法解决的问题作为抽象方法供继承者解决。
1. setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)方法 : 当setParameter方法中parameter和jdbcType同时为空则报出异常,仅当parameter为空时则会执行ps.setNull(i, jdbcType.TYPE_CODE);进行空设置。但当paramter不为空时,需要用户实现setNonNullParameter(ps, i, parameter, jdbcType)方法。让PreparedStatement准确的调用方法将参数设置到sql中。
2. getNullableResult(ResultSet rs, String columnName) 方法 : 是以列名方式从JDBC结果集中获取数据进行类型转换。
3. getNullableResult(ResultSet rs, int columnIndex) 方法 : 是以下标方式从JDBC结果集中获取数据进行类型转换。
4. getNullableResult(CallableStatement cs, int columnIndex)用于存储过程
接下里我们看一个例子,mybatis中String的typeHandler是怎样实现的
public class StringTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
throws SQLException {
ps.setString(i, parameter);
}
@Override
public String getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return rs.getString(columnName);
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return rs.getString(columnIndex);
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return cs.getString(columnIndex);
}
}
在这里,typeHandler实现了jdbcType和javaType的相互转换。再看一下源代码中它们是如何注册的。
public TypeHandlerRegistry() {
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
//此后省略
}
需要注意的是自定义的类型转换是不需要注册的,一般是通过配置或扫描。
接下来我们模仿一个String的类型转换。
public class MyTypeHandler implements TypeHandler<String> {
Logger logger = Logger.getLogger(MyTypeHandler.class);
@Override
public void setParameter(PreparedStatement ps, int i, String parameter,
JdbcType jdbcType) throws SQLException {
logger.info("设置string参数"+parameter);
ps.setString(i, parameter);
}
@Override
public String getResult(ResultSet rs, String columnName)
throws SQLException {
String result = rs.getString(columnName);
logger.info("读取String columnName参数"+result);
return result;
}
@Override
public String getResult(ResultSet rs, int columnIndex) throws SQLException {
String result = rs.getString(columnIndex);
logger.info("读取String columnIndex参数"+result);
return result;
}
@Override
public String getResult(CallableStatement cs, int columnIndex)
throws SQLException {
String result = cs.getString(columnIndex);
logger.info("cs 读取String columnIndex参数"+result);
return result;
}
}
接下来我们需要配置启用刚刚的typeHandler,然后就可以测试了。
<typeHandlers>
<typeHandler javaType="string" jdbcType="VARCHAR" handler="lly.mybatis.example.utils.MyTypeHandler"/>
</typeHandlers>