mybatis的使用及源码分析(五) mybatis自定义typeHandler

当我们在javabean中自定义了枚举类型或者其它某个类型,但是在数据库中存储时往往需要转换成数据库对应的类型,并且在从数据库中取出来时也需要将数据库类型转换为javabean中的对应类型。比如:javabean中字段类型为Date,数据库中存储的是varchar类型;javabean中字段类型是Enum,数据库中存储的是String或者Integer。
因为有大量类似数据的转换,手动转换类型进行存储和查询已经过于麻烦。MyBatis为我们提供了解决办法:TypeHandler类型处理器。

MyBatis 内置的 TypeHandler

在 MyBatis 常见内的内置Ty’peHandler
BooleanTypeHandler:用于 java 类型 boolean,jdbc 类型 bit、boolean
ByteTypeHandler:用于 java 类型 byte,jdbc 类型 TINYINT
ShortTypeHandler:用于 java 类型 short,jdbc 类型 SMALLINT
IntegerTypeHandler:用于 INTEGER 类型
LongTypeHandler:用于 long 类型
FloatTypeHandler:用于 FLOAT 类型
DoubleTypeHandler:用于 double 类型
StringTypeHandler:用于 java 类型 string,jdbc 类型 CHAR、VARCHAR
ArrayTypeHandler:用于 jdbc 类型 ARRAY
BigDecimalTypeHandler:用于 java 类型 BigDecimal,jdbc 类型 REAL、DECIMAL、NUMERIC
DateTypeHandler:用于 java 类型 Date,jdbc 类型 TIMESTAMP
DateOnlyTypeHandler:用于 java 类型 Date,jdbc 类型 DATE
TimeOnlyTypeHandler:用于 java 类型 Date,jdbc 类型 TIME
对于常见的 Enum 类型,内置了 EnumTypeHandler 进行 Enum 名称的转换和 EnumOrdinalTypeHandler 进行 Enum 序数的转换。这两个类型处理器没有在 TypeHandlerRegistry 中注册,如果需要使用必须手动配置。

自定义一个TypeHandler:
本项目搭建源码:https://github.com/zhuquanwen/mybatis-learn/releases/tag/for-typehandler
搭建过程:
https://blog.csdn.net/u011943534/article/details/104732289文章基础上搭建,有些过程不详细描述.

1、导入本文测试使用的sql脚本

/*
Navicat MySQL Data Transfer

Source Server         : localhost
Source Server Version : 50722
Source Host           : localhost:3306
Source Database       : mybatis_learn

Target Server Type    : MYSQL
Target Server Version : 50722
File Encoding         : 65001

Date: 2020-03-16 23:06:58
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for bird
-- ----------------------------
DROP TABLE IF EXISTS `bird`;
CREATE TABLE `bird` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(80) DEFAULT NULL,
  `sex` smallint(6) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of bird
-- ----------------------------
INSERT INTO `bird` VALUES ('1', '小黄', '1');
INSERT INTO `bird` VALUES ('2', '小绿', '0');
INSERT INTO `bird` VALUES ('3', '小黑', '1');

2、配置generator插件,之后运行com.learn.zqw.generator.Generator的主函数将bird表自动生成实体和mapper

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>

    <!-- context指定环境 -->
    <context id="MyGererator" targetRuntime="MyBatis3">

        <!-- 这个标签可以去掉各类元素生成的注释,默认是全部生成的 -->
        <commentGenerator>
            <!-- 去掉注释 -->
            <property name="suppressAllComments" value="true"/>
            <!-- 去掉时间戳 -->
            <property name="suppressDate" value="true"/>
        </commentGenerator>

        <!-- 数据库连接信息 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/mybatis_learn?useUnicode=true&amp;characterEncoding=utf8"
                        userId="root"
                        password="root">
        </jdbcConnection>

        <!-- JAVA JDBC数据类型转换,可以参照官方文档 -->
        <javaTypeResolver >
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!--  javaModelGenerator javaBean配置
        targetPackage 输入包名 输出路径
        targetProject 输出项目位置 -->
        <javaModelGenerator targetPackage="com.learn.zqw.generator.domain" targetProject="src/main/java">
            <!-- enableSubPackages 是否开启子包名称 是否在包名后边加上scheme名称 -->
            <property name="enableSubPackages" value="false" />
            <!-- 在Set方法中加入.trim -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

        <!-- 映射文件mapper.xml配置 -->
        <sqlMapGenerator targetPackage="com.learn.zqw.generator.mapper"  targetProject="src/main/java">
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>

        <!-- 动态代理类接口,和mapper.xml在要同一个路径 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.learn.zqw.generator.mapper"  targetProject="src/main/java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>

        <!-- 数据表 根据数据库中的表来生成  -->
        <!--<table tableName="student"/>-->
        <table tableName="bird"/>

        <!-- 数据表更详细的属性参见官方文档,也可参照https://www.jianshu.com/p/e09d2370b796,里注释掉-->
        <!-- <table schema="DB2ADMIN" tableName="ALLTYPES" domainObjectName="Customer" >
          <property name="useActualColumnNames" value="true"/>
          <generatedKey column="ID" sqlStatement="DB2" identity="true" />
          <columnOverride column="DATE_FIELD" property="startDate" />
          <ignoreColumn column="FRED" />
          <columnOverride column="LONG_VARCHAR_FIELD" jdbcType="VARCHAR" />
        </table> -->
    </context>
</generatorConfiguration>

3、定义性别的枚举,并将Bird实体的sex字段改为此枚举类型

package com.learn.zqw.generator.domain;

/**
 * //TODO
 *
 * @author zhuquanwen
 * @vesion 1.0
 * @date 2020/3/16 22:29
 * @since jdk1.8
 */
public enum SexEnum {
    MALE(0, "雄性"),

    FEMALE(1, "雌性");

    public int val;
    public String label;

    public int getVal() {
        return val;
    }
    public String getLabel() {
        return label;
    }
    SexEnum(int val, String label) {
        this.val = val;
        this.label = label;
    }
}

package com.learn.zqw.generator.domain;

import lombok.Data;

@Data
public class Bird {
    private Integer id;

    private String name;

    private SexEnum sex = SexEnum.MALE;


}

4、编写SexEnum转换的SexEnumTypeHandler

package com.learn.zqw.handler;

import com.learn.zqw.generator.domain.SexEnum;
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;

/**
 * //TODO
 *
 * @author zhuquanwen
 * @vesion 1.0
 * @date 2020/3/16 22:04
 * @since jdk1.8
 */
@MappedTypes(value = {SexEnum.class})
@MappedJdbcTypes(JdbcType.SMALLINT)
public class SexEnumTypeHandler extends BaseTypeHandler<SexEnum> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, SexEnum parameter, JdbcType jdbcType) throws SQLException {
        ps.setInt(i, parameter.getVal());
    }

    @Override
    public SexEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {
        int val = rs.getInt(columnName);
        return convert(val);
    }

    @Override
    public SexEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        int val = rs.getInt(columnIndex);
        return convert(val);
    }

    @Override
    public SexEnum getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        int val = cs.getInt(columnIndex);
        return convert(val);
    }

    private SexEnum convert(int val) {
        SexEnum[] objs = SexEnum.values();
        for (SexEnum em : objs) {
            if (em.getVal() == val) {
                return em;
            }
        }
        return null;
    }

}

5、在SqlMapConfig.xml中注册此SexEnumTypeHandlerBirdMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--mybatis的主配置文件-->
<configuration>

    <!--自定义typeHandler-->
    <typeHandlers>
        <typeHandler handler="com.learn.zqw.handler.SexEnumTypeHandler"
                     javaType="com.learn.zqw.generator.domain.SexEnum"
                     jdbcType="SMALLINT"/>
    </typeHandlers>

    <!--配置环境-->
    <environments default="mysql">
        <!--配置mysql环境-->
        <environment id="mysql">
            <!--配置事务类型-->
            <transactionManager type="JDBC" />
            <!--配置数据源(连接池)-->
            <dataSource type="POOLED">
                <!--配置连接数据库的4个基本信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis_learn?useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>


    <!--指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件-->
    <mappers>

        <mapper resource="com/learn/zqw/IUserMapper.xml" />
        <mapper resource="com/learn/zqw/generator/mapper/StudentMapper.xml" />
        <mapper resource="com/learn/zqw/generator/mapper/BirdMapper.xml" />

        <!--可以直接把每个xml作配置,也可以使用包扫描,使用包扫描,如果使用纯注解方式,也可以把对应的.xml文件删除-->
        <!--<mapper resource="com/learn/zqw/sqlannotation/mapper/BookMapper.xml" />-->
        <!-- <mapper resource="com/learn/zqw/sqlannotation/mapper/LibraryMapper.xml" />-->
        <package name="com.learn.zqw.sqlannotation.mapper"/>
    </mappers>
</configuration>

6、修改自动生成的BirdMapper,将sex字段的定义都加上typeHandler

<?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.learn.zqw.generator.mapper.BirdMapper">
  <resultMap id="BaseResultMap" type="com.learn.zqw.generator.domain.Bird">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="name" jdbcType="VARCHAR" property="name" />
    <result column="sex" jdbcType="SMALLINT"  property="sex" typeHandler="com.learn.zqw.handler.SexEnumTypeHandler" />
  </resultMap>
  <sql id="Example_Where_Clause">
    <where>
      <foreach collection="oredCriteria" item="criteria" separator="or">
        <if test="criteria.valid">
          <trim prefix="(" prefixOverrides="and" suffix=")">
            <foreach collection="criteria.criteria" item="criterion">
              <choose>
                <when test="criterion.noValue">
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue">
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue">
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue">
                  and ${criterion.condition}
                  <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Update_By_Example_Where_Clause">
    <where>
      <foreach collection="example.oredCriteria" item="criteria" separator="or">
        <if test="criteria.valid">
          <trim prefix="(" prefixOverrides="and" suffix=")">
            <foreach collection="criteria.criteria" item="criterion">
              <choose>
                <when test="criterion.noValue">
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue">
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue">
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue">
                  and ${criterion.condition}
                  <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Base_Column_List">
    id, name, sex
  </sql>
  <select id="selectByExample" parameterType="com.learn.zqw.generator.domain.BirdExample" resultMap="BaseResultMap">
    select
    <if test="distinct">
      distinct
    </if>
    <include refid="Base_Column_List" />
    from bird
    <if test="_parameter != null">
      <include refid="Example_Where_Clause" />
    </if>
    <if test="orderByClause != null">
      order by ${orderByClause}
    </if>
  </select>
  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from bird
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from bird
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <delete id="deleteByExample" parameterType="com.learn.zqw.generator.domain.BirdExample">
    delete from bird
    <if test="_parameter != null">
      <include refid="Example_Where_Clause" />
    </if>
  </delete>
  <insert id="insert" parameterType="com.learn.zqw.generator.domain.Bird">
    insert into bird (id, name, sex
    )
    values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR},
    #{sex,jdbcType=SMALLINT, typeHandler=com.learn.zqw.handler.SexEnumTypeHandler}
    )
  </insert>
  <insert id="insertSelective" parameterType="com.learn.zqw.generator.domain.Bird">
    insert into bird
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        id,
      </if>
      <if test="name != null">
        name,
      </if>
      <if test="sex != null">
        sex,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=INTEGER},
      </if>
      <if test="name != null">
        #{name,jdbcType=VARCHAR},
      </if>
      <if test="sex != null">
        #{sex,jdbcType=SMALLINT},
      </if>
    </trim>
  </insert>
  <select id="countByExample" parameterType="com.learn.zqw.generator.domain.BirdExample" resultType="java.lang.Long">
    select count(*) from bird
    <if test="_parameter != null">
      <include refid="Example_Where_Clause" />
    </if>
  </select>
  <update id="updateByExampleSelective" parameterType="map">
    update bird
    <set>
      <if test="record.id != null">
        id = #{record.id,jdbcType=INTEGER},
      </if>
      <if test="record.name != null">
        name = #{record.name,jdbcType=VARCHAR},
      </if>
      <if test="record.sex != null">
        sex = #{record.sex,jdbcType=SMALLINT, typeHandler=com.learn.zqw.handler.SexEnumTypeHandler},
      </if>
    </set>
    <if test="_parameter != null">
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByExample" parameterType="map">
    update bird
    set id = #{record.id,jdbcType=INTEGER},
      name = #{record.name,jdbcType=VARCHAR},
      sex = #{record.sex,jdbcType=SMALLINT, typeHandler=com.learn.zqw.handler.SexEnumTypeHandler}
    <if test="_parameter != null">
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByPrimaryKeySelective" parameterType="com.learn.zqw.generator.domain.Bird">
    update bird
    <set>
      <if test="name != null">
        name = #{name,jdbcType=VARCHAR},
      </if>
      <if test="sex != null">
        sex = #{sex,jdbcType=SMALLINT, typeHandler=com.learn.zqw.handler.SexEnumTypeHandler},
      </if>
    </set>
    where id = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.learn.zqw.generator.domain.Bird">
    update bird
    set name = #{name,jdbcType=VARCHAR},
    sex = #{sex,jdbcType=SMALLINT, typeHandler=com.learn.zqw.handler.SexEnumTypeHandler}
    where id = #{id,jdbcType=INTEGER}
  </update>
</mapper>

7、编写单元测试

package com.learn.zqw;

import com.learn.zqw.generator.domain.Bird;
import com.learn.zqw.generator.domain.BirdExample;
import com.learn.zqw.generator.domain.SexEnum;
import com.learn.zqw.generator.mapper.BirdMapper;
import lombok.Cleanup;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 *
 * @author zhuquanwen
 * @vesion 1.0
 * @date 2020/3/16 22:14
 * @since jdk1.8
 */
@RunWith(JUnit4.class)
public class TypeHandlerTests {

    /**
     * 测试查询所有
     * */
    @Test
    public void test() throws IOException {
        @Cleanup InputStream in = Resources.getResourceAsStream ("SqlMapConfig.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        SqlSession session = factory.openSession();
        BirdMapper mapper = session.getMapper(BirdMapper.class);
        BirdExample birdExample = new BirdExample();
        List<Bird> birds = mapper.selectByExample(birdExample);
        Assert.assertNotNull(birds);
        if (birds != null) {
            birds.forEach(System.out::println);
        }

        Bird bird = new Bird();
        bird.setName("小紫");
        bird.setSex(SexEnum.MALE);
        mapper.insert(bird);
        session.commit();

    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值