1.背景

为什么要做自定义类型转换器。就是为了解决 java 类中的属性和数据库表字段属性类型不一致的情况。例如如下案例:

 

 我们可以看到  id、name、age的属性表与实体类都是一一对应的,但是 sex确不对应,此时如果我们不进行类型转换,那么向表 person 插入数据的时候会报错,查询的时候性别 sex 会显示空值(NULL),为了解决类似这种情形,我们需要自定义类型转换器;

2.具体实现步骤

我们通过实例具体对其进行演示,具体实现步骤如下:

  • 在 mysql 数据库中建立一个表 person,建表语句如下:
create table person
(
    id int(10) auto_increment comment '人员ID'
        primary key,
    name varchar(20) null comment '姓名',
    age int(20) null comment '年龄',
    sex int(10) null comment '性别:1-男,0-女'
)
comment '人力表';
  • 建立一个实体类 命名为 Person,省略其 Setter、Getter 以及 toString 方法
package com.entity;

/**
 * 类注释
 *
 * @author Lenovo
 * @Title: Person
 * @ProjectName mybatisYG
 * @Description: TODO  与 person 对应的  Person 实体类
 * @date 2019/11/1513:54
 */
public class Person {
    private int id;
    private String name;
    private int age;
    private String sex;

    public Person() {

    }

    public Person(int id, String name, int age, String sex) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
@Setter
@Getter
@toString
}
  • 建立转换器类,叫其继承 BaseTypeHandler(当然也可以叫其实现 TypeHandler 接口,BaseTypeHandler 为 TypeHandler 接口的实现类),具体实现代码如下:

 

package com.convert;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 类注释
 *
 * @author Lenovo
 * @Title: StringToInt
 * @ProjectName mybatisYG
 * @Description: TODO  建立一个转换器,将String 转换为 int
 * @date 2019/11/1515:57
 */
public class StringToInt extends BaseTypeHandler<String> {
    /*    该方法实现:通过 java类向数据库中设置(插入数据)
        参数含义:第一个为 JDBC 预处理对象,第二个为操作的位置,第三个为 javaType,第四个为 jdbcTpye*/
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {
        //如果是男,则存储到数据库中为1,如果为女,则存储在数据库中为0
        if (s.equals( "男" )) {
            preparedStatement.setInt( i, 1 );
        } else {
            preparedStatement.setInt( i, 0 );
        }

    }

    /*该方法实现通过列名获取数据库表中的数据*/
    @Override
    public String getNullableResult(ResultSet resultSet, String s) throws SQLException {
        int sex = resultSet.getInt( s );
/*
        三目运算符,如果表达式为 true,返回第一个值,如果表达式为 false,返回第二个值
*/
        return sex == 1 ? " 男" : "女";
    }

    /*该方法实现通过列标获取数据库表中的数据*/
    @Override
    public String getNullableResult(ResultSet resultSet, int i) throws SQLException {
        int sex = resultSet.getInt( i );
/*
        三目运算符,如果表达式为 true,返回第一个值,如果表达式为 false,返回第二个值
*/
        return sex == 1 ? " 男" : "女";
    }
    /*该方法实现通过存储过程获取数据库表中的数据*/

    @Override
    public String getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        int sex = callableStatement.getInt( i );
/*
        三目运算符,如果表达式为 true,返回第一个值,如果表达式为 false,返回第二个值
*/
        return sex == 1 ? " 男" : "女";
    }
}

 

  • 在 mybatis-config.xml 中配置自定义的转换器
   <!--配置类型转换器-->
    <typeHandlers>
        <typeHandler handler="com.convert.StringToInt" javaType="String" jdbcType="INTEGER"></typeHandler>
    </typeHandlers>
  • 配置实体类 Person 的映射文件 PersonMapper.xml,具体配置内容如下:
<?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="com.mapper.PersonMapper">
    <!--带转换器查询单个人员信息-->
    <select id="selectById" resultMap="PersonResult" parameterType="int">
        select * from person where id=#{id}
    </select>
    <resultMap id="PersonResult" type="Person">
        <!--主键是 id,其他列值为result-->
        <id property="id" column="id"></id>
        <result property="name" column="name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex" javaType="String" jdbcType="INTEGER"></result>
    </resultMap>
    <!--带转换器形式的插入一个人员信息-->
    <insert id="addPerson" parameterType="Person">
        INSERT INTO person (id, name, age, sex) VALUES (#{id}, #{name}, #{age}, #{sex,javaType=String,jdbcType=INTEGER})
    </insert>
</mapper>
  • 新建 PersonMapper.xml 对应的动态代理接口 PersonMapper,具体代码如下:
package com.mapper;

import com.entity.Person;

/**
 * 类注释
 *
 * @author Lenovo
 * @Title: PersonMapper
 * @ProjectName mybatisYG
 * @Description: TODO 与 PersonMapper.xml 文件同级的 动态代理接口
 * @date 2019/11/1513:59
 */
public interface PersonMapper {
    //按照 id 值查询指定的人员信息
    Person selectById(int id);

    //插入一个人员信息
    void addPerson(Person person);
}
  • 编写测试类,例如我们要查询,核心代码如下:
     Reader reader = Resources.getResourceAsReader( "mybatis-config.xml" );
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build( reader );
        //创建 session--相当于 connection
        SqlSession session = sqlSessionFactory.openSession();
        //动态代理  建立 PersonMapper 接口和 PersonMapper.xml 文件的映射关系
        PersonMapper personMapper = session.getMapper( PersonMapper.class );
        Person person = personMapper.selectById( 1 );
        System.out.println( "查询出来的人员信息为:" + person );
        //关闭session
        session.close();

  经过如上步骤,即完成了一个简单的类型转换。

 3.总结

总结起来,要想自定义类型转换器,需要经过如下步骤:

  • 创建类型转换器的类,需要实现 TypeHandler 接口,或者继承其实现类 BaseTypeHandler
  • 需要在 mybatis 的全局配置文件中配置类型转换器,配置项为:
   <!--配置类型转换器-->
    <typeHandlers>
        <typeHandler handler="com.convert.StringToInt" javaType="String" jdbcType="INTEGER"></typeHandler>
    </typeHandlers>

三个参数的含义如下:

        handler:转换器类的全路径名

        javaType:实体类中的属性类型;

        jdbcType:数据库表的字段类型;

  • 在mapper.xml 文件中进行引用,如果是查询,需要将返回类型变为 resultMap。
    <select id="selectById" resultMap="PersonResult" parameterType="int">
        select * from person where id=#{id}
    </select>
    <resultMap id="PersonResult" type="Person"><!--此 id 值必须和 select 标签中的 resultMap 的值完全一致-->
        <!--主键是 id,其他列值为result-->
        <id property="id" column="id"></id><!--此为表的主键-->
        <result property="name" column="name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex" javaType="String" jdbcType="INTEGER"></result>
    </resultMap>