Mybatis[3] - 配置文件 - typeHandler

本文详细介绍了Mybatis中的TypeHandler,包括其作用、如何自定义TypeHandler的案例,以及涉及到的相关源码解析,如SqlSessionFactory、XMLConfigBuilder等。通过对执行路径的分析,深入理解TypeHandler在Mybatis中的工作原理。
摘要由CSDN通过智能技术生成

博由

上一篇主要讲述了基本的mybatis的配置文件使用,但是没有详细针对typeHandler, plugins, objectFactory三个标签如何使用进行阐述,主要原因是因为这三个东西都是可以自定义的,其作用涉及到一些mybatis的高级功能。本文主要阐述typeHandler的使用方式。

什么是TypeHandler

    顾名思义:类型处理器;既然是处理类型的,那么处理什么类型,如何处理类型,有什么作用,才是我们需要关心的问题。
ORM:最大的问题在于使用Java实体类替换直白的SQL Table,换句话说通常一个实体类对应一个Table;也就是我们可以通过操作Class来替换操作Table的方式,就不需要关系这是Oracle的Table还是MYSQL的Table或者其他数据库的Table。那么这里最大的问题就在于javaType和jdbcType之间的映射关系,简单来说就是:实体类的类型如何与数据库的字段类型进行匹配。这样我们在更新数据时,查询数据时可以进行转换,然后直接使用实体类。
预定义映射关系:[摘自源码:TypeHandlerRegistry.java]
构造初始化函数:Key:类型;value:类型处理器
public TypeHandlerRegistry() {
    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());

    register(Byte.class, new ByteTypeHandler());
    register(byte.class, new ByteTypeHandler());
    register(JdbcType.TINYINT, new ByteTypeHandler());

    register(Short.class, new ShortTypeHandler());
    register(short.class, new ShortTypeHandler());
    register(JdbcType.SMALLINT, new ShortTypeHandler());

    register(Integer.class, new IntegerTypeHandler());
    register(int.class, new IntegerTypeHandler());
    register(JdbcType.INTEGER, new IntegerTypeHandler());

    register(Long.class, new LongTypeHandler());
    register(long.class, new LongTypeHandler());

    register(Float.class, new FloatTypeHandler());
    register(float.class, new FloatTypeHandler());
    register(JdbcType.FLOAT, new FloatTypeHandler());

    register(Double.class, new DoubleTypeHandler());
    register(double.class, new DoubleTypeHandler());
    register(JdbcType.DOUBLE, new DoubleTypeHandler());

    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());

    register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
    register(JdbcType.ARRAY, new ArrayTypeHandler());

    register(BigInteger.class, new BigIntegerTypeHandler());
    register(JdbcType.BIGINT, new LongTypeHandler());

    register(BigDecimal.class, new BigDecimalTypeHandler());
    register(JdbcType.REAL, new BigDecimalTypeHandler());
    register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
    register(JdbcType.NUMERIC, new BigDecimalTypeHandler());

    register(InputStream.class, new BlobInputStreamTypeHandler());
    register(Byte[].class, new ByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
    register(byte[].class, new ByteArrayTypeHandler());
    register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
    register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.BLOB, new BlobTypeHandler());

    register(Object.class, UNKNOWN_TYPE_HANDLER);
    register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
    register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);

    register(Date.class, new DateTypeHandler());
    register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
    register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
    register(JdbcType.TIMESTAMP, new DateTypeHandler());
    register(JdbcType.DATE, new DateOnlyTypeHandler());
    register(JdbcType.TIME, new TimeOnlyTypeHandler());

    register(java.sql.Date.class, new SqlDateTypeHandler());
    register(java.sql.Time.class, new SqlTimeTypeHandler());
    register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());

    // mybatis-typehandlers-jsr310
    try {
      // since 1.0.0
      register("java.time.Instant", "org.apache.ibatis.type.InstantTypeHandler");
      register("java.time.LocalDateTime", "org.apache.ibatis.type.LocalDateTimeTypeHandler");
      register("java.time.LocalDate", "org.apache.ibatis.type.LocalDateTypeHandler");
      register("java.time.LocalTime", "org.apache.ibatis.type.LocalTimeTypeHandler");
      register("java.time.OffsetDateTime", "org.apache.ibatis.type.OffsetDateTimeTypeHandler");
      register("java.time.OffsetTime", "org.apache.ibatis.type.OffsetTimeTypeHandler");
      register("java.time.ZonedDateTime", "org.apache.ibatis.type.ZonedDateTimeTypeHandler");
      // since 1.0.1
      register("java.time.Month", "org.apache.ibatis.type.MonthTypeHandler");
      register("java.time.Year", "org.apache.ibatis.type.YearTypeHandler");
      // since 1.0.2
      register("java.time.YearMonth", "org.apache.ibatis.type.YearMonthTypeHandler");
      register("java.time.chrono.JapaneseDate", "org.apache.ibatis.type.JapaneseDateTypeHandler");

    } catch (ClassNotFoundException e) {
      // no JSR-310 handlers
    }

    // issue #273
    register(Character.class, new CharacterTypeHandler());
    register(char.class, new CharacterTypeHandler())

思考:

默认情况下,mybatis会帮助我们完成一般类型(预定义类型)的转化过程,那么在某些情况下,需要一些特殊类型的处理,就没办法满足了,这个时候,就需要自定义类型处理器,并设定在特定场合触发类型转换。

自定义TypeHandler

我们描述了typeHandler的产生背景,接下来我们需要重点阐述的是如何使用和深度理解其内部机制。

案例

数据库表:
CREATE TABLE `user1` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `name` varchar(128) NOT NULL DEFAULT '' COMMENT '名称',
  `created_at` varchar(10) NOT NULL DEFAULT '' COMMENT '创建时间',
  `updated_at` varchar(10) NOT NULL DEFAULT '' COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='用户表 - 测试typeHandler';

满足:插入时写入Date数据,存储为字符串时间戳,查询获取时为Date类型;
实现自定义时间类型处理器
package com.typehandler.handlers;

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.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Created by wangzhiping on 17/3/1.
 */


// 设置映射的数据库类型,必须是JdbcType指定类型
@MappedJdbcTypes(JdbcType.VARCHAR)
// 设置映射的java类型,主要是java的类型
@MappedTypes(value=java.util.Date.class)
// 如果没有在javacode中指定javaType 和 javaType那么需要配置文件指定,如果制定了可以不需要再制定
public class MyDateTypeHandler extends BaseTypeHandler<Date>{
     // 继承BaseTypeHandler,期泛型就是需要转化的类型

    /**
     * 该函数:
     * @param ps jdbc中的PreparedStatement对象
     * @param i 数据的位置索引
     * @param parameter 需要插入的数据参数(一般是执行更新、删除、插入时调用)
     * @param jdbcType 数据库类型
     * @throws SQLException
     */
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {
        // 注入值时
        Long timestamp = parameter.getTime() / 1000;
        ps.setString(i, timestamp.toString());
    }


    @Override
    public Date getNullableResult(ResultSet rs, String columnName) throws SQLException {
        // 从数据库中获取数据
        String timestamp = rs.getString(columnName);
        return new Date(Long.parseLong(timestamp) * 1000);
    }

    @Override
    public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException {

        String timestamp = rs.getString(columnIndex);
        return new Date(Long.parseLong(timestamp) * 1000);
    }

    @Override
    public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String timestamp = cs.getString(columnIndex);
        return new Date(Long.parseLong(timestamp) * 1000);
    }
}
实体类
    package com.typehandler.pojo;


import org.apache.ibatis.type.Alias;

import java.io.Serializable;
import java.util.Date;

/**
 * Created by wangzhiping on 17/2/21.
 */
public class User1 implements Serializable{
   

    private Integer id;

    private String name;

    private Date createdAt;

    private Date updatedAt;

    public User1() {
    }

    public User1(Integer id, String name, Date createdAt, Date updatedAt) {
        this.id = id;
        this.name = name;
        this.createdAt = createdAt;
        this.updatedAt = updatedAt;
    }

    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;
    }

    public Date getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Date createdAt) {
        this.createdAt = createdAt;
    }

    public Date getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(Date updatedAt) {
        this.updatedAt = updatedAt;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", createdAt=" + createdAt +
                ", updatedAt=" + updatedAt +
                '}';
    }
}
配置typeHandler
<!-- 配置typehandlers -->
    <typeHandlers>
        <package name="com.typehandler.handlers" />
        <!-- <typeHandler handler="com.typehandler.handlers.MyDateTypeHandler" /> -->
    </typeHandlers>
 其中package和typeHandler的概念与typeAliases中的package和typeAlias一致。
使用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.typehandler.pojo.User1">

    <insert id="add" parameterType="User1">
        INSERT INTO user1
        VALUES(null, #{name},
        #{createdAt, javaType=java.util.Date, jdbcType=VARCHAR, typeHandler=com.typehandler.handlers.MyDateTypeHandler},
        #{createdAt, javaType=java.util.Date, jdbcType=VARCHAR, typeHandler=com.typehandler.handlers.MyDateTypeHandler})
    </insert>

    <resultMap id="user1Map" type="User1">
        <id property="id" column="id"/>
        <result property="name" column="name"/>

        <!-- 需要做转换的字段必须指定typeHandler -->
        <result property="createdAt" column="created_at" typeHandler="com.typehandler.handlers.MyDateTypeHandler"/>
        <result property="updatedAt" column="updated_at" typeHandler="com.typehandler.handlers.MyDateTypeHandler"/>
    </resultMap>

    <select id="findById" parameterType="int" resultMap="user1Map">
        SELECT *
        FROM user1
        WHERE id=#{id}
    </select>
</<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值