在Mybatis中可以定义一个TypeHandler类型,通过它可以实现Java类型跟数据库类型的相互转换。
TypeHandler接口
在Mybatis中要实现自己的TypeHandler就需要实现Mybatis为提供的TypeHandler接口。在TypeHandler中定义了四个方法:
public interface TypeHandler<T> { /** * 用于定义在Mybatis设置参数时,该如何把Java类型的参数转换为对应的数据库类型 * @param ps 当前的PreparedStatement对象 * @param i 当前参数的位置 * @param parameter 当前参数的Java对象 * @param jdbcType 当前参数的数据库类型 * @throws SQLException */ void setParameter(PreparedStatement ps, int i, T parameter,JdbcType jdbcType) throws SQLException; /** * 用于在Mybatis获取数据结果集时,如何把数据库类型转换为对应的Java类型 * @param rs 当前的结果集 * @param columnName 当前的字段名称 * @return 转换后的Java对象 * @throws SQLException */ T getResult(ResultSet rs, String columnName) throws SQLException; /** * 用于在Mybatis通过字段位置获取字段数据时,把数据库类型转换为对应的Java类型 * @param rs 当前的结果集 * @param columnIndex 当前字段的位置 * @return 转换后的Java对象 * @throws SQLException */ T getResult(ResultSet rs, int columnIndex) throws SQLException; /** * 用于Mybatis在调用存储过程后,把数据库类型的数据转换为对应的Java类型 * @param cs 当前的CallableStatement执行后的CallableStatement * @param columnIndex 当前输出参数的位置 * @return * @throws SQLException */ T getResult(CallableStatement cs, int columnIndex) throws SQLException; }
Mybatis还为我们提供一个实现了TypeHandler接口的抽象类BaseTypeHandler。所以我们也可以通过继承BaseTypeHandler来实现自己的TypeHandler。
先来看一下BaseTypeHandler类的定义:
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> { protected Configuration configuration; public void setConfiguration(Configuration c) { this.configuration = c; } 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 { setNonNullParameter(ps, i, parameter, jdbcType); } } public T getResult(ResultSet rs, String columnName) throws SQLException { T result = getNullableResult(rs, columnName); if (rs.wasNull()) { return null; } else { return result; } } public T getResult(ResultSet rs, int columnIndex) throws SQLException { T result = getNullableResult(rs, columnIndex); if (rs.wasNull()) { return null; } else { return result; } } public T getResult(CallableStatement cs, int columnIndex) throws SQLException { T result = getNullableResult(cs, columnIndex); if (cs.wasNull()) { return null; } else { return result; } } public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException; public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException; public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException; public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException; }
案例
向数据库写数据,要写的是一个Date对象,但是写到数据库之后,这个Date对象就变成了Date对象所描述的时间到1970年的秒数,然后当从数据库读取这个秒数之后,系统又会自动帮我将这个秒数转为Date对象。
创建一张User表
user实例
public class User { private Long id; private String username; private String password; private Date regTime; ....
在定义TypeHandler对象
package mybatis.handler; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedTypes; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Date; @MappedJdbcTypes(JdbcType.VARCHAR) @MappedTypes(Date.class) public class MyTypeHandler extends BaseTypeHandler<Date> { @Override public void setNonNullParameter(PreparedStatement ps, int i, Date date, JdbcType jdbcType) throws SQLException { ps.setString(i,String.valueOf(date.getTime())); } @Override public Date getNullableResult(ResultSet rs, String columnName) throws SQLException { return new Date(rs.getLong(columnName));//以long的数据类型形式读取内容 } @Override public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return new Date(rs.getLong(columnIndex)); } @Override public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return cs.getDate(columnIndex); } }
1.@MappedJdbcTypes定义是JdbcType类型,必须要是枚举类org.apache.ibatis.type.JdbcType所枚举的数据类型 2.@MappedTypes定义的是JavaType的数据类型,描述哪些Java类型可被拦截。 3.在我们启用了自定义的这个TypeHandler之后,数据的读写都会被这个类所过滤 4.在setNonNullParameter方法中,重新定义要写往数据库的数据。 5.在另外三个方法中我们将从数据库读出的数据类型进行转换。
在配置文件中注册自定义的handler
mybatis配置文件中注册typeHandler,注册有两种不同的方式:1、直接注册一个包中所有的typeHandler,系统在启动时会自动扫描包下的所有文件 2、一个类一个类的注册
<!--需要在别名标签之后--> <typeHandlers> <typeHandler handler="mybatis.handler.MyTypeHandler"/> </typeHandlers> or <typeHandlers> <package name="mybatis.handler"/> </typeHandlers>
在Mapper中配置
<resultMap id="userResult" type="user"> <result typeHandler="mybatis.handler.MyTypeHandler" javaType="java.util.Date" jdbcType="VARCHAR" property="regTime" column="regTime"/> </resultMap>
select中使用这个resultMap:
<select id="getUser" parameterType="Integer" resultMap="userResult"> select * from user where id=#{id} </select>
想要在插入的时候,启用自定义的typeHandler,除了在mybatis配置文件中配置的方法,也可以insert节点中简单配置一下,如下:
<insert id="insertUser" parameterType="org.sang.bean.User"> INSERT INTO user(username,password,regTime)
VALUES (#{username},#{password},#{regTime,javaType=Date,jdbcType=VARCHAR,typeHandler=mybatis.handler.MyDateTypeHandler}) </insert>
插入结果:
读出结果
总结就是读取时的配置要和插入时的配置分开,读取时数据转换,有两种配置方式,分别是resultMap和在mybatis配置文件中配置typeHandlers,插入时的配置就是在insert节点中进行配置。