引言:在JDBC中,需要在PreparedStatement对象中设置那些已经预编译过的SQlL语句的参数。执行SQL后,会通过ResultSet对象获取得到数据库的数据,而这些在Mybatis是根据数据的类型通过typeHandler来实现的。
在typeHandler中,分为jdbcType和javatype,其中jdbcType用于定义数据库类型,而javaType用于定义Java类型,那么typeHandler的作用就是承担jdbcTypr和javaType之间的相互转换。
很多情况下我们并不需要去配置typeHandler、jdbcType和javaType,因为在Mybatis会探测应该使用什么类型的typeHandler去处理,也就是说Mybatis内部定义了许多常用的typeHandler。但是有些场景是无法探测到,比如那些需要使用自定义枚举场景,或者数据库使用特殊数据类型的场景,这些情况我们可以使用自定义的typeHandler去处理类型之间的转换问题。
下面给大家列出几个常用简单的typeHander
这些都是Mybatis系统已经创建好的TypeHandler转换器,在大部分情况下无需显式地声明jdbcType和javaType,或者用typeHandler去指定对应的typeHandler来实现数据类型的转换,因为Mybatis系统会自动探测。
1、分析Typehandlet源码
在Mybatis中typeHandler都要实现接口org.apache.ibatis.type.TypeHandler,下面根据源码简单分析一下。
public interface TypeHandler {
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;
}
2、自定义typeHandler(以String为例)
从系统定义的typeHandler中可以知道,要实现typeHandler就需要去实现接口Typehandler,或者继承BaseTypeHandler(实际上,BaseTypehandler实现了typeHandler接口)。在这里,我们直接实现TypeHandler接口,代码如下:
package com.ydj.utils;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.log4j.Logger;
public class MyTypeHandler implements TypeHandler{
//定义一个日志
Logger log = Logger.getLogger(MyTypeHandler.class);
@Override
public String getResult(ResultSet rs, String columnName) throws SQLException {
String result = rs.getString(columnName);
log.info("读取string参数1【"+result+"】");
return result;
}
@Override
public String getResult(ResultSet rs, int columnIdex) throws SQLException {
String result = rs.getString(columnIdex);
log.info("读取string参数2【"+result+"】");
return result;
}
@Override
public String getResult(CallableStatement cs, int columnIdex) throws SQLException {
String result = cs.getString(columnIdex);
log.info("读取string参数3【"+result+"】");
return result;
}
@Override
public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
log.info("设置string参数【"+parameter+"】");
ps.setString(i, parameter);
}
}
3、在代码中使用自定义typeHandler
方式一:
(1)首先在mybatis-config.xml配置typeHandlers
配置完后系统才会读取它,这样注册后,当jdbcTypeHandler和javaTypeHandler能与MyTypeHandler对应的时候,它就会启动MyTypeHandler。
注意:在mybatis-config.xml配置属性时,是有顺序的,不然在Mybatis启动阶段就会发生异常,导致程序无法运行。顺序如下:
< configuration > //配置 < properties/> //属性 < settings /> //设置 < typeAliases > //类型别名 < typeHandlers/> //类型处理器 < objectFactory/> //对象工厂 < plugins> //插件 < environments> //配置环境 < environment> //环境变量 < transactionManager /> //事务管理器 < dataSource/> //数据源 < environment> < environments> < databaseIdProvider/> //数据库厂商标识 < mappers/> //映射器
(2)在映射文件 xxxMapper.xml配置
方式二
直接在映射文件xxxMapper.xml里面配置,代码如下:
总结
实现自定义typeHandler:
要么指定了与自定义typeHandler一致的jdbcType和javaType(见方式一)
要么直接使用typeHandler指定的具体实现类(全限定名)(见方式二)
当然,两者也可一起使用,最好在mybatis-congif.xml里面配置typeHandlers属性
----mybatis.config.xml代码片段----
----xxxMapper.xml代码片段----
4、多个自定义typeHandler在同一个包中
有时候由于枚举类型很多,系统需要的typeHandler也会很多,如果采用配置也会很麻烦,这个时候可以考虑使用包扫描的形式。
(1)假定自定义的typeHandler类在com.ydj.utils下
----mybatis.config.xml代码片段----
如果这样注册就没法指定jdbcType和javaType了,不过我们可以使用注解来处理它们。
(2)注解表明jdbcType和javaType
@MappedTypes(String.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class MyTypeHandler implements TypeHandler{
......
}
在绝大多数情况下,typeHandler因为枚举而使用,Mybatis已经定义了两个类作为枚举类型的支持,这两个类分别为:
因为它们的作用不大,所以大部分情况下,我们都不用它们,不过我们还是要稍微了解一下它们的用法。
先来建一个性别枚举类——SexEnum
package com.ydj.model;
public enum SexEnum {
MALE(1, "男"),
FEMALE(0, "女");
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
SexEnum(int id, String name){
this.id = id;
this.name = name;
}
public static SexEnum getSexById(int id){
for (SexEnum sex : SexEnum.values()) {
if(sex.getId() == id){
return sex;
}
}
return null;
}
}
用户表类代码:
package com.ydj.model;
public class User {
private Integer uid;
private String uname;
private String upwd;
private SexEnum sex;
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public
String getUpwd() {
return upwd;
}
public void setUpwd(String upwd) {
this.upwd = upwd;
}
public SexEnum getSex() {
return sex;
}
public void setSex(SexEnum sex) {
this.sex = sex;
}
@Override
public String toString() {
return "User [uid=" + uid + ", uname=" + uname + ", upwd=" + upwd + ", sex=" + sex + "]";
}
}
1、用EnumOrdinalTypeHandler实现
它是Mybatis根据枚举数组下标索引的方式进行匹配的,它要求数据库返回一个整数作为其下标,它会根据下标找到对应的枚举类型。
----xxxMapper.xml部分代码----
2、用EnumTypeHandler实现
它会把使用的名称转化为对应的枚举,比如它会根据数据库返回的字符串“MALE”,进行Enum.valueOf(SexEnum.class, “MALE”);转换。
3、自定义枚举typeHandler
package com.ydj.utils;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.apache.ibatis.type.TypeHandler;
import com.ydj.model.SexEnum;
@MappedTypes(SexEnum.class)
@MappedJdbcTypes(JdbcType.INTEGER)
public class SexEnumTypeHandler implements TypeHandler{
@Override
public SexEnum getResult(ResultSet rs, String ColumnName) throws SQLException {
int id = rs.getInt(ColumnName);
return SexEnum.getSexById(id);
}
@Override
public SexEnum getResult(ResultSet rs, int ColumnIndex) throws SQLException {
int id = rs.getInt(ColumnIndex);
return SexEnum.getSexById(id);
}
@Override
public SexEnum getResult(CallableStatement cs, int ColumnIndex) throws SQLException {
int id = cs.getInt(ColumnIndex);
return SexEnum.getSexById(id);
}
@Override
public void setParameter(PreparedStatement ps, int i, SexEnum parameter, JdbcType jdbcType) throws SQLException {
ps.setInt(i, parameter.getId());
}
}
实现
----xxxMapper.xml部分代码