MyBatis(15)——类型处理器typeHandlers

目录

一、类型处理器

1、typeHandler类型处理器具体用处:

2、typeHandler结构:

3、默认的类型处理器

二、自定义类型处理器处理枚举

1、MyBatis提供的两个枚举处理器

2、定义枚举类型处理器


一、类型处理器

1、typeHandler类型处理器具体用处:

MyBatis在与数据库交互的时候,Executor对象会调用ParameterHandler对象进行设置参数和ResultSetHandler对象处理数据库查询返回的数据。而这两个主要的操作都需要经过typeHandler来处理:

//ParameterHandler
typeHandler.setParameter(ps, i + 1, value, jdbcType);


//ResultSetHandler
typeHandler.getResult(rs, column);

2、typeHandler结构:

类型处理器一般为实现org.apache.ibatis.type.TypeHandler 接口,或继承 org.apache.ibatis.type.BaseTypeHandler抽象类。

(1)org.apache.ibatis.type.TypeHandler 接口

public interface TypeHandler<T> {

  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  T getResult(ResultSet rs, String columnName) throws SQLException;

  T getResult(ResultSet rs, int columnIndex) throws SQLException;

  T getResult(CallableStatement cs, int columnIndex) throws SQLException;

}

接口中有四个方法,只有两个功能,设置参数setParameter和获取结果getResult。getResult有三种不同的获取方式,根据列名称获取、根据列的索引获取、从存储过程中获取。

(2) org.apache.ibatis.type.BaseTypeHandler抽象类

public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
    //...
}

BaseTypeHandler同样实现了TypeHandler 接口,重写了接口中的方法,并且为我们实现了其中的逻辑。其中多出来的四个方法:setNonNullParameter、getNullableResult为精细化的处理器提供进一步逻辑处理接口。

MyBatis框架为我们提供的BaseTypeHandler完成了大部分的功能,因此不同类型处理器只需要继承这个BaseTypeHandler就可以了。

例如下方的处理String的StringTypeHandler:

(3)示例:StringTypeHandler

StringTypeHandler继承了BaseTypeHandler,只要重写BaseTypeHandler种额外提供的抽象方法就可以。底层依然是jdbc的基本操作。

public class StringTypeHandler extends BaseTypeHandler<String> {

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

3、默认的类型处理器

类型处理器Java 类型JDBC 类型
BooleanTypeHandlerjava.lang.Boolean, boolean数据库兼容的 BOOLEAN
ByteTypeHandlerjava.lang.Byte, byte数据库兼容的 NUMERIC 或 BYTE
ShortTypeHandlerjava.lang.Short, short数据库兼容的 NUMERIC 或 SHORT INTEGER
IntegerTypeHandlerjava.lang.Integer, int数据库兼容的 NUMERIC 或 INTEGER
LongTypeHandlerjava.lang.Long, long数据库兼容的 NUMERIC 或 LONG INTEGER
FloatTypeHandlerjava.lang.Float, float数据库兼容的 NUMERIC 或 FLOAT
DoubleTypeHandlerjava.lang.Double, double数据库兼容的 NUMERIC 或 DOUBLE
BigDecimalTypeHandlerjava.math.BigDecimal数据库兼容的 NUMERIC 或 DECIMAL
StringTypeHandlerjava.lang.StringCHAR, VARCHAR
ClobReaderTypeHandlerjava.io.Reader-
ClobTypeHandlerjava.lang.StringCLOB, LONGVARCHAR
NStringTypeHandlerjava.lang.StringNVARCHAR, NCHAR
NClobTypeHandlerjava.lang.StringNCLOB
BlobInputStreamTypeHandlerjava.io.InputStream-
ByteArrayTypeHandlerbyte[]数据库兼容的字节流类型
BlobTypeHandlerbyte[]BLOB, LONGVARBINARY
DateTypeHandlerjava.util.DateTIMESTAMP
DateOnlyTypeHandlerjava.util.DateDATE
TimeOnlyTypeHandlerjava.util.DateTIME
SqlTimestampTypeHandlerjava.sql.TimestampTIMESTAMP
SqlDateTypeHandlerjava.sql.DateDATE
SqlTimeTypeHandlerjava.sql.TimeTIME
ObjectTypeHandlerAnyOTHER 或未指定类型
EnumTypeHandlerEnumeration TypeVARCHAR-任何兼容的字符串类型,存储枚举的名称(而不是索引)
EnumOrdinalTypeHandlerEnumeration Type任何兼容的 NUMERIC 或 DOUBLE 类型,存储枚举的索引(而不是名称)。
InstantTypeHandlerjava.time.InstantTIMESTAMP
LocalDateTimeTypeHandlerjava.time.LocalDateTimeTIMESTAMP
LocalDateTypeHandlerjava.time.LocalDateDATE
LocalTimeTypeHandlerjava.time.LocalTimeTIME
OffsetDateTimeTypeHandlerjava.time.OffsetDateTimeTIMESTAMP
OffsetTimeTypeHandlerjava.time.OffsetTimeTIME
ZonedDateTimeTypeHandlerjava.time.ZonedDateTimeTIMESTAMP
YearTypeHandlerjava.time.YearINTEGER
MonthTypeHandlerjava.time.MonthINTEGER
YearMonthTypeHandlerjava.time.YearMonthVARCHAR or LONGVARCHAR
JapaneseDateTypeHandlerjava.time.chrono.JapaneseDateDATE

二、自定义类型处理器处理枚举

枚举的知识复习参考:https://blog.csdn.net/shaohe18362202126/article/details/86766999

1、MyBatis提供的两个枚举处理器

EnumTypeHandler与EnumOrdinalTypeHandler

EnumTypeHandler与EnumOrdinalTypeHandler同样继承了BaseTypeHandler,具体的逻辑在BaseTypeHandler提供的四个方法中。

(1)EnumTypeHandler

EnumTypeHandler接收枚举类型,但是存储在数据库中是枚举的名称即name属性,从数据库中获取值后根据name获取枚举的对象。

setNonNullParameter方法:parameter.name()获取枚举参数的name

@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
	if (jdbcType == null) {
		ps.setString(i, parameter.name());
	} else {
		ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE); // see r3589
	}
}

getNullableResult方法:Enum.valueOf(type, s)根据枚举类型和枚举名称获取枚举对象,type为枚举的class。

private Class<E> type;

@Override
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
	String s = rs.getString(columnName);
	return s == null ? null : Enum.valueOf(type, s);
}

(2)EnumOrdinalTypeHandler

EnumOrdinalTypeHandler是将枚举对象转换为枚举对象的索引值,接收结果时候同样根据索引值转换成枚举对象。

setNonNullParameter方法:parameter.ordinal()获取枚举参数的ordinal

@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
	ps.setInt(i, parameter.ordinal());
}

getNullableResult方法:根据索引从枚举数组enums中获取枚举对象。

private final E[] enums;

@Override
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
	int i = rs.getInt(columnName);
	if (rs.wasNull()) {
		return null;
	} else {
		try {
			return enums[i];
		} catch (Exception ex) {
			throw new IllegalArgumentException(
					"Cannot convert " + i + " to " + type.getSimpleName() + " by ordinal value.", ex);
		}
	}
}

(3)两者切换

示例:

增加employee的枚举对象:

public enum StatusEnum{
    LOGIN, LOGOUT, UMKOWN;
}

employee对象中包含此属性:

public class Employee implements Serializable {

    //其他属性...
    
    private StatusEnum status;
    
    public StatusEnum getStatus() {
		return status;
	}

	public void setStatus(StatusEnum status) {
		this.status = status;
	}

}

首先,MyBatis默认对枚举的处理器为EnumTypeHandler,数据库中的枚举保存为其名称name的值。:

EnumTypeHandlerEnumOrdinalTypeHandler的切换:

方法一:全局配置

在mybatis全局配置文件中增加配置:

<typeHandlers>
	<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler"
		javaType="com.starfall.mybaits.typehandler.StatusEnum" />
</typeHandlers>

javaType属性表示该类型处理器只在com.starfall.mybaits.typehandler.StatusEnum时候生效,对于其他的枚举还是使用默认的EnumTypeHandler。

或者在类型处理器的类上(TypeHandler class)增加一个 @MappedTypes 注解来指定与其关联的 Java 类型列表

方法二:精细化配置

在具体的SQL中的具体的字段上配置需要调用的typeHandler

insert和update语句中:

<insert id="addEmpEnum" parameterType="com.starfall.mybaits.entity.Employee">
	INSERT INTO employee (first_name, last_name, email, status)
	VALUES
		(
			#{firstName,jdbcType=VARCHAR},
			#{lastName,jdbcType=VARCHAR},
			#{email,jdbcType=VARCHAR},
			#{status,typeHandler=org.apache.ibatis.type.EnumOrdinalTypeHandler}
		)
</insert>

select中:在对应的resultMap中配置typeHandler


<select id="getEmpEnumById" resultMap="EmpEnum">
	SELECT
		*
	FROM
		employee
	WHERE
		employee_id = #{employeeId}
</select>

<resultMap id="EmpEnum" type="com.starfall.mybaits.entity.Employee">
	<id column="employee_id" jdbcType="INTEGER" property="employeeId" />
	<result column="first_name" jdbcType="VARCHAR" property="firstName" />
	<result column="last_name" jdbcType="VARCHAR" property="lastName" />
	<result column="email" jdbcType="VARCHAR" property="email" />
	<result column="phone_number" jdbcType="VARCHAR" property="phoneNumber" />
	<result column="hire_date" jdbcType="DATE" property="hireDate" />
	<result column="job_id" jdbcType="VARCHAR" property="jobId" />
	<result column="salary" jdbcType="DECIMAL" property="salary" />
	<result column="manager_id" jdbcType="INTEGER" property="managerId" />
	<result column="department_id" jdbcType="INTEGER" property="departmentId" />
	<result column="status" property="status" jdbcType="VARCHAR" typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>
</resultMap>

2、定义枚举类型处理器

升级枚举StatusEnum的功能:

增加两个属性code和msg。

public enum StatusEnum {

	LOGIN(100, "登录"), LOGOUT(200, "登出"), UMKOWN(300, "未知");

	// 自定义的属性code
	private final Integer code;
	// 自定义的属性msg
	private final String msg;

	// 自定义的构造函数,传入属性
	private StatusEnum(Integer code, String msg) {
		this.code = code;
		this.msg = msg;
	}

	public Integer getCode() {
		return code;
	}

	public String getMsg() {
		return msg;
	}

	/**
	 * 根据code获取枚举对象
	 * 
	 * @param code
	 * @return
	 */
	public static StatusEnum getEnumByCode(Integer code) {
		StatusEnum status = null;
		Class<?> clasz = StatusEnum.class;
		if (clasz.isEnum()) {
			// 返回该枚举类型的所有元素,如果Class对象不是枚举类型,则返回null。
			StatusEnum[] values = (StatusEnum[]) clasz.getEnumConstants();
			for (StatusEnum statusEnum : values) {
				if (statusEnum.getCode() == code) {
					status = statusEnum;
				}
			}
		}
		return status;
	}
}

需求:数据库中保存StatusEnum的code,而不是枚举的name和ordinal了

(1)创建上述的枚举对象,并提供根据code获取枚举对象的方法。

(2)自定义类型处理器,继承BaseTypeHandler类。

实现四个方法中的逻辑,设置参数为枚举的code、根据code获取枚举对象。

public class StatusEnumTypeHandler extends BaseTypeHandler<StatusEnum> {

	@Override
	public void setNonNullParameter(PreparedStatement ps, int i, StatusEnum parameter, JdbcType jdbcType)
			throws SQLException {
		ps.setString(i, parameter.getCode().toString());
	}

	@Override
	public StatusEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {
		int code = rs.getInt(columnName);
		return StatusEnum.getEnumByCode(code);
	}

	@Override
	public StatusEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
		int code = rs.getInt(columnIndex);
		return StatusEnum.getEnumByCode(code);
	}

	@Override
	public StatusEnum getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
		int code = cs.getInt(columnIndex);
		return StatusEnum.getEnumByCode(code);
	}
}

(3)配置自定义的类型处理器。可以参考上方的全局配置和具体到字段的配置:

<insert id="addEmpEnum" parameterType="com.starfall.mybaits.entity.Employee">
	INSERT INTO employee (first_name, last_name, email, status)
	VALUES
		(
			#{firstName,jdbcType=VARCHAR},
			#{lastName,jdbcType=VARCHAR},
			#{email,jdbcType=VARCHAR},
			#{status,typeHandler=com.starfall.mybaits.typehandler.StatusEnumTypeHandler}
		)
</insert>

<select id="getEmpEnumById" resultMap="EmpEnum">
	SELECT
		*
	FROM
		employee
	WHERE
		employee_id = #{employeeId}
</select>

<resultMap id="EmpEnum" type="com.starfall.mybaits.entity.Employee">
	<id column="employee_id" jdbcType="INTEGER" property="employeeId" />
	<result column="first_name" jdbcType="VARCHAR" property="firstName" />
	<result column="last_name" jdbcType="VARCHAR" property="lastName" />
	<result column="email" jdbcType="VARCHAR" property="email" />
	<result column="phone_number" jdbcType="VARCHAR" property="phoneNumber" />
	<result column="hire_date" jdbcType="DATE" property="hireDate" />
	<result column="job_id" jdbcType="VARCHAR" property="jobId" />
	<result column="salary" jdbcType="DECIMAL" property="salary" />
	<result column="manager_id" jdbcType="INTEGER" property="managerId" />
	<result column="department_id" jdbcType="INTEGER" property="departmentId" />
	<result column="status" property="status" jdbcType="VARCHAR" typeHandler="com.starfall.mybaits.typehandler.StatusEnumTypeHandler"/>
</resultMap>

(5)测试:

新增的数据:

查询的数据:

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值