mybatis typeHandler類型轉換器


 typeHandler類型轉換器  

  在JDBC中,需要在PreparedStatement對象中設置那些已經預編譯過的SQL語句的參數。執行SQL后,會通過ResultSet對象獲取得到數據庫的數據,而這些MyBatis是根據數據的類型通過typeHandler來實現的。在typeHandler中,分為jdbcType和javaType,其中jdbcType用於定義數據庫類型,而javaType用於定義Java類型,那么typeHandler的作用就是承擔jdbcType和javaType之間的相互轉換,如圖4-1所示。在很多情況下我們並不需要去配置typeHandler、jdbcType、javaType,因為MyBatis會探測應該使用什么類型的typeHandler進行處理,但是有些場景無法探測到。對於那些需要使用自定義枚舉的場景,或者數據庫使用特殊數據類型的場景,可以使用自定義的typeHandler去處理類型之間的轉換問題。

  和別名一樣,在MyBatis中存在系統定義typeHandler和自定義typeHandler。MyBatis會根據javaType和數據庫的jdbcType來決定采用哪個typeHandler處理這些轉換規則。系統提供的typeHandler能覆蓋大部分場景的要求,但是有些情況下是不夠的,比如我們有特殊的轉換規則,枚舉類就是這樣。

 

系統定義的typeHandler

  MyBatis內部定義了許多有用的typeHandler,如表所示。

  

  這些就是MyBatis系統已經創建好的typeHandler。在大部分的情況下無須顯式地聲明jdbcType和javaType,或者用typeHandler去指定對應的typeHandler來實現數據類型轉換,因為MyBatis系統會自己探測。有時候需要修改一些轉換規則,比如枚舉類往往需要自己去編寫規則。

 

  在MyBatis中typeHandler都要實現接口org.apache.ibatis.type.TypeHandler,首先讓我們先看看這個接口的定義,如代碼清單4-9所示。
  代碼清單4-9:TypeHandler.java

public interface TypeHandler<T> {
    //T是泛型,專指javaType,比如我們需要String的時候,那么實現類可以寫為implements TypeHandler<String>。
    //setParameter方法,是使用typeHandler通過PreparedStatement對象進行設置SQL參數的時候使用的具體方法,其中i是參數在SQL的下標,parameter是參數,jdbcType是數據庫類型。
    void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

    //其中有3個getResult的方法,它的作用是從JDBC結果集中獲取數據進行轉換,要么使用列名(columnName)要么使用下標(columnIndex)獲取數據庫的數據,其中最后一個getResult方法是存儲過程專用的。
    T getResult(ResultSet rs, String columnName) throws SQLException;

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

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

  在編寫typeHandler前,先來研究一下MyBatis系統的typeHandler是如何實現的,所以有必要先研究一下MyBatis系統的typeHandler。如果讀者打開源碼,就可以發現它們都繼承了org.apache.ibatis.type.BaseTypeHandler,如代碼清單4-10所示。
  代碼清單4-10:BaseTypeHandler.java

/**
 * BaseTypeHandler是個抽象類,需要子類去實現其定義的4個抽象方法,而它本身實現了typeHandler接口的4個方法。
 * @param <T>
 */
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
    protected Configuration configuration;

    public BaseTypeHandler() {
    }

    public void setConfiguration(Configuration c) {
        this.configuration = c;
    }

    //setParameter方法,當參數parameter和jdbcType同時為空時,MyBatis將拋出異常。如果能明確jdbcType,則會進行空設置;如果參數不為空,那么它將采用setNonNullPa-rameter方法設置參數。
    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); } } } //getResult方法,非空結果集是通過getNullableResult方法獲取的。如果判斷為空,則返回null。 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 { Object result; try { result = this.getNullableResult(rs, columnIndex); } catch (Exception var5) { throw new ResultMapException("Error attempting to get column #" + columnIndex + " from result set. Cause: " + var5, var5); } return rs.wasNull() ? null : result; } public T getResult(CallableStatement cs, int columnIndex) throws SQLException { Object result; try { result = this.getNullableResult(cs, columnIndex); } catch (Exception var5) { throw new ResultMapException("Error attempting to get column #" + columnIndex + " from callable statement. Cause: " + var5, var5); } return cs.wasNull() ? null : result; } public abstract void setNonNullParameter(PreparedStatement var1, int var2, T var3, JdbcType var4) throws SQLException; //getNullableResult方法用於存儲過程。 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; }

  

  MyBatis使用最多的typeHanlder之一——StringTypeHandler。它用於字符串轉換,其源碼如代碼清單4-11所示。
  代碼清單4-11:StringTypeHanlder

public class StringTypeHandler extends BaseTypeHandler<String> {
    public StringTypeHandler() {
    }

    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter);
    }

    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getString(columnName);
    }

    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return rs.getString(columnIndex);
    }

    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return cs.getString(columnIndex);
    }
}

  顯然它實現了BaseTypeHandler的4個抽象方法,代碼也非常簡單。
  在這里,MyBatis把javaType和jdbcType相互轉換,那么它們是如何注冊的呢?在MyBatis中采用org.apache.ibatis.type.TypeHandlerRegistry類對象的register方法進行注冊,如代碼清單4-12所示。
  代碼清單4-12:系統注冊typeHanlder

public TypeHandlerRegistry() {
    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    //....
    register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
    register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
    //....
}

這樣就實現了用代碼的形式注冊typeHandler。注意,自定義的typeHandler一般不會使用代碼注冊,而是通過配置或掃描

 

mybatis 自定義typeHandler

mybatis 枚舉typeHandler


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM