日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

MyBatis自定义类型处理器 TypeHandler

發(fā)布時間:2024/4/14 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MyBatis自定义类型处理器 TypeHandler 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在項目開發(fā)中經(jīng)常會遇到一個問題:

當(dāng)我們在javabean中自定義了枚舉類型或者其它某個類型,但是在數(shù)據(jù)庫中存儲時往往需要轉(zhuǎn)換成數(shù)據(jù)庫對應(yīng)的類型,并且在從數(shù)據(jù)庫中取出來時也需要將數(shù)據(jù)庫類型轉(zhuǎn)換為javabean中的對應(yīng)類型。比如:javabean中字段類型為Date,數(shù)據(jù)庫中存儲的是varchar類型;javabean中字段類型是Enum,數(shù)據(jù)庫中存儲的是String或者Integer。
因為有大量類似數(shù)據(jù)的轉(zhuǎn)換,手動轉(zhuǎn)換類型進(jìn)行存儲和查詢已經(jīng)過于麻煩。MyBatis為我們提供了解決辦法:TypeHandler類型處理器。

類型處理器 TypeHandler

MyBatis 中的 TypeHandler 類型處理器用于 JavaType 與 JdbcType 之間的轉(zhuǎn)換,用于 PreparedStatement 設(shè)置參數(shù)值和從 ResultSet 或 CallableStatement 中取出一個值。MyBatis 內(nèi)置了大部分基本類型的類型處理器,所以對于基本類型可以直接處理,當(dāng)我們需要處理其他類型的時候就需要自定義類型處理器。

MyBatis 內(nèi)置的 TypeHandler

在 MyBatis 的 TypeHandlerRegistry 類型中,可以看到內(nèi)置的類型處理器。內(nèi)置處理器比較多,這里整理常見的一些。
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 類型,內(nèi)置了 EnumTypeHandler 進(jìn)行 Enum 名稱的轉(zhuǎn)換和 EnumOrdinalTypeHandler 進(jìn)行 Enum 序數(shù)的轉(zhuǎn)換。這兩個類型處理器沒有在 TypeHandlerRegistry 中注冊,如果需要使用必須手動配置。

自定義 TypeHandler

自定義類型處理器是通過實現(xiàn) org.apache.ibatis.type.TypeHandler 接口實現(xiàn)的。這個接口定義了類型處理器的基本功能,接口定義如下所示。

其中 setParameter 方法用于把 java 對象設(shè)置到 PreparedStatement 的參數(shù)中,getResult 方法用于從 ResultSet(根據(jù)列名或者索引位置獲取) 或 CallableStatement(根據(jù)存儲過程獲取) 中取出數(shù)據(jù)轉(zhuǎn)換為 java 對象。

實際開發(fā)中,我們可以繼承 org.apache.ibatis.type.BaseTypeHandler 類型來實現(xiàn)自定義類型處理器。這個類型是抽象類型,實現(xiàn)了 TypeHandler 的方法進(jìn)行通用流程的封裝,做了異常處理,并定義了幾個類似的抽象方法,如下所示。繼承 BaseTypeHandler 類型可以極大地降低開發(fā)難度。


類型轉(zhuǎn)換器還可以通過注解配置 java 類型和 jdbc 類型:

@MappedTypes:注解配置 java 類型
@MappedJdbcTypes:注解配置 jdbc 類型

自定義枚舉類型處理器示例:

自定義一個枚舉基類

import org.springframework.util.ReflectionUtils; import java.lang.reflect.Field;/*** @author: liumengbing* @date: 2019/05/20 15:37**/ public interface BaseEnum {String DEFAULT_VALUE_NAME = "value";String DEFAULT_LABEL_NAME = "label";default Integer getValue() {Field field = ReflectionUtils.findField(this.getClass(), DEFAULT_VALUE_NAME);if (field == null)return null;try {field.setAccessible(true);return Integer.parseInt(field.get(this).toString());} catch (IllegalAccessException e) {throw new RuntimeException(e);}}default String getLabel() {Field field = ReflectionUtils.findField(this.getClass(), DEFAULT_LABEL_NAME);if (field == null)return null;try {field.setAccessible(true);return field.get(this).toString();} catch (IllegalAccessException e) {throw new RuntimeException(e);}}static <T extends Enum<T>> T valueOfEnum(Class<T> enumClass, Integer value) {if (value == null)throw new IllegalArgumentException("DisplayedEnum value should not be null");if (enumClass.isAssignableFrom(com.zfkr.qianyue.common.enums.BaseEnum.class))throw new IllegalArgumentException("illegal DisplayedEnum type");T[] enums = enumClass.getEnumConstants();for (T t: enums) {com.zfkr.qianyue.common.enums.BaseEnum displayedEnum = (com.zfkr.qianyue.common.enums.BaseEnum)t;if (displayedEnum.getValue().equals(value))return (T) displayedEnum;}throw new IllegalArgumentException("cannot parse integer: " + value + " to " + enumClass.getName());}static <T> T valueOfEnum1(T[] enums, Integer value) {for (T t: enums) {com.zfkr.qianyue.common.enums.BaseEnum displayedEnum = (com.zfkr.qianyue.common.enums.BaseEnum)t;if (displayedEnum.getValue().equals(value))return (T) displayedEnum;}throw new IllegalArgumentException("cannot parse integer: " + value + " to " );} }

定義一個枚舉類Enum1

/*** @author: liumengbing* @date: 2019/05/20 15:34**/ public enum Enum1 implements BaseEnum{UNAUDITED("未審核",0),AUDIT("待審核",1),AUDITED("已審核",2);private String label;private Integer value;Enum1(String label,Integer value){this.label = label;this.value = value;}public Integer getValue() {return value;}public String getLabel() {return label;}public static Enum1 getByValue(Integer value){for(Enum1 enum1 : values()){if (enum1.getValue() == value) {return enum1;}}return null;}}

自定義枚舉類型處理器

/*** @author: liumengbing* @date: 2019/05/20 15:34**/ @MappedTypes(value = { Enum1.class, Enum2.class}) public class EnumTypeHandler extends BaseTypeHandler<BaseEnum> {private Class<BaseEnum> type;public EnumTypeHandler() {}public EnumTypeHandler(Class<BaseEnum> type) {if (type == null) throw new IllegalArgumentException("Type argument cannot be null");this.type = type;}@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, BaseEnum parameter, JdbcType jdbcType) throws SQLException {ps.setInt(i, parameter.getValue());}@Overridepublic BaseEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {return convert(rs.getInt(columnName));}@Overridepublic BaseEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException {return convert(rs.getInt(columnIndex));}@Overridepublic BaseEnum getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {return convert(cs.getInt(columnIndex));}private BaseEnum convert(int status) {BaseEnum[] objs = type.getEnumConstants();for (BaseEnum em : objs) {if (em.getValue() == status) {return em;}}return null;} }

Javabean,Dao層,mapper層處理忽略,將自定義的類型處理器配置到程序中即可。

把TypeHandler配置到程序中有三種方法:

1.在Mapper.xml中聲明

<result column="enum1" jdbcType="INTEGER" property="enum1" typeHandler="com.xxx.handler.EnumTypeHandler"/>

2.在mybatis配置文件中設(shè)置

<typeHandlers><typeHandler handler="com.xxx.handler.EnumTypeHandler"/></typeHandlers>

3.在springboot的yml配置文件中設(shè)置類型處理器所在的包名

mybatis:type-handlers-package: com.xxx.handler

源碼分析

MyBatis 啟動后首先把 typeHandler 注冊進(jìn)去。首先嘗試讀取 MappedTypes 注解,如果有這個注解定義了 java 類型,則把這個類型處理器注冊到相應(yīng)的 java 類型的處理器中。如果沒有使用注解,但是繼承了 TypeReference 類型,比如前面提到的 BaseTypeHandler,則通過 TypeReference 的接口獲取原始類型注冊到相應(yīng)的 java 類型的處理器中。如果實在是獲取不到 java 類型,則按照無類型處理。

public <T> void register(TypeHandler<T> typeHandler) {boolean mappedTypeFound = false;MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);if (mappedTypes != null) {for (Class<?> handledType : mappedTypes.value()) {register(handledType, typeHandler);mappedTypeFound = true;}}// @since 3.1.0 - try to auto-discover the mapped typeif (!mappedTypeFound && typeHandler instanceof TypeReference) {try {TypeReference<T> typeReference = (TypeReference<T>) typeHandler;register(typeReference.getRawType(), typeHandler);mappedTypeFound = true;} catch (Throwable t) {// maybe users define the TypeReference with a different type and are not assignable, so just ignore it}}if (!mappedTypeFound) {register((Class<T>) null, typeHandler);}}

MyBatis 在預(yù)處理語句設(shè)置參數(shù)時調(diào)用 TypeHandler 進(jìn)行 java 對象到 jdbc 的 PreparedStatement 參數(shù)值的轉(zhuǎn)換。以下為其中一個調(diào)用片段。

TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) {jdbcType = configuration.getJdbcTypeForNull(); } try {typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (TypeException e) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } catch (SQLException e) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); }

MyBatis 查詢數(shù)據(jù)庫完成后,調(diào)用 TypeHandler 的方法讀取數(shù)據(jù)轉(zhuǎn)換成 java 對象。以下為其中一個調(diào)用片段。

private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)throws SQLException {if (propertyMapping.getNestedQueryId() != null) {return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);} else if (propertyMapping.getResultSet() != null) {addPendingChildRelation(rs, metaResultObject, propertyMapping); // TODO is that OK?return DEFERED;} else {final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);return typeHandler.getResult(rs, column);} }

參考資料:
mybatis中幾種typeHandler的定義使用
MyBatis 類型處理器 TypeHandler

超強(qiáng)干貨來襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生

總結(jié)

以上是生活随笔為你收集整理的MyBatis自定义类型处理器 TypeHandler的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。