【MyBatis学习笔记】5:认识使用typeHandlers配置类型处理器

简述

注册了的类型处理器会用于处理下面两种情形:

  • 为PreparedStatement设置一个参数,将参数从Java类型转为JDBC类型。
  • 从ResultSet中取出一个值,将结果从JDBC类型转为Java类型。

类型处理器可分为以下两类:

  • MyBatis系统定义的类型处理器
  • 用户自定义的类型处理器

认识系统定义的类型处理器

org.apache.ibatis.type.TypeHandlerRegistry类中的构造器可以看到系统注册的大量的类型处理器,如其中一段:

    register(Reader.class, new ClobReaderTypeHandler());
    register(String.class, new StringTypeHandler());
    register(String.class, JdbcType.CHAR, new StringTypeHandler());
    register(String.class, JdbcType.CLOB, new ClobTypeHandler());
    register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
    register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
    register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
    register(JdbcType.CHAR, new StringTypeHandler());
    register(JdbcType.VARCHAR, new StringTypeHandler());
    register(JdbcType.CLOB, new ClobTypeHandler());
    register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
    register(JdbcType.NVARCHAR, new NStringTypeHandler());
    register(JdbcType.NCHAR, new NStringTypeHandler());
    register(JdbcType.NCLOB, new NClobTypeHandler());

点开其中的org.apache.ibatis.type.StringTypeHandler类查看一下具体实现:

//继承BaseTypeHandler抽象接口,间接实现了必要的TypeHandler接口
public class StringTypeHandler extends BaseTypeHandler<String> {

    //设置非空参数(要设置参数的预处理语句,设置参数的下标,使用的Java类型字符串,对应数据库的JDBC类型)
    @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);
    }
}

自定义的类型处理器

按照书上的例子,先尝试自定义覆盖掉一个系统定义的类型处理器,实现StringVARCHAR的转换。

配置文件中
<!--注册自定义的类型处理器-->
<typeHandlers>
    <typeHandler jdbcType="VARCHAR" javaType="string" handler="test.MyStringTypeHandler"/>
</typeHandlers>
该类型处理器类

一定要实现TypeHandler接口,可以像前面系统实现的那样通过继承BaseTypeHandler类来实现,也可以直接实现。

对于处理器类,使用@MappedTypes注解Java类型;使用@MappedJdbcTypes注解JDBC类型,即org.apache.ibatis.type.JdbcType枚举类所列的枚举类型。

package test;

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 java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

//Java类型为String
@MappedTypes({String.class})
//JDBC类型为VARCHAR
@MappedJdbcTypes(JdbcType.VARCHAR)
//直接实现TypeHandler接口,泛型<T>影响了后三个方法的返回值<T>
public class MyStringTypeHandler implements TypeHandler<String> {

    //为PreparedStatement设置参数
    @Override
    public void setParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {
        System.out.println("调用了自定的setParameter");
        preparedStatement.setString(i, s);
    }

    //从结果集中按列名获取
    @Override
    public String getResult(ResultSet resultSet, String s) throws SQLException {
        System.out.println("调用了自定的getResult(1)");
        return resultSet.getString(s);
    }

    //从结果集中按列号获取
    @Override
    public String getResult(ResultSet resultSet, int i) throws SQLException {
        System.out.println("调用了自定的getResult(2)");
        return resultSet.getString(i);
    }

    //从存储过程中按列号获取
    @Override
    public String getResult(CallableStatement callableStatement, int i) throws SQLException {
        System.out.println("调用了自定的getResult(3)");
        return callableStatement.getString(i);
    }
}

不改变其行为,只是在调用其内方法时多输出一句话,以验证是否配置上了这个自定义的类型处理器。

映射文件中
  • 为映射文件添加<resultMap>标签(构建ResultSet类型处理器)
  • 为查找的标签添加resultMap属性指向这个刚构建的类型处理器(增删改不会返回ResultSet所以不用)
  • 为SQL语句中使用到参数的位置添加上javaTypejdbcTypetypeHandler属性于#{}内(参数使用类型处理器)。

前两件事共同配置了ResultSet使用类型处理器,后面一件事是在配置PreparedStatement的参数传入时使用类型处理器。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.StudentMapper">
    <!--设置关于ResultSet使用类型处理器-->
    <!--id用来给后面使用,type就使用PO类或其别名表示结果集对应的是这个类的对象-->
    <resultMap id="stuMap" type="stu">
        <!--id子元素用来标识主键,column标识在数据库中的列或属性,property标识PO类的属性,后两个属性不用说了-->
        <id column="id" property="id" javaType="int" jdbcType="INTEGER"/>
        <!--result子元素标识非主属性,元素内的四个属性都和前面id的一样-->
        <!--可以直接使用typeHandler属性,就不必指出javaType和jdbcType了,或只给出javaType和jdbcType也够用-->
        <result column="name" property="stuName" typeHandler="test.MyStringTypeHandler"/>
    </resultMap>

    <!--查询语句需要给出resultMap属性,指向前面配置的resultMap的id,表示对该语句的结果集的处理使用前面resultMap的配置-->
    <select id="getStudent" parameterType="int" resultType="stu" resultMap="stuMap">
        SELECT
            id,
            name
        FROM student
        WHERE id = #{id}
    </select>

    <insert id="addStudent" parameterType="stu">
        INSERT INTO student (name) VALUES (#{stuName})
    </insert>

    <delete id="deleteStudent" parameterType="int">
        DELETE FROM student
        WHERE id = #{id}
    </delete>

    <!--这里面的name设置了参数使用这个自定的类型处理器-->
    <update id="updateStudent" parameterType="stu">
        UPDATE student
        SET name = #{stuName,javaType=string,jdbcType=VARCHAR,typeHandler=test.MyStringTypeHandler}
        WHERE id = #{id}
    </update>
</mapper>
运行结果

这里写图片描述
可以看到输出了自定义的类型处理器中的这两句话,说明成功配置了自定义的类型处理器。

未解决的疑问

在前面映射文件中,可以看到除了新的和类型处理器相关的配置之外,还有一个和之前不一样的地方:

SELECT
    id,
    name
FROM student
WHERE id = #{id}

这里之前是:

SELECT
    id,
    name as stuName
FROM student
WHERE id = #{id}

如果采用这种带有AS的写法,读出来的name始终是null,而且不会输出第一句调用(即没有调用自定义的类型处理器中的getResult()方法)。

我猜测是因为添加ResultMap已经给出了这种映射关系,即从columnproperty的映射关系。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值