Mybatis類型轉(zhuǎn)換的TypeHandler介紹

1.1 目錄

1.2 建立TypeHandler

1.2.1 TypeHandler接口
1.2.2 BaseTypeHandler抽象類

1.3 注冊TypeHandler

1.4 Mybatis自動獲取TypeHandler

1.5 Mybatis中自動注冊的TypeHandler

1.2建立TypeHandler

我們知道java有java的數(shù)據(jù)類型矢劲,數(shù)據(jù)庫有數(shù)據(jù)庫的數(shù)據(jù)類型菩混,那么我們在往數(shù)據(jù)庫中插入數(shù)據(jù)的時候是如何把java類型當(dāng)做數(shù)據(jù)庫類型插入數(shù)據(jù)庫银觅,在從數(shù)據(jù)庫讀取數(shù)據(jù)的時候又是如何把數(shù)據(jù)庫類型當(dāng)做java類型來處理呢宇植?這中間必然要經(jīng)過一個類型轉(zhuǎn)換障斋。在Mybatis中我們可以定義一個叫做TypeHandler類型處理器的東西酌住,通過它可以實現(xiàn)Java類型跟數(shù)據(jù)庫類型的相互轉(zhuǎn)換叼旋。下面將就如何建立自己的TypeHandler做一個簡要介紹伴挚。

1.2.1TypeHandler接口

在Mybatis中要實現(xiàn)自己的TypeHandler就需要實現(xiàn)Mybatis為我們提供的TypeHandler接口靶衍。在TypeHandler中定義了四個方法:

public interface TypeHandler<T> {



     /**

     * 用于定義在Mybatis設(shè)置參數(shù)時該如何把Java類型的參數(shù)轉(zhuǎn)換為對應(yīng)的數(shù)據(jù)庫類型

     * @param ps 當(dāng)前的PreparedStatement對象

     * @param i 當(dāng)前參數(shù)的位置

     * @param parameter 當(dāng)前參數(shù)的Java對象

     * @param jdbcType 當(dāng)前參數(shù)的數(shù)據(jù)庫類型

     * @throws SQLException

     */ 

      void setParameter(PreparedStatement ps, int i, T parameter,

              JdbcType jdbcType) throws SQLException;



    /**

     * 用于在Mybatis獲取數(shù)據(jù)結(jié)果集時如何把數(shù)據(jù)庫類型轉(zhuǎn)換為對應(yīng)的Java類型

     * @param rs 當(dāng)前的結(jié)果集

     * @param columnName 當(dāng)前的字段名稱

     * @return 轉(zhuǎn)換后的Java對象

     * @throws SQLException

     */ 

     T getResult(ResultSet rs, String columnName) throws SQLException;

   

   /**

    * 用于在Mybatis通過字段位置獲取字段數(shù)據(jù)時把數(shù)據(jù)庫類型轉(zhuǎn)換為對應(yīng)的Java類型

    * @param rs 當(dāng)前的結(jié)果集

    * @param columnIndex 當(dāng)前字段的位置

    * @return 轉(zhuǎn)換后的Java對象

    * @throws SQLException

    */ 

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

  

    /**

     * 用于Mybatis在調(diào)用存儲過程后把數(shù)據(jù)庫類型的數(shù)據(jù)轉(zhuǎn)換為對應(yīng)的Java類型

     * @param cs 當(dāng)前的CallableStatement執(zhí)行后的CallableStatement

     * @param columnIndex 當(dāng)前輸出參數(shù)的位置

     * @return

     * @throws SQLException

     */ 

     T getResult(CallableStatement cs, int columnIndex) throws SQLException;
   

} 

現(xiàn)在假設(shè)我們有一個實體對象User,其中有一個屬性interests是String數(shù)組類型茎芋,如下所示:

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 + "]";
    }
   
}

我們需要把它以拼接字符串的形式存到數(shù)據(jù)庫中颅眶,然后在取出來的時候又把它還原為一個String數(shù)組。這個時候我們就可以給它定義一個TypeHandler專門來處理String數(shù)組類型和數(shù)據(jù)庫VARCHAR類型的相互轉(zhuǎn)換田弥。在這里我們建立一個名叫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 {
              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抽象類

在實現(xiàn)自己的TypeHandler時,除了上面提到的實現(xiàn)最原始的接口之外偷厦,Mybatis還為我們提供了一個實現(xiàn)了TypeHandler接口的抽象類BaseTypeHandler商叹。所以我們也可以通過繼承BaseTypeHandler來實現(xiàn)自己的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值的情況都做了一個過濾剖笙,核心的取值和設(shè)值的方法還是抽象出來了供子類來實現(xiàn)。使用BaseTypeHandler還有一個好處是它繼承了另外一個叫做TypeReference的抽象類辜妓,通過TypeReference的getRawType()方法可以獲取到當(dāng)前TypeHandler所使用泛型的原始類型枯途。這對Mybatis在注冊TypeHandler的時候是非常有好處的忌怎。在沒有指定javaType的情況下,Mybatis在注冊TypeHandler時可以通過它來獲取當(dāng)前TypeHandler所使用泛型的原始類型作為要注冊的TypeHandler的javaType類型酪夷,這個在講到Mybatis注冊TypeHandler的方式時將講到榴啸。

當(dāng)通過繼承BaseTypeHandler來實現(xiàn)自己的TypeHandler時,我們的StringArrayTypeHandler應(yīng)該這樣寫:

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中已經(jīng)把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屬性來指明當(dāng)前要注冊的TypeHandler的全名稱啰挪,這個屬性是必須要的。另外還有兩個附加屬性可以指定嘲叔,一個是javaType亡呵,用以指定對應(yīng)的java類型;另一個是jdbcType硫戈,用以指定對應(yīng)的jdbc類型锰什。使用package子元素注冊時需要我們通過它的name屬性來指定要掃描的包,如果這個時候我們也需要指定對應(yīng)TypeHandler的javaType和jdbcType的話就需要我們在TypeHandler類上使用注解來定義了丁逝。Mybatis注冊TypeHandler最基本的方式就是建立一個javaType汁胆、jdbcType和TypeHandler的對應(yīng)關(guān)系。在使用typeHandler子元素進(jìn)行注冊的時候果港,有三種類型的注冊方式:

1.如果我們指定了javaType和jdbcType沦泌,那么Mybatis會注冊一個對應(yīng)javaType和jdbcType的TypeHandler糊昙。

2.如果我們只指定了javaType屬性辛掠,那么這個時候又分兩種情況:

(1)如果我們通過注解的形式在TypeHandler類上用@MappedJdbcTypes指定了對應(yīng)的jdbcType,那么Mybatis會一一注冊指定的javaType释牺、jdbcType和TypeHandler的組合萝衩,也包括使用這種形式指定了jdbcType為null的情況。現(xiàn)假設(shè)我們有如下這樣一個StringArrayTypeHandler:

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

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

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

則Mybatis在實際注冊的時候是以javaType為String數(shù)組没咙,jdbcType為VARCHAR來注冊StringArrayTypeHandler的猩谊。

(2)如果沒有使用@MappedJdbcTypes注解指定對應(yīng)的jdbcType,那么這個時候Mybatis會把jdbcType置為null祭刚,然后注冊一個javaType牌捷、null和TypeHandler的組合墙牌。

3.既沒有指定javaType屬性,又沒有指定jdbcType屬性暗甥,或者只指定了jdbcType屬性喜滨。這種情況又分三種類型:

(1)如果TypeHandler類上使用了注解@MappedTypes指定了對應(yīng)的javaType,那么Mybatis將一一利用對應(yīng)的javaType和TypeHandler去以2的方式進(jìn)行注冊〕贩溃現(xiàn)假設(shè)我們定義了這樣一個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的時候首先會判斷它上面有沒有標(biāo)注@MappedTypes寄月,如果有則把它的MappedTypes一一拿出來作為javaType辜膝,然后以方式2進(jìn)行注冊。所以這里實際上Mybatis注冊的還是javaType為String數(shù)組漾肮,jdbcType為VARCHAR這樣一個組合的TypeHandler厂抖。

(2)TypeHandler類上沒有使用@MappedTypes指定對應(yīng)的javaType時,如果當(dāng)前的TypeHandler繼承了TypeReference抽象類克懊,Mybatis會利用TypeReference的getRawType()方法取到當(dāng)前TypeHandler泛型對應(yīng)的javaType類型验游,然后利用取得的javaType和TypeHandler以2的方式進(jìn)行注冊,同時還包括一個javaType為null以方式2進(jìn)行的注冊保檐。TypeReference是Mybatis中定義的一個抽象類耕蝉,主要是用來獲取對應(yīng)的泛型類型。

(3)TypeHandler類上既沒有標(biāo)注@MappedTypes夜只,又沒有繼承TypeReference抽象類垒在。這種情況Mybatis會以null和null的組合注冊該TypeHandler。

使用package子元素注冊的TypeHandler會以上面的方式3進(jìn)行注冊扔亥。

這里我們?nèi)缦伦晕覀兊腡ypeHandler:

<?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數(shù)組的全類名稱是“[Ljava.lang.String;”场躯,所以上面在注冊StringArrayTypeHandler時定義的javaType屬性為“[Ljava.lang.String;”。

1.4Mybatis自動獲取TypeHandler

在介紹了Mybatis是如何注冊TypeHandler之后就介紹一下Mybatis是如何獲取對應(yīng)的TypeHandler進(jìn)行類型轉(zhuǎn)換的旅挤。

如果我們在Mapper.xml文件中配置某一個屬性或變量的映射關(guān)系時指定了該屬性對應(yīng)的javaType和jdbcType踢关,則Mybatis會從注冊好的TypeHandler中尋找對應(yīng)的javaType和jdbcType組合的TypeHandler進(jìn)行處理,這也是Mybatis最基本的獲取TypeHandler進(jìn)行類型轉(zhuǎn)換的方式粘茄。假設(shè)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中签舞,我們定義了一個對應(yīng)字段interests的映射關(guān)系,并且定義了其javaType為“[Ljava.lang.String;”柒瓣,jdbcType為VARCHAR儒搭,這個時候Mybatis就會到已經(jīng)注冊了的TypeHandler中尋找到能處理javaType和jdbcType對應(yīng)的類型轉(zhuǎn)換的TypeHandler來進(jìn)行處理。在這里就會找到我們注冊的StringArrayTypeHandler芙贫。在上面id為insertUser的insert語句中搂鲫,我們也為變量interests指定了它的javaType和jdbcType屬性,這時候Mybatis也會尋找javaType和jdbcType對應(yīng)的TypeHandler磺平。上面這樣定義是Mybatis最基本也是最完整地獲取到對應(yīng)的TypeHandler的方法魂仍。這里我們來對UserMapper(它的代碼我就不貼出來了拐辽,有Mybatis基礎(chǔ)的都應(yīng)該知道它的代碼)的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();
       }
    }

其輸出結(jié)果如下:

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

我們可以看到Mybatis已經(jīng)把我們存放在數(shù)據(jù)庫中VARCHAR類型的字段interests轉(zhuǎn)換為User類字符串?dāng)?shù)組類型的interests屬性,這說明我們定義的StringArrayTypeHandler發(fā)生作用了擦酌。

除了上面的完整指定一個變量對應(yīng)的javaType和jdbcType薛训,讓Mybatis能夠完美的找到對應(yīng)的TypeHandler之外。我們平常在使用的時候可能還有以下方式:

1.只指定變量對應(yīng)的javaType類型仑氛。這個時候Mybatis會拿著這個javaType和jdbcType為null的組合到注冊的TypeHandler中尋找對應(yīng)的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>

這時候再運行上面的測試程序锯岖,輸出結(jié)果如下:

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

我們可以看到輸出的interests為null介袜,這說明Mybatis沒有使用我們定義的StringArrayTypeHandler來轉(zhuǎn)換interests。

注意出吹,mybatis3中上面這一例子的結(jié)果有些不同遇伞。
經(jīng)測試mybatis3這里不會為null,自動獲取typeHandler有一套更寬容的機制
先根據(jù)javaType找出一個Map集合捶牢,然后再根據(jù)jdbcType匹對鸠珠,若沒有,尋找jdbcType為null的
若還找不到秋麸,就看是不是Map集合中是不是只有一種javaType對應(yīng)的handler渐排,若是則返回此種handler,所以能找到我們自定義的TypeHandler灸蟆。源碼如下:

Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
TypeHandler<?> handler = null;
if (jdbcHandlerMap != null) {
    handler = jdbcHandlerMap.get(jdbcType);
    if (handler == null) {
        handler = jdbcHandlerMap.get(null);
    }
    if (handler == null) {
        // #591
        handler = pickSoleHandler(jdbcHandlerMap);
    }
}
// type drives generics here
return (TypeHandler<T>) handler;

(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>

這個時候再運行上面的測試代碼炒考,輸出結(jié)果如下:

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

這是因為我們是以javaType和null注冊的StringArrayTypeHandler可缚,然后在需要轉(zhuǎn)換interests時又是以相同的javaType和null來尋找的,所以就會找到我們注冊的StringArrayTypeHandler來進(jìn)行類型轉(zhuǎn)換斋枢。

2.只指定變量對應(yīng)的jdbcType類型帘靡。這個時候Mybatis會利用我們指定的返回類型和對應(yīng)的屬性取該屬性在返回類型中對應(yīng)的javaType,之后再拿著該javaType和我們指定的jdbcType到注冊的TypeHandler中獲取對應(yīng)的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>

這個時候繼續(xù)運行上面的測試代碼轰胁,輸出結(jié)果如下:

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

這個時候Mybatis是這樣獲取TypeHandler的:首先它發(fā)現(xiàn)我們的interests沒有指定javaType,這個時候它就會通過我們指定的類型User和屬性interests獲取User類的interests屬性對應(yīng)的java類型朝扼,即String數(shù)組,再拿著獲取到的javaType和我們指定的jdbcType即VARCHAR去尋找對應(yīng)的TypeHandler霎肯,這個時候就找到了我們之前以String數(shù)組和VARCHAR注冊好的StringArrayTypeHandler來處理interests的類型轉(zhuǎn)換擎颖。

3.javaType類型和jdbcType類型都不指定榛斯。這個時候Mybatis會以方式2中的方式獲取到對應(yīng)的javaType類型,然后再以方式1獲取到對應(yīng)的TypeHandler搂捧。這里我們也來做一個測試:

(1)首先驮俗,注冊一個javaType為String數(shù)組,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字段的映射關(guān)系時既不指定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數(shù)組和jdbcType為null注冊了一個StringArrayTypeHandler,然后在定義interests字段的映射關(guān)系時我們沒有指明其對應(yīng)的javaType和jdbcType弱睦,這個時候Mybatis會利用我們指定的User類型和interests屬性獲取到User類的interests屬性對應(yīng)的java類型百姓,即String數(shù)組,然后結(jié)合jdbcType為null去尋找注冊的TypeHandler况木,這樣就找到了StringArrayTypeHandler垒拢。經(jīng)StringArrayTypeHandler的處理就把jdbcType為VARCHAR的數(shù)據(jù)轉(zhuǎn)換為javaType為String數(shù)組的數(shù)據(jù),所以輸出結(jié)果如上所示火惊。

4.還有一種形式是我們直接通過變量的typeHandler屬性指定其對應(yīng)的TypeHandler求类,這個時候Mybatis就會使用該用戶自己指定的TypeHandler來進(jìn)行類型轉(zhuǎn)換,而不再以javaType和jdbcType組合的方式獲取對應(yīng)的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來取對應(yīng)的TypeHandler的話,這里注冊的StringArrayTypeHandler只有在指定變量的javaType為字符串?dāng)?shù)組而jdbcType為VARCHAR的情況下才能被獲取到张症。

(2)然后我們在UserMapper.xml文件中不指定interests字段對應(yīng)的javaType和jdbcType仓技,但是通過typeHandler屬性指定將以StringArrayTypeHandler來進(jìn)行類型轉(zhuǎn)換,代碼如下:

<?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>

運行上面的測試代碼俗他,輸出結(jié)果:

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

這是因為我們指定了進(jìn)行interests字段的映射關(guān)系時使用StringArrayTypeHandler來進(jìn)行類型轉(zhuǎn)換脖捻。當(dāng)指定了某一個字段或變量進(jìn)行映射關(guān)系時所使用的TypeHandler時,Mybatis在需要進(jìn)行類型轉(zhuǎn)換時就使用給定的TypeHandler進(jìn)行類型轉(zhuǎn)換兆衅,而不會再通過javaType和jdbcType的組合去注冊好的TypeHandler中尋找對應(yīng)的TypeHandler地沮。

1.5 Mybatis中自動注冊的TypeHandler

對于一些常用類型的自動轉(zhuǎn)換Mybatis已經(jīng)為我們建立了相關(guān)的TypeHandler,并且會自動注冊它們羡亩,這主要包括:

    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(Byte.class, new ByteTypeHandler());
    register(byte.class, new ByteTypeHandler());
    register(Short.class, new ShortTypeHandler());
    register(short.class, new ShortTypeHandler());
    register(Integer.class, new IntegerTypeHandler());
    register(int.class, new IntegerTypeHandler());
    register(Long.class, new LongTypeHandler());
    register(long.class, new LongTypeHandler());
    register(Float.class, new FloatTypeHandler());
    register(float.class, new FloatTypeHandler());
    register(Double.class, new DoubleTypeHandler());
    register(double.class, new DoubleTypeHandler());
    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(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
    register(BigInteger.class, new BigIntegerTypeHandler());
    register(BigDecimal.class, new BigDecimalTypeHandler());
    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(Object.class, UNKNOWN_TYPE_HANDLER);
    register(Object.class, 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(java.sql.Date.class, new SqlDateTypeHandler());
    register(java.sql.Time.class, new SqlTimeTypeHandler());
    register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
    register(Character.class, new CharacterTypeHandler());
    register(char.class, new CharacterTypeHandler());
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末摩疑,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子畏铆,更是在濱河造成了極大的恐慌雷袋,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辞居,死亡現(xiàn)場離奇詭異楷怒,居然都是意外死亡蛋勺,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門鸠删,熙熙樓的掌柜王于貴愁眉苦臉地迎上來抱完,“玉大人,你說我怎么就攤上這事刃泡∏捎椋” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵烘贴,是天一觀的道長禁添。 經(jīng)常有香客問我,道長庙楚,這世上最難降的妖魔是什么上荡? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮馒闷,結(jié)果婚禮上酪捡,老公的妹妹穿的比我還像新娘。我一直安慰自己纳账,他們只是感情好逛薇,可當(dāng)我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著疏虫,像睡著了一般永罚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上卧秘,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天呢袱,我揣著相機與錄音,去河邊找鬼翅敌。 笑死羞福,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蚯涮。 我是一名探鬼主播治专,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼遭顶!你這毒婦竟也來了张峰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤棒旗,失蹤者是張志新(化名)和其女友劉穎喘批,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡谤祖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年婿滓,在試婚紗的時候發(fā)現(xiàn)自己被綠了老速。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片粥喜。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖橘券,靈堂內(nèi)的尸體忽然破棺而出额湘,到底是詐尸還是另有隱情,我是刑警寧澤旁舰,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布锋华,位于F島的核電站,受9級特大地震影響箭窜,放射性物質(zhì)發(fā)生泄漏毯焕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一磺樱、第九天 我趴在偏房一處隱蔽的房頂上張望纳猫。 院中可真熱鬧,春花似錦竹捉、人聲如沸芜辕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽侵续。三九已至,卻和暖如春憨闰,著一層夾襖步出監(jiān)牢的瞬間状蜗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工鹉动, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留轧坎,地道東北人。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓训裆,卻偏偏與公主長得像眶根,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子边琉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,834評論 2 345