1. 程式人生 > >Mybatis學習之自定義typeHandler

Mybatis學習之自定義typeHandler

自定義typeHandler背景

比如儲存到資料庫時,有以下需求:

1.有個列舉型別的值,想要儲存到資料庫為字串或整數

2.Date型別存入資料庫為毫秒數

3.物件中的集合(List)屬性儲存資料庫為{xxx,xxx,xxx}的格式,讀取出來自動轉車List

傳統的讀取操作也能達到這個功能,但必須自己程式碼實現邏輯,工作量較大,這樣就可以使用Mybatis下的自定義typeHandler的功能。

官方解釋typeHandler: 無論是 MyBatis 在預處理語句(PreparedStatement)中設定一個引數時,還是從結果集中取出一個值時, 都會用型別處理器將獲取的值以合適的方式轉換成 Java 型別。 

 官網說明地址

自定義typeHandler實現

1.實現 org.apache.ibatis.type.TypeHandler 介面, 或繼承一個很便利的類 org.apache.ibatis.type.BaseTypeHandler, 然後可以選擇性地將它對映到一個 JDBC 型別。比如:

// ExampleTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR)
//@MappedTypes({String.class}) //泛型有此註釋功能
public class ExampleTypeHandler extends BaseTypeHandler<String> { //如果不用泛型,可以加@MappedTypes註釋來匹配需要被攔截的Java型別

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

2.註冊handler

<!-- mybatis-config.xml -->
<typeHandlers>
  <typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>
3.配置Mapper.xml

 ①在resultMap標籤中加入typeHandler

<resultMap id="testResultMap" type="com.lhl.mybatis.beans.Test">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="nums" jdbcType="INTEGER" property="nums" />
    <result column="name" jdbcType="VARCHAR" property="name" typeHandler="com.lhl.mybatis.typehandler.CustomTypeHandler"/>
  </resultMap>

②在查詢結果集中指定自定義resultMap

<select id="selectById" parameterType="java.lang.Integer" resultMap="testResultMap">
    SELECT * FROM test WHERE id=#{id}
  </select>

查詢結果時,如果呼叫String型別的資料,就會到自定義typeHandler的get方法中處理自定義邏輯

如果想要在插入時加入自定義typeHandler,如何處理呢?以下兩種方式等效

①在插入時配置jdbcType, 測試時只需要jdbcType配置就可以觸發自定義typeHandler

<insert id="insert" parameterType="com.lhl.mybatis.beans.Test" useGeneratedKeys="true" keyProperty="id">
    insert into test(id, nums, name)
    values (#{id,jdbcType=INTEGER},#{nums,jdbcType=INTEGER},#{name,jdbcType=VARCHAR,javaType=java.lang.String})
  </insert>

②在插入時指定typeHandler,注意不要引號

<insert id="insert" parameterType="com.lhl.mybatis.beans.Test" useGeneratedKeys="true" keyProperty="id">
    insert into test(id, nums, name)
    values (#{id,jdbcType=INTEGER},#{nums,jdbcType=INTEGER},#{name,typeHandler=com.lhl.mybatis.typehandler.CustomTypeHandler})
  </insert>

============================================================================

最後引用官網說明:

使用這個的型別處理器將會覆蓋已經存在的處理 Java 的 String 型別屬性和 VARCHAR 引數及結果的型別處理器。 要注意 MyBatis 不會窺探資料庫元資訊來決定使用哪種型別,所以你必須在引數和結果對映中指明那是 VARCHAR 型別的欄位, 以使其能夠繫結到正確的型別處理器上。 這是因為:MyBatis 直到語句被執行才清楚資料型別。

通過型別處理器的泛型,MyBatis 可以得知該型別處理器處理的 Java 型別,不過這種行為可以通過兩種方法改變:

  • 在型別處理器的配置元素(typeHandler element)上增加一個 javaType 屬性(比如:javaType="String");
  • 在型別處理器的類上(TypeHandler class)增加一個 @MappedTypes 註解來指定與其關聯的 Java 型別列表。 如果在 javaType 屬性中也同時指定,則註解方式將被忽略。

可以通過兩種方式來指定被關聯的 JDBC 型別:

  • 在型別處理器的配置元素上增加一個 jdbcType 屬性(比如:jdbcType="VARCHAR");
  • 在型別處理器的類上(TypeHandler class)增加一個 @MappedJdbcTypes 註解來指定與其關聯的 JDBC 型別列表。 如果在 jdbcType 屬性中也同時指定,則註解方式將被忽略。

當決定在ResultMap中使用某一TypeHandler時,此時java型別是已知的(從結果型別中獲得),但是JDBC型別是未知的。 因此Mybatis使用javaType=[TheJavaType], jdbcType=null的組合來選擇一個TypeHandler。 這意味著使用@MappedJdbcTypes註解可以限制TypeHandler的範圍,同時除非顯式的設定,否則TypeHandler在ResultMap中將是無效的。 如果希望在ResultMap中使用TypeHandler,那麼設定@MappedJdbcTypes註解的includeNullJdbcType=true即可。 然而從Mybatis 3.4.0開始,如果只有一個註冊的TypeHandler來處理Java型別,那麼它將是ResultMap使用Java型別時的預設值(即使沒有includeNullJdbcType=true)。