Mybatis中typeHandler类型转换器

本文详细介绍了Mybatis中的TypeHandler,包括其在JDBC中的作用,系统默认的typeHandler,以及如何自定义typeHandler以处理特殊类型的转换规则。文章通过定义枚举类型和实现TypeHandler接口的方式展示了自定义typeHandler的两种方法,并讲解了如何在mybatis配置中使用这些自定义转换器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.typeHandler简介

      在JDBC中,需要在PreparedStatement对象中设置那些已经编译过的SQL语句的参数.执行sql后,会通过ResultSet对象获取得到数据库的数据,而这些Mybatis是根据数据的类型通过typeHandler来实现的.在typeHandler中,分为jdbcType和javaType,其中jdbcType用于定义数据库类型,而javaType用于定义java类型,那么typeHandler的作用就是承担jdbcType和javaType之间的相互转换.

        在mybtis中存在系统定义typeHandler和自定义typeHandler.mybatis会根据JavaType和数据库的jdbcType来决定采用哪个typeHandler处理这些转换规则.系统提供的typeHandler能覆盖大部分场景的要求,但是有些情况下是不够的,比如我们有特殊的转换规则.枚举类型就是这样.

二.系统自定义的typeHandler

Mybatis内部定义了许多有用的typeHandler.,这些转换器都在TypeHandlerRegistry类中注册

我们可以在这个类的构造函数中看到内置的typeHandler

三.自定义typeHandler 

1.定义一个枚举类型

首先我们需要一个枚举类型

public enum SexEnum {
	MALE(1, "男"),
	FEMALE(0, "女");

	private int id;
	private String name;

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

	SexEnum(int id, String name) {
		this.id = id;
		this.name = name;
	}

	public static SexEnum getSexById(int id) {
		for (SexEnum sex : SexEnum.values()) {
			if (sex.getId() == id) {
				return sex;
			}
		}
		return null;
	}
}

2.实现typeHandler接口(第一种实现自定义typeHandler的方法)

在mybatis中要自定义typeHandler,其中一种方法是实现org.apache.ibatis.type.TypeHandler接口

public interface TypeHandler<T> {
    void setParameter(PreparedStatement var1, int var2, T var3, JdbcType var4) throws SQLException;

    T getResult(ResultSet var1, String var2) throws SQLException;

    T getResult(ResultSet var1, int var2) throws SQLException;

    T getResult(CallableStatement var1, int var2) throws SQLException;
}

这个接口中有4个方法

  • 其中T是泛型.专指javaType,比如我们需要string的时候,那么实现类可以写为implements TypeHandler<String>.
  • setParameter方法,是使用typeHandler通过PreparedStatement对象进行设置SQL参数的时候使用的具体方法,其中i是参数在SQL的下标,parameter是参数,jdbcType是数据库类型.
  • 其中有3个getResult的方法,它的作用是从jdbc结果集中获取数据进行转换,要么使用列名(columnName)要么使用下标(columnIndex)获取数据库的数据,其中最后一个getResult方法是存储过程专用的.

下面的代码用于转换刚刚定义的枚举:


public class SexTypeHandler2 implements TypeHandler<SexEnum> {

    public void setParameter(PreparedStatement ps, int i, SexEnum parameter,
                             JdbcType jdbcType) throws SQLException {
        ps.setInt(i, parameter.getId());
    }

    public SexEnum getResult(ResultSet rs, String columnName)
            throws SQLException {
        int id = rs.getInt(columnName);
        return SexEnum.getSexById(id);
    }

    public SexEnum getResult(ResultSet rs, int columnIndex) throws SQLException {
        int id = rs.getInt(columnIndex);
        return SexEnum.getSexById(id);
    }

    public SexEnum getResult(CallableStatement cs, int columnIndex)
            throws SQLException {
        int id = cs.getInt(columnIndex);
        return SexEnum.getSexById(id);
    }

}

 3.实现自定义typeHandler的第二种方法

其实我们看Mybatis系统定义的typeHandler我们会发现他们都是继承了一个叫BaseTypeHandler的类

看一下BaseTypeHandler的源码

public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
    protected Configuration configuration;

    public BaseTypeHandler() {
    }

    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 var7) {
                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: " + var7, var7);
            }
        } else {
            try {
                this.setNonNullParameter(ps, i, parameter, jdbcType);
            } catch (Exception var6) {
                throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . Try setting a different JdbcType for this parameter or a different configuration property. Cause: " + var6, var6);
            }
        } 

    }

    public T getResult(ResultSet rs, String columnName) throws SQLException {
        Object result;
        try {
            result = this.getNullableResult(rs, columnName);
        } catch (Exception var5) {
            throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set.  Cause: " + var5, var5);
        }

        return rs.wasNull() ? null : result;
    }

    public T getResult(ResultSet rs, int columnIndex) throws SQLException {
       ...
    }

    public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
      ...
    }

    public abstract void setNonNullParameter(PreparedStatement var1, int var2, T var3, JdbcType var4) throws SQLException;

    public abstract T getNullableResult(ResultSet var1, String var2) throws SQLException;

    public abstract T getNullableResult(ResultSet var1, int var2) throws SQLException;

    public abstract T getNullableResult(CallableStatement var1, int var2) throws SQLException;
}

简单分析一下这个类(篇幅原因省略部分代码)

  • BaseTypeHandler是个抽象类,需要子类去实现其定义的4个抽象方法.而他本身实现了typeHandler接口的4个方法.
  • getResult方法,非空结果集是通过getNullableResult方法获取的.如果判断为空,则返回null.
  • setParameter方法,当参数parameter和jdbcType同时为空时,Mybatis将抛出异常,如果能明确jdbcType,则会进行行空设置,如果参数不为空,那么它将采用setNonNullParameter方法设置参数.
  • getNullableResult方法用于存储过程

于是我们可以继承这个类实现该类的四个抽象方法来实现自定义typeHandler

public class SexTypeHandler extends BaseTypeHandler<SexEnum> {

    @Override
    public SexEnum getNullableResult(ResultSet rs, String name) throws SQLException {
        int sex = rs.getInt(name);
        return SexEnum.getSexById(sex);
    }

    @Override
    public SexEnum getNullableResult(ResultSet rs, int index) throws SQLException {
        int sex = rs.getInt(index);
        return SexEnum.getSexById(sex);
    }

    @Override
    public SexEnum getNullableResult(CallableStatement cs, int index) throws SQLException {
        int sex = cs.getInt(index);
        return SexEnum.getSexById(sex);
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int index, SexEnum sex, JdbcType jdbcType) throws SQLException {
        ps.setInt(index, sex.getId());
    }
}

 四.配置使用自定义typeHandler

1.第一种方法

在mybatis的主配文件中加入如下配置

handler是自定义typeHandler的路径

    <typeHandlers>
        <typeHandler handler="com.exia.typeHandler.SexTypeHandler2" javaType="SexEnum" jdbcType="int" />
    </typeHandlers>

 如果转化器特别多的话,可以考虑扫描包的形式

    <typeHandlers>
        <package name="com.exia.typeHandler"/>
    </typeHandlers>

这样的话,这个包下的所有typeHandler都会被扫描到,但扫包的话就不能指定JavaType和jdbcType了.

我们需要在自定义的TypeHandler上加入注解指定jdbcType和JavaType

@MappedTypes(SexEnum.class)
@MappedJdbcTypes(JdbcType.INTEGER)

2.第二种方法

在映射时,直接指定typeHandler属性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值