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 {

  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 extends TypeReference implements TypeHandler {
    //...
}

MyBatis(15)——类型处理器typeHandlers_第1张图片

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

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

例如下方的处理String的StringTypeHandler:

(3)示例:StringTypeHandler

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

public class StringTypeHandler extends BaseTypeHandler {

  @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 类型
BooleanTypeHandler java.lang.Boolean, boolean 数据库兼容的 BOOLEAN
ByteTypeHandler java.lang.Byte, byte 数据库兼容的 NUMERIC 或 BYTE
ShortTypeHandler java.lang.Short, short 数据库兼容的 NUMERIC 或 SHORT INTEGER
IntegerTypeHandler java.lang.Integer, int 数据库兼容的 NUMERIC 或 INTEGER
LongTypeHandler java.lang.Long, long 数据库兼容的 NUMERIC 或 LONG INTEGER
FloatTypeHandler java.lang.Float, float 数据库兼容的 NUMERIC 或 FLOAT
DoubleTypeHandler java.lang.Double, double 数据库兼容的 NUMERIC 或 DOUBLE
BigDecimalTypeHandler java.math.BigDecimal 数据库兼容的 NUMERIC 或 DECIMAL
StringTypeHandler java.lang.String CHAR, VARCHAR
ClobReaderTypeHandler java.io.Reader -
ClobTypeHandler java.lang.String CLOB, LONGVARCHAR
NStringTypeHandler java.lang.String NVARCHAR, NCHAR
NClobTypeHandler java.lang.String NCLOB
BlobInputStreamTypeHandler java.io.InputStream -
ByteArrayTypeHandler byte[] 数据库兼容的字节流类型
BlobTypeHandler byte[] BLOB, LONGVARBINARY
DateTypeHandler java.util.Date TIMESTAMP
DateOnlyTypeHandler java.util.Date DATE
TimeOnlyTypeHandler java.util.Date TIME
SqlTimestampTypeHandler java.sql.Timestamp TIMESTAMP
SqlDateTypeHandler java.sql.Date DATE
SqlTimeTypeHandler java.sql.Time TIME
ObjectTypeHandler Any OTHER 或未指定类型
EnumTypeHandler Enumeration Type VARCHAR-任何兼容的字符串类型,存储枚举的名称(而不是索引)
EnumOrdinalTypeHandler Enumeration Type 任何兼容的 NUMERIC 或 DOUBLE 类型,存储枚举的索引(而不是名称)。
InstantTypeHandler java.time.Instant TIMESTAMP
LocalDateTimeTypeHandler java.time.LocalDateTime TIMESTAMP
LocalDateTypeHandler java.time.LocalDate DATE
LocalTimeTypeHandler java.time.LocalTime TIME
OffsetDateTimeTypeHandler java.time.OffsetDateTime TIMESTAMP
OffsetTimeTypeHandler java.time.OffsetTime TIME
ZonedDateTimeTypeHandler java.time.ZonedDateTime TIMESTAMP
YearTypeHandler java.time.Year INTEGER
MonthTypeHandler java.time.Month INTEGER
YearMonthTypeHandler java.time.YearMonth VARCHAR or LONGVARCHAR
JapaneseDateTypeHandler java.time.chrono.JapaneseDate DATE

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

枚举的知识复习参考: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 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全局配置文件中增加配置:


	

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

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

方法二:精细化配置

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

insert和update语句中:


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

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





	
	
	
	
	
	
	
	
	
	
	

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 {

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





	
	
	
	
	
	
	
	
	
	
	

(5)测试:

新增的数据:

查询的数据:

 

 

你可能感兴趣的:(MyBatis系列)