【MyBatis學習筆記】5:認識使用typeHandlers配置型別處理器
阿新 • • 發佈:2019-01-01
簡述
註冊了的型別處理器會用於處理下面兩種情形:
- 為PreparedStatement設定一個引數,將引數從Java型別轉為JDBC型別。
- 從ResultSet中取出一個值,將結果從JDBC型別轉為Java型別。
型別處理器可分為以下兩類:
- MyBatis系統定義的型別處理器
- 使用者自定義的型別處理器
認識系統定義的型別處理器
在org.apache.ibatis.type.TypeHandlerRegistry
類中的構造器可以看到系統註冊的大量的型別處理器,如其中一段:
register(Reader.class, new ClobReaderTypeHandler());
register (String.class, new StringTypeHandler());
register(String.class, JdbcType.CHAR, new StringTypeHandler());
register(String.class, JdbcType.CLOB, new ClobTypeHandler());
register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
register (String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
register(JdbcType.CHAR, new StringTypeHandler());
register(JdbcType.VARCHAR, new StringTypeHandler());
register (JdbcType.CLOB, new ClobTypeHandler());
register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
register(JdbcType.NVARCHAR, new NStringTypeHandler());
register(JdbcType.NCHAR, new NStringTypeHandler());
register(JdbcType.NCLOB, new NClobTypeHandler());
點開其中的org.apache.ibatis.type.StringTypeHandler
類檢視一下具體實現:
//繼承BaseTypeHandler抽象介面,間接實現了必要的TypeHandler介面
public class StringTypeHandler extends BaseTypeHandler<String> {
//設定非空引數(要設定引數的預處理語句,設定引數的下標,使用的Java型別字串,對應資料庫的JDBC型別)
@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);
}
}
自定義的型別處理器
按照書上的例子,先嚐試自定義覆蓋掉一個系統定義的型別處理器,實現String
到VARCHAR
的轉換。
配置檔案中
<!--註冊自定義的型別處理器-->
<typeHandlers>
<typeHandler jdbcType="VARCHAR" javaType="string" handler="test.MyStringTypeHandler"/>
</typeHandlers>
該型別處理器類
一定要實現TypeHandler
介面,可以像前面系統實現的那樣通過繼承BaseTypeHandler
類來實現,也可以直接實現。
對於處理器類,使用@MappedTypes
註解Java型別;使用@MappedJdbcTypes
註解JDBC型別,即org.apache.ibatis.type.JdbcType
列舉類所列的列舉型別。
package test;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.apache.ibatis.type.TypeHandler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
//Java型別為String
@MappedTypes({String.class})
//JDBC型別為VARCHAR
@MappedJdbcTypes(JdbcType.VARCHAR)
//直接實現TypeHandler介面,泛型<T>影響了後三個方法的返回值<T>
public class MyStringTypeHandler implements TypeHandler<String> {
//為PreparedStatement設定引數
@Override
public void setParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {
System.out.println("呼叫了自定的setParameter");
preparedStatement.setString(i, s);
}
//從結果集中按列名獲取
@Override
public String getResult(ResultSet resultSet, String s) throws SQLException {
System.out.println("呼叫了自定的getResult(1)");
return resultSet.getString(s);
}
//從結果集中按列號獲取
@Override
public String getResult(ResultSet resultSet, int i) throws SQLException {
System.out.println("呼叫了自定的getResult(2)");
return resultSet.getString(i);
}
//從儲存過程中按列號獲取
@Override
public String getResult(CallableStatement callableStatement, int i) throws SQLException {
System.out.println("呼叫了自定的getResult(3)");
return callableStatement.getString(i);
}
}
不改變其行為,只是在呼叫其內方法時多輸出一句話,以驗證是否配置上了這個自定義的型別處理器。
對映檔案中
- 為對映檔案新增
<resultMap>
標籤(構建ResultSet型別處理器) - 為查詢的標籤新增
resultMap
屬性指向這個剛構建的型別處理器(增刪改不會返回ResultSet所以不用) - 為SQL語句中使用到引數的位置新增上
javaType
、jdbcType
、typeHandler
屬性於#{}
內(引數使用型別處理器)。
前兩件事共同配置了ResultSet使用型別處理器,後面一件事是在配置PreparedStatement的引數傳入時使用型別處理器。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.StudentMapper">
<!--設定關於ResultSet使用型別處理器-->
<!--id用來給後面使用,type就使用PO類或其別名表示結果集對應的是這個類的物件-->
<resultMap id="stuMap" type="stu">
<!--id子元素用來標識主鍵,column標識在資料庫中的列或屬性,property標識PO類的屬性,後兩個屬性不用說了-->
<id column="id" property="id" javaType="int" jdbcType="INTEGER"/>
<!--result子元素標識非主屬性,元素內的四個屬性都和前面id的一樣-->
<!--可以直接使用typeHandler屬性,就不必指出javaType和jdbcType了,或只給出javaType和jdbcType也夠用-->
<result column="name" property="stuName" typeHandler="test.MyStringTypeHandler"/>
</resultMap>
<!--查詢語句需要給出resultMap屬性,指向前面配置的resultMap的id,表示對該語句的結果集的處理使用前面resultMap的配置-->
<select id="getStudent" parameterType="int" resultType="stu" resultMap="stuMap">
SELECT
id,
name
FROM student
WHERE id = #{id}
</select>
<insert id="addStudent" parameterType="stu">
INSERT INTO student (name) VALUES (#{stuName})
</insert>
<delete id="deleteStudent" parameterType="int">
DELETE FROM student
WHERE id = #{id}
</delete>
<!--這裡面的name設定了引數使用這個自定的型別處理器-->
<update id="updateStudent" parameterType="stu">
UPDATE student
SET name = #{stuName,javaType=string,jdbcType=VARCHAR,typeHandler=test.MyStringTypeHandler}
WHERE id = #{id}
</update>
</mapper>
執行結果
可以看到輸出了自定義的型別處理器中的這兩句話,說明成功配置了自定義的型別處理器。
未解決的疑問
在前面對映檔案中,可以看到除了新的和型別處理器相關的配置之外,還有一個和之前不一樣的地方:
SELECT
id,
name
FROM student
WHERE id = #{id}
這裡之前是:
SELECT
id,
name as stuName
FROM student
WHERE id = #{id}
如果採用這種帶有AS
的寫法,讀出來的name始終是null,而且不會輸出第一句呼叫(即沒有呼叫自定義的型別處理器中的getResult()
方法)。
我猜測是因為新增ResultMap已經給出了這種對映關係,即從
column
到property
的對映關係。