Mybatis類型轉換TypeHandler介紹

1.2 建立TypeHandler
我們知道java有java的數據類型,數據庫有數據庫的數據類型,那么我們在往數據庫中插入數據的時候是如何把java類型當做數據庫類型插入數據庫,在從數據庫讀取數據的時候又是如何把數據庫類型當做java類型來處理呢?這中間必然要經過一個類型轉換。在Mybatis中我們可以定義一個叫做TypeHandler類型處理器的東西,通過它可以實現Java類型跟數據庫類型的相互轉換。下面將就如何建立自己的TypeHandler做一個簡要介紹。
1.2.1 TypeHandler接口
在Mybatis中要實現自己的TypeHandler就需要實現Mybatis為我們提供的TypeHandler接口。在TypeHandler中定義了四個方法:

public interface TypeHandler<T> {  
   
    /** 
     * 用于定義在Mybatis設置參數時該如何把Java類型的參數轉換為對應的數據庫類型 
     * @param ps 當前的PreparedStatement對象 
     * @param i 當前參數的位置 
     * @param parameter 當前參數的Java對象 
     * @param jdbcType 當前參數的數據庫類型 
     * @throws SQLException 
     */  
    void setParameter(PreparedStatement ps, int i, T parameter,  
           JdbcType jdbcType) throws SQLException;  
   
    /** 
     * 用于在Mybatis獲取數據結果集時如何把數據庫類型轉換為對應的Java類型 
     * @param rs 當前的結果集 
     * @param columnName 當前的字段名稱 
     * @return 轉換后的Java對象 
     * @throws SQLException 
     */  
    T getResult(ResultSet rs, String columnName) throws SQLException;  
   
    /** 
     * 用于在Mybatis通過字段位置獲取字段數據時把數據庫類型轉換為對應的Java類型 
     * @param rs 當前的結果集 
     * @param columnIndex 當前字段的位置 
     * @return 轉換后的Java對象 
     * @throws SQLException 
     */  
    T getResult(ResultSet rs, int columnIndex) throws SQLException;  
   
    /** 
     * 用于Mybatis在調用存儲過程后把數據庫類型的數據轉換為對應的Java類型 
     * @param cs 當前的CallableStatement執行后的CallableStatement 
     * @param columnIndex 當前輸出參數的位置 
     * @return 
     * @throws SQLException 
     */  
    T getResult(CallableStatement cs, int columnIndex) throws SQLException;  
   
}  

現在假設我們有一個實體對象User,其中有一個屬性interests是String數組類型,如下所示:

public class User {  
   
    private int id;  
    private String name;  
    private int age;  
    private String[] interests;  
   
    public int getId() {  
       return id;  
    }  
   
    public void setId(int id) {  
       this.id = id;  
    }  
   
    public String getName() {  
       return name;  
    }  
   
    public void setName(String name) {  
       this.name = name;  
    }  
   
    public int getAge() {  
       return age;  
    }  
   
    public void setAge(int age) {  
       this.age = age;  
    }  
   
    public String[] getInterests() {  
       return interests;  
    }  
   
    public void setInterests(String[] interests) {  
       this.interests = interests;  
    }  
   
    @Override  
    public String toString() {  
       return "User [age=" + age + ", id=" + id + ", interests="  
              + Arrays.toString(interests) + ", name=" + name + "]";  
    }  
     
}  

我們需要把它以拼接字符串的形式存到數據庫中,然后在取出來的時候又把它還原為一個String數組。這個時候我們就可以給它定義一個TypeHandler專門來處理String數組類型和數據庫VARCHAR類型的相互轉換。在這里我們建立一個名叫StringArrayTypeHandler的TypeHandler,代碼如下所示:

package com.tiantian.mybatis.handler;  
   
import java.sql.CallableStatement;  
import java.sql.PreparedStatement;  
import java.sql.ResultSet;  
import java.sql.SQLException;  
import java.sql.Types;  
   
import org.apache.ibatis.type.JdbcType;  
import org.apache.ibatis.type.TypeHandler;  
   
public class StringArrayTypeHandler implements TypeHandler<String[]> {  
   
       public String[] getResult(ResultSet rs, String columnName)  
                     throws SQLException {  
              String columnValue = rs.getString(columnName);  
              return this.getStringArray(columnValue);  
       }  
   
       public String[] getResult(ResultSet rs, int columnIndex)  
                     throws SQLException {  
              String columnValue = rs.getString(columnIndex);  
              return this.getStringArray(columnValue);  
       }  
   
       public String[] getResult(CallableStatement cs, int columnIndex)  
                     throws SQLException {  
              // TODO Auto-generated method stub  
              String columnValue = cs.getString(columnIndex);  
              return this.getStringArray(columnValue);  
       }  
   
       public void setParameter(PreparedStatement ps, int i, String[] parameter,  
                     JdbcType jdbcType) throws SQLException {  
              if (parameter == null)  
                     ps.setNull(i, Types.VARCHAR);  
              else {  
                     StringBuffer result = new StringBuffer();  
                     for (String value : parameter)  
                            result.append(value).append(",");  
                     result.deleteCharAt(result.length()-1);  
                     ps.setString(i, result.toString());  
              }  
       }  
   
       private String[] getStringArray(String columnValue) {  
              if (columnValue == null)  
                     return null;  
              return columnValue.split(",");  
       }  
   
}  

1.2.2 BaseTypeHandler抽象類
在實現自己的TypeHandler時,除了上面提到的實現最原始的接口之外,Mybatis還為我們提供了一個實現了TypeHandler接口的抽象類BaseTypeHandler。所以我們也可以通過繼承BaseTypeHandler來實現自己的TypeHandler。
我們先來看一下BaseTypeHandler類的定義:

public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {  
   
  protected Configuration configuration;  
   
  public void setConfiguration(Configuration c) {  
    this.configuration = c;  
  }  
   
  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 e) {  
        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: " + e, e);  
      }  
    } else {  
      setNonNullParameter(ps, i, parameter, jdbcType);  
    }  
  }  
   
  public T getResult(ResultSet rs, String columnName) throws SQLException {  
    T result = getNullableResult(rs, columnName);  
    if (rs.wasNull()) {  
      return null;  
    } else {  
      return result;  
    }  
  }  
   
  public T getResult(ResultSet rs, int columnIndex) throws SQLException {  
    T result = getNullableResult(rs, columnIndex);  
    if (rs.wasNull()) {  
      return null;  
    } else {  
      return result;  
    }  
  }  
   
  public T getResult(CallableStatement cs, int columnIndex) throws SQLException {  
    T result = getNullableResult(cs, columnIndex);  
    if (cs.wasNull()) {  
      return null;  
    } else {  
      return result;  
    }  
  }  
   
  public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;  
   
  public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;  
   
  public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;  
   
  public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;  
   
}  

我們可以看到BaseTypeHandler對TypeHandler接口的四個方法做了一個簡單的選擇,把null值的情況都做了一個過濾,核心的取值和設值的方法還是抽象出來了供子類來實現。使用BaseTypeHandler還有一個好處是它繼承了另外一個叫做TypeReference的抽象類,通過TypeReference的getRawType()方法可以獲取到當前TypeHandler所使用泛型的原始類型。這對Mybatis在注冊TypeHandler的時候是非常有好處的。在沒有指定javaType的情況下,Mybatis在注冊TypeHandler時可以通過它來獲取當前TypeHandler所使用泛型的原始類型作為要注冊的TypeHandler的javaType類型,這個在講到Mybatis注冊TypeHandler的方式時將講到。
當通過繼承BaseTypeHandler來實現自己的TypeHandler時,我們的StringArrayTypeHandler應該這樣寫:

public class StringArrayTypeHandler extends BaseTypeHandler<String[]> {  
   
    @Override  
    public String[] getNullableResult(ResultSet rs, String columnName)  
           throws SQLException {  
       return getStringArray(rs.getString(columnName));  
    }  
   
    @Override  
    public String[] getNullableResult(ResultSet rs, int columnIndex)  
           throws SQLException {  
       return this.getStringArray(rs.getString(columnIndex));  
    }  
   
    @Override  
    public String[] getNullableResult(CallableStatement cs, int columnIndex)  
           throws SQLException {  
       return this.getStringArray(cs.getString(columnIndex));  
    }  
   
    @Override  
    public void setNonNullParameter(PreparedStatement ps, int i,  
           String[] parameter, JdbcType jdbcType) throws SQLException {  
       //由于BaseTypeHandler中已經把parameter為null的情況做了處理,所以這里我們就不用再判斷parameter是否為空了,直接用就可以了  
       StringBuffer result = new StringBuffer();  
       for (String value : parameter)  
           result.append(value).append(",");  
       result.deleteCharAt(result.length()-1);  
       ps.setString(i, result.toString());  
    }  
     
    private String[] getStringArray(String columnValue) {  
       if (columnValue == null)  
           return null;  
       return columnValue.split(",");  
    }  
}  

1.3 注冊TypeHandler

建立了自己的TypeHandler之后就需要把它注冊到Mybatis的配置文件中,讓Mybatis能夠識別并使用它。注冊TypeHandler主要有兩種方式,一種是通過在Mybatis配置文件中定義typeHandlers元素的子元素typeHandler來注冊;另一種是通過在Mybatis配置文件中定義typeHandlers元素的子元素package來注冊。使用typeHandler子元素注冊時一次只能注冊一個TypeHandler,而使用package子元素注冊時,Mybatis會把指定包里面的所有TypeHandler都注冊為TypeHandler。使用typeHandler子元素注冊時我們需要通過它的handler屬性來指明當前要注冊的TypeHandler的全名稱,這個屬性是必須要的。另外還有兩個附加屬性可以指定,一個是javaType,用以指定對應的java類型;另一個是jdbcType,用以指定對應的jdbc類型。使用package子元素注冊時需要我們通過它的name屬性來指定要掃描的包,如果這個時候我們也需要指定對應TypeHandler的javaType和jdbcType的話就需要我們在TypeHandler類上使用注解來定義了。Mybatis注冊TypeHandler最基本的方式就是建立一個javaType、jdbcType和TypeHandler的對應關系。在使用typeHandler子元素進行注冊的時候,有三種類型的注冊方式:
1.如果我們指定了javaType和jdbcType,那么Mybatis會注冊一個對應javaType和jdbcType的TypeHandler。
2.如果我們只指定了javaType屬性,那么這個時候又分兩種情況:
(1)如果我們通過注解的形式在TypeHandler類上用@MappedJdbcTypes指定了對應的jdbcType,那么Mybatis會一一注冊指定的javaType、jdbcType和TypeHandler的組合,也包括使用這種形式指定了jdbcType為null的情況?,F假設我們有如下這樣一個StringArrayTypeHandler:

@MappedJdbcTypes({JdbcType.VARCHAR})  
public class StringArrayTypeHandler implements TypeHandler<String[]> {  
    //..中間的實現代碼省略了  
    //..  
} 

然后我們在Mybatis的配置文件中這樣注冊它:

<typeHandlers>  
   <typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler" javaType="[Ljava.lang.String;"/>  
</typeHandlers>  

則Mybatis在實際注冊的時候是以javaType為String數組,jdbcType為VARCHAR來注冊StringArrayTypeHandler的。
(2)如果沒有使用@MappedJdbcTypes注解指定對應的jdbcType,那么這個時候Mybatis會把jdbcType置為null,然后注冊一個javaType、null和TypeHandler的組合。
3.既沒有指定javaType屬性,又沒有指定jdbcType屬性,或者只指定了jdbcType屬性。這種情況又分三種類型:
(1)如果TypeHandler類上使用了注解@MappedTypes指定了對應的javaType,那么Mybatis將一一利用對應的javaType和TypeHandler去以2的方式進行注冊?,F假設我們定義了這樣一個StringArrayTypeHandler:

@MappedTypes({String[].class})  
@MappedJdbcTypes({JdbcType.VARCHAR})  
public class StringArrayTypeHandler implements TypeHandler<String[]> {  
   
     
   
}  

然后,在Mybatis的配置文件中注冊它時既不指定它的javaType屬性也不指定它的jdbcType屬性,代碼如下:

<typeHandlers>  
   <typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler"/>  
</typeHandlers>  

則這個時候Mybatis在注冊該StringArrayTypeHandler的時候首先會判斷它上面有沒有標注@MappedTypes,如果有則把它的MappedTypes一一拿出來作為javaType,然后以方式2進行注冊。所以這里實際上Mybatis注冊的還是javaType為String數組,jdbcType為VARCHAR這樣一個組合的TypeHandler。
(2)TypeHandler類上沒有使用@MappedTypes指定對應的javaType時,如果當前的TypeHandler繼承了TypeReference抽象類,Mybatis會利用TypeReference的getRawType()方法取到當前TypeHandler泛型對應的javaType類型,然后利用取得的javaType和TypeHandler以2的方式進行注冊,同時還包括一個javaType為null以方式2進行的注冊。TypeReference是Mybatis中定義的一個抽象類,主要是用來獲取對應的泛型類型。
(3)TypeHandler類上既沒有標注@MappedTypes,又沒有繼承TypeReference抽象類。這種情況Mybatis會以null和null的組合注冊該TypeHandler。
使用package子元素注冊的TypeHandler會以上面的方式3進行注冊。
這里我們如下注冊我們的TypeHandler:

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE configuration  
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
  "http://mybatis.org/dtd/mybatis-3-config.dtd">  
<configuration>  
   
    <properties resource="config/jdbc.properties"></properties>  
    <typeAliases>  
       <package name="com.tiantian.mybatis.model"/>  
    </typeAliases>  
    <typeHandlers>  
       <typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler" javaType="[Ljava.lang.String;" jdbcType="VARCHAR"/>  
    </typeHandlers>  
    <environments default="development">  
       <environment id="development">  
           <transactionManager type="JDBC" />  
           <dataSource type="POOLED">  
              <property name="driver" value="${jdbc.driver}" />  
              <property name="url" value="${jdbc.url}" />  
              <property name="username" value="${jdbc.username}" />  
              <property name="password" value="${jdbc.password}" />  
           </dataSource>  
       </environment>  
    </environments>  
    <mappers>  
       <mapper resource="com/tiantian/mybatis/mapper/UserMapper.xml"/>  
       <package name="com.tiantian.mybatis.mapperinterface"/>  
    </mappers>  
</configuration>  

注意String數組的全類名稱是“[Ljava.lang.String;”,所以上面在注冊StringArrayTypeHandler時定義的javaType屬性為“[Ljava.lang.String;”。

1.4 Mybatis自動獲取TypeHandler

在介紹了Mybatis是如何注冊TypeHandler之后就介紹一下Mybatis是如何獲取對應的TypeHandler進行類型轉換的。
如果我們在Mapper.xml文件中配置某一個屬性或變量的映射關系時指定了該屬性對應的javaType和jdbcType,則Mybatis會從注冊好的TypeHandler中尋找對應的javaType和jdbcType組合的TypeHandler進行處理,這也是Mybatis最基本的獲取TypeHandler進行類型轉換的方式。假設Mybatis配置文件中有這么一段TypeHandler的注冊信息:

<typeHandlers>  
   <typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler" javaType="[Ljava.lang.String;" jdbcType="VARCHAR"/>  
</typeHandlers>  

看這樣一個UserMapper.xml定義:

<?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="com.tiantian.mybatis.mapper.UserMapper">  
   
    <resultMap id="UserResult" type="User">  
       <id column="id" property="id"/>  
       <result column="interests" property="interests" javaType="[Ljava.lang.String;" jdbcType="VARCHAR"/>  
    </resultMap>  
   
    <insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyColumn="id">  
       insert into t_user(name, age, interests) values(#{name}, #{age}, #{interests, javaType=[Ljava.lang.String;, jdbcType=VARCHAR})  
    </insert>  
     
    <update id="updateUser" parameterType="User">  
       update t_user set name=#{name}, age=#{age}, interests=#{interests} where id=#{id}  
    </update>  
     
    <select id="findById" parameterType="int" resultMap="UserResult">  
       select * from t_user where id=#{id}  
    </select>  
     
    <delete id="deleteUser" parameterType="int">  
       delete from t_user where id=#{id}  
    </delete>  
</mapper>  

我們可以看到在id為UserResult的resultMap中,我們定義了一個對應字段interests的映射關系,并且定義了其javaType為“[Ljava.lang.String;”,jdbcType為VARCHAR,這個時候Mybatis就會到已經注冊了的TypeHandler中尋找到能處理javaType和jdbcType對應的類型轉換的TypeHandler來進行處理。在這里就會找到我們注冊的StringArrayTypeHandler。在上面id為insertUser的insert語句中,我們也為變量interests指定了它的javaType和jdbcType屬性,這時候Mybatis也會尋找javaType和jdbcType對應的TypeHandler。上面這樣定義是Mybatis最基本也是最完整地獲取到對應的TypeHandler的方法。這里我們來對UserMapper(它的代碼我就不貼出來了,有Mybatis基礎的都應該知道它的代碼)的findById來做一個測試:

@Test  
public void testFind() {  
   SqlSession sqlSession = sqlSessionFactory.openSession();  
   try {  
       UserMapper userMapper = sqlSession.getMapper(UserMapper.class);  
       User user = userMapper.findById(20);  
       System.out.println(user);  
   } finally {  
       sqlSession.close();  
   }  
}  

其輸出結果如下:

User [age=30, id=20, interests=[Music, Movie, NBA], name=張三]  

我們可以看到Mybatis已經把我們存放在數據庫中VARCHAR類型的字段interests轉換為User類字符串數組類型的interests屬性,這說明我們定義的StringArrayTypeHandler發生作用了。
除了上面的完整指定一個變量對應的javaType和jdbcType,讓Mybatis能夠完美的找到對應的TypeHandler之外。我們平常在使用的時候可能還有以下方式:
1.只指定變量對應的javaType類型。這個時候Mybatis會拿著這個javaType和jdbcType為null的組合到注冊的TypeHandler中尋找對應的TypeHandler。這里我們同樣來做一個測試:
(1)不動StringArrayTypeHandler的注冊信息,把我們的UserMapper.xml改為如下形式:

<?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="com.tiantian.mybatis.mapper.UserMapper">  
    <resultMap id="UserResult" type="User">  
       <id column="id" property="id"/>  
       <result column="interests" property="interests" javaType="[Ljava.lang.String;"/>  
    </resultMap>  
   
    <select id="findById" parameterType="int" resultMap="UserResult">  
       select * from t_user where id=#{id}  
    </select>  
</mapper>  

這時候再運行上面的測試程序,輸出結果如下:

User [age=30, id=20, interests=null, name=張三]  

我們可以看到輸出的interests為null,這說明Mybatis沒有使用我們定義的StringArrayTypeHandler來轉換interests。
(2)UserMapper.xml還像上面那樣定義,但是也只指定javaType屬性來注冊我們的StringArrayTypeHandler,代碼如下:

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE configuration  
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
  "http://mybatis.org/dtd/mybatis-3-config.dtd">  
<configuration>  
    <properties resource="config/jdbc.properties"></properties>  
    <typeAliases>  
       <package name="com.tiantian.mybatis.model"/>  
    </typeAliases>  
    <typeHandlers>  
       <typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler" javaType="[Ljava.lang.String;"/>  
    </typeHandlers>  
    <environments default="development">  
       <environment id="development">  
           <transactionManager type="JDBC" />  
           <dataSource type="POOLED">  
              <property name="driver" value="${jdbc.driver}" />  
              <property name="url" value="${jdbc.url}" />  
              <property name="username" value="${jdbc.username}" />  
              <property name="password" value="${jdbc.password}" />  
           </dataSource>  
       </environment>  
    </environments>  
    <mappers>  
       <mapper resource="com/tiantian/mybatis/mapper/UserMapper.xml"/>  
    </mappers>  
</configuration>  

這個時候再運行上面的測試代碼,輸出結果如下:

User [age=30, id=20, interests=[Music, Movie, NBA], name=張三]  

這是因為我們是以javaType和null注冊的StringArrayTypeHandler,然后在需要轉換interests時又是以相同的javaType和null來尋找的,所以就會找到我們注冊的StringArrayTypeHandler來進行類型轉換。
2.只指定變量對應的jdbcType類型。這個時候Mybatis會利用我們指定的返回類型和對應的屬性取該屬性在返回類型中對應的javaType,之后再拿著該javaType和我們指定的jdbcType到注冊的TypeHandler中獲取對應的TypeHandler。這里我們來看這樣一個測試:
保持之前指定javaType和jdbcType的方式注冊StringArrayTypeHandler,然后在定義interests變量的時候不指定javaType,只指定jdbcType,這個時候UserMapper.xml如下所示:

<?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="com.tiantian.mybatis.mapper.UserMapper">  
    <resultMap id="UserResult" type="User">  
       <id column="id" property="id"/>  
       <result column="interests" property="interests" jdbcType="VARCHAR"/>  
    </resultMap>  
    <select id="findById" parameterType="int" resultMap="UserResult">  
       select * from t_user where id=#{id}  
    </select>  
</mapper>  

這個時候繼續運行上面的測試代碼,輸出結果如下:

User [age=30, id=20, interests=[Music, Movie, NBA], name=張三]  

這個時候Mybatis是這樣獲取TypeHandler的:首先它發現我們的interests沒有指定javaType,這個時候它就會通過我們指定的類型User和屬性interests獲取User類的interests屬性對應的java類型,即String數組,再拿著獲取到的javaType和我們指定的jdbcType即VARCHAR去尋找對應的TypeHandler,這個時候就找到了我們之前以String數組和VARCHAR注冊好的StringArrayTypeHandler來處理interests的類型轉換。
3.javaType類型和jdbcType類型都不指定。這個時候Mybatis會以方式2中的方式獲取到對應的javaType類型,然后再以方式1獲取到對應的TypeHandler。這里我們也來做一個測試:
(1)首先,注冊一個javaType為String數組,jdbcType不指定即為null的TypeHandler—StringArrayTypeHandler,代碼如下:

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE configuration  
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
  "http://mybatis.org/dtd/mybatis-3-config.dtd">  
<configuration>  
    <properties resource="config/jdbc.properties"></properties>  
    <typeAliases>  
       <package name="com.tiantian.mybatis.model"/>  
    </typeAliases>  
    <typeHandlers>  
       <typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler" javaType="[Ljava.lang.String;"/>  
    </typeHandlers>  
    <environments default="development">  
       <environment id="development">  
           <transactionManager type="JDBC" />  
           <dataSource type="POOLED">  
              <property name="driver" value="${jdbc.driver}" />  
              <property name="url" value="${jdbc.url}" />  
              <property name="username" value="${jdbc.username}" />  
              <property name="password" value="${jdbc.password}" />  
           </dataSource>  
       </environment>  
    </environments>  
    <mappers>  
       <mapper resource="com/tiantian/mybatis/mapper/UserMapper.xml"/>  
    </mappers>  
</configuration>  

(2)然后,定義我們的interests字段的映射關系時既不指定javaType,又不指定jdbcType,代碼如下:

<?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="com.tiantian.mybatis.mapper.UserMapper">  
    <resultMap id="UserResult" type="User">  
       <id column="id" property="id"/>  
       <result column="interests" property="interests"/>  
    </resultMap>  
    <select id="findById" parameterType="int" resultMap="UserResult">  
       select * from t_user where id=#{id}  
    </select>  
</mapper> 

這個時候再運行上面的測試代碼,輸出如下:

User [age=30, id=20, interests=[Music, Movie, NBA], name=張三]  

這種情況是這樣的:我們以javaType為String數組和jdbcType為null注冊了一個StringArrayTypeHandler,然后在定義interests字段的映射關系時我們沒有指明其對應的javaType和jdbcType,這個時候Mybatis會利用我們指定的User類型和interests屬性獲取到User類的interests屬性對應的java類型,即String數組,然后結合jdbcType為null去尋找注冊的TypeHandler,這樣就找到了StringArrayTypeHandler。經StringArrayTypeHandler的處理就把jdbcType為VARCHAR的數據轉換為javaType為String數組的數據,所以輸出結果如上所示。
4.還有一種形式是我們直接通過變量的typeHandler屬性指定其對應的TypeHandler,這個時候Mybatis就會使用該用戶自己指定的TypeHandler來進行類型轉換,而不再以javaType和jdbcType組合的方式獲取對應的TypeHandler。這里我們也來做一個測試:
(1)首先在Mybatis的配置文件中以javaType和jdbcType配套的方式注冊一個StringArrayTypeHandler,代碼如下:

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE configuration  
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
  "http://mybatis.org/dtd/mybatis-3-config.dtd">  
<configuration>  
    <properties resource="config/jdbc.properties"></properties>  
    <typeAliases>  
       <package name="com.tiantian.mybatis.model"/>  
    </typeAliases>  
    <typeHandlers>  
       <typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler" javaType="[Ljava.lang.String;" jdbcType="VARCHAR"/>  
    </typeHandlers>  
    <environments default="development">  
       <environment id="development">  
           <transactionManager type="JDBC" />  
           <dataSource type="POOLED">  
              <property name="driver" value="${jdbc.driver}" />  
              <property name="url" value="${jdbc.url}" />  
              <property name="username" value="${jdbc.username}" />  
              <property name="password" value="${jdbc.password}" />  
           </dataSource>  
       </environment>  
    </environments>  
    <mappers>  
       <mapper resource="com/tiantian/mybatis/mapper/UserMapper.xml"/>  
    </mappers>  
</configuration>  

按照前面說的Mybatis按照變量的javaType和jdbcType來取對應的TypeHandler的話,這里注冊的StringArrayTypeHandler只有在指定變量的javaType為字符串數組而jdbcType為VARCHAR的情況下才能被獲取到。
(2)然后我們在UserMapper.xml文件中不指定interests字段對應的javaType和jdbcType,但是通過typeHandler屬性指定將以StringArrayTypeHandler來進行類型轉換,代碼如下:

<?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="com.tiantian.mybatis.mapper.UserMapper">  
    <resultMap id="UserResult" type="User">  
       <id column="id" property="id"/>  
       <result column="interests" property="interests" typeHandler="com.tiantian.mybatis.handler.StringArrayTypeHandler"/>  
    </resultMap>  
    <select id="findById" parameterType="int" resultMap="UserResult">  
       select * from t_user where id=#{id}  
    </select>  
</mapper>  

運行上面的測試代碼,輸出結果:

User [age=30, id=20, interests=[Music, Movie, NBA], name=張三]  

這是因為我們指定了進行interests字段的映射關系時使用StringArrayTypeHandler來進行類型轉換。當指定了某一個字段或變量進行映射關系時所使用的TypeHandler時,Mybatis在需要進行類型轉換時就使用給定的TypeHandler進行類型轉換,而不會再通過javaType和jdbcType的組合去注冊好的TypeHandler中尋找對應的TypeHandler。

1.5 Mybatis中自動注冊的TypeHandler
對于一些常用類型的自動轉換Mybatis已經為我們建立了相關的TypeHandler,并且會自動注冊它們,這主要包括:

register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
register(Byte.class, new ByteTypeHandler());
register(byte.class, new ByteTypeHandler());
register(JdbcType.TINYINT, new ByteTypeHandler());
register(Short.class, new ShortTypeHandler());
register(short.class, new ShortTypeHandler());
register(JdbcType.SMALLINT, new ShortTypeHandler());
register(Integer.class, new IntegerTypeHandler());
register(int.class, new IntegerTypeHandler());
register(JdbcType.INTEGER, new IntegerTypeHandler());
register(Long.class, new LongTypeHandler());
register(long.class, new LongTypeHandler());
register(Float.class, new FloatTypeHandler());
register(float.class, new FloatTypeHandler());
register(JdbcType.FLOAT, new FloatTypeHandler());
register(Double.class, new DoubleTypeHandler());
register(double.class, new DoubleTypeHandler());
register(JdbcType.DOUBLE, new DoubleTypeHandler());
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());
register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
register(JdbcType.ARRAY, new ArrayTypeHandler());
register(BigInteger.class, new BigIntegerTypeHandler());
register(JdbcType.BIGINT, new LongTypeHandler());
register(BigDecimal.class, new BigDecimalTypeHandler());
register(JdbcType.REAL, new BigDecimalTypeHandler());
register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
register(JdbcType.NUMERIC, new BigDecimalTypeHandler());
register(InputStream.class, new BlobInputStreamTypeHandler());
register(Byte[].class, new ByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
register(byte[].class, new ByteArrayTypeHandler());
register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.BLOB, new BlobTypeHandler());
register(Object.class, UNKNOWN_TYPE_HANDLER);
register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
register(Date.class, new DateTypeHandler());
register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
register(JdbcType.TIMESTAMP, new DateTypeHandler());
register(JdbcType.DATE, new DateOnlyTypeHandler());
register(JdbcType.TIME, new TimeOnlyTypeHandler());
register(java.sql.Date.class, new SqlDateTypeHandler());
register(java.sql.Time.class, new SqlTimeTypeHandler());
register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容

  • 1. 簡介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存儲過程以及高級映射的優秀的...
    笨鳥慢飛閱讀 5,571評論 0 4
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,829評論 18 139
  • MyBatis配置xml層次結構,而且必須注意其順序。 MyBatis官網中文XML映射配置文件 1.proper...
    落葉飛逝的戀閱讀 2,352評論 0 4
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,737評論 18 399
  • 女性自述: 關鍵詞:親密關系 冷大你好,我是你的資深小迷妹一只,期期微問答必看,電視節目也必看,覺得冷大分析問題時...
    冷愛閱讀 906評論 0 0