1.1 目錄
1.2 建立TypeHandler
1.2.1 TypeHandler接口
1.2.2 BaseTypeHandler抽象類
1.3 注冊(cè)TypeHandler
1.4 Mybatis自動(dòng)獲取TypeHandler
1.5 Mybatis中自動(dòng)注冊(cè)的TypeHandler
1.2建立TypeHandler
我們知道java有java的數(shù)據(jù)類型,數(shù)據(jù)庫(kù)有數(shù)據(jù)庫(kù)的數(shù)據(jù)類型,那么我們?cè)谕鶖?shù)據(jù)庫(kù)中插入數(shù)據(jù)的時(shí)候是如何把java類型當(dāng)做數(shù)據(jù)庫(kù)類型插入數(shù)據(jù)庫(kù),在從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)的時(shí)候又是如何把數(shù)據(jù)庫(kù)類型當(dāng)做java類型來(lái)處理呢?這中間必然要經(jīng)過(guò)一個(gè)類型轉(zhuǎn)換。在Mybatis中我們可以定義一個(gè)叫做TypeHandler類型處理器的東西,通過(guò)它可以實(shí)現(xiàn)Java類型跟數(shù)據(jù)庫(kù)類型的相互轉(zhuǎn)換。下面將就如何建立自己的TypeHandler做一個(gè)簡(jiǎn)要介紹。
1.2.1TypeHandler接口
在Mybatis中要實(shí)現(xiàn)自己的TypeHandler就需要實(shí)現(xiàn)Mybatis為我們提供的TypeHandler接口。在TypeHandler中定義了四個(gè)方法:
public interface TypeHandler<T> {
/**
* 用于定義在Mybatis設(shè)置參數(shù)時(shí)該如何把Java類型的參數(shù)轉(zhuǎn)換為對(duì)應(yīng)的數(shù)據(jù)庫(kù)類型
* @param ps 當(dāng)前的PreparedStatement對(duì)象
* @param i 當(dāng)前參數(shù)的位置
* @param parameter 當(dāng)前參數(shù)的Java對(duì)象
* @param jdbcType 當(dāng)前參數(shù)的數(shù)據(jù)庫(kù)類型
* @throws SQLException
*/
void setParameter(PreparedStatement ps, int i, T parameter,
JdbcType jdbcType) throws SQLException;
/**
* 用于在Mybatis獲取數(shù)據(jù)結(jié)果集時(shí)如何把數(shù)據(jù)庫(kù)類型轉(zhuǎn)換為對(duì)應(yīng)的Java類型
* @param rs 當(dāng)前的結(jié)果集
* @param columnName 當(dāng)前的字段名稱
* @return 轉(zhuǎn)換后的Java對(duì)象
* @throws SQLException
*/
T getResult(ResultSet rs, String columnName) throws SQLException;
/**
* 用于在Mybatis通過(guò)字段位置獲取字段數(shù)據(jù)時(shí)把數(shù)據(jù)庫(kù)類型轉(zhuǎn)換為對(duì)應(yīng)的Java類型
* @param rs 當(dāng)前的結(jié)果集
* @param columnIndex 當(dāng)前字段的位置
* @return 轉(zhuǎn)換后的Java對(duì)象
* @throws SQLException
*/
T getResult(ResultSet rs, int columnIndex) throws SQLException;
/**
* 用于Mybatis在調(diào)用存儲(chǔ)過(guò)程后把數(shù)據(jù)庫(kù)類型的數(shù)據(jù)轉(zhuǎn)換為對(duì)應(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è)我們有一個(gè)實(shí)體對(duì)象User,其中有一個(gè)屬性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ù)庫(kù)中,然后在取出來(lái)的時(shí)候又把它還原為一個(gè)String數(shù)組。這個(gè)時(shí)候我們就可以給它定義一個(gè)TypeHandler專門(mén)來(lái)處理String數(shù)組類型和數(shù)據(jù)庫(kù)VARCHAR類型的相互轉(zhuǎn)換。在這里我們建立一個(gè)名叫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抽象類
在實(shí)現(xiàn)自己的TypeHandler時(shí),除了上面提到的實(shí)現(xiàn)最原始的接口之外,Mybatis還為我們提供了一個(gè)實(shí)現(xiàn)了TypeHandler接口的抽象類BaseTypeHandler。所以我們也可以通過(guò)繼承BaseTypeHandler來(lái)實(shí)現(xiàn)自己的TypeHandler。
我們先來(lái)看一下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對(duì)TypeHandler接口的四個(gè)方法做了一個(gè)簡(jiǎn)單的選擇,把null值的情況都做了一個(gè)過(guò)濾,核心的取值和設(shè)值的方法還是抽象出來(lái)了供子類來(lái)實(shí)現(xiàn)。使用BaseTypeHandler還有一個(gè)好處是它繼承了另外一個(gè)叫做TypeReference的抽象類,通過(guò)TypeReference的getRawType()方法可以獲取到當(dāng)前TypeHandler所使用泛型的原始類型。這對(duì)Mybatis在注冊(cè)TypeHandler的時(shí)候是非常有好處的。在沒(méi)有指定javaType的情況下,Mybatis在注冊(cè)TypeHandler時(shí)可以通過(guò)它來(lái)獲取當(dāng)前TypeHandler所使用泛型的原始類型作為要注冊(cè)的TypeHandler的javaType類型,這個(gè)在講到Mybatis注冊(cè)TypeHandler的方式時(shí)將講到。
當(dāng)通過(guò)繼承BaseTypeHandler來(lái)實(shí)現(xiàn)自己的TypeHandler時(shí),我們的StringArrayTypeHandler應(yīng)該這樣寫(xiě):
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 注冊(cè)TypeHandler
建立了自己的TypeHandler之后就需要把它注冊(cè)到Mybatis的配置文件中,讓Mybatis能夠識(shí)別并使用它。注冊(cè)TypeHandler主要有兩種方式,一種是通過(guò)在Mybatis配置文件中定義typeHandlers元素的子元素typeHandler來(lái)注冊(cè);另一種是通過(guò)在Mybatis配置文件中定義typeHandlers元素的子元素package來(lái)注冊(cè)。使用typeHandler子元素注冊(cè)時(shí)一次只能注冊(cè)一個(gè)TypeHandler,而使用package子元素注冊(cè)時(shí),Mybatis會(huì)把指定包里面的所有TypeHandler都注冊(cè)為T(mén)ypeHandler。使用typeHandler子元素注冊(cè)時(shí)我們需要通過(guò)它的handler屬性來(lái)指明當(dāng)前要注冊(cè)的TypeHandler的全名稱,這個(gè)屬性是必須要的。另外還有兩個(gè)附加屬性可以指定,一個(gè)是javaType,用以指定對(duì)應(yīng)的java類型;另一個(gè)是jdbcType,用以指定對(duì)應(yīng)的jdbc類型。使用package子元素注冊(cè)時(shí)需要我們通過(guò)它的name屬性來(lái)指定要掃描的包,如果這個(gè)時(shí)候我們也需要指定對(duì)應(yīng)TypeHandler的javaType和jdbcType的話就需要我們?cè)赥ypeHandler類上使用注解來(lái)定義了。Mybatis注冊(cè)TypeHandler最基本的方式就是建立一個(gè)javaType、jdbcType和TypeHandler的對(duì)應(yīng)關(guān)系。在使用typeHandler子元素進(jìn)行注冊(cè)的時(shí)候,有三種類型的注冊(cè)方式:
1.如果我們指定了javaType和jdbcType,那么Mybatis會(huì)注冊(cè)一個(gè)對(duì)應(yīng)javaType和jdbcType的TypeHandler。
2.如果我們只指定了javaType屬性,那么這個(gè)時(shí)候又分兩種情況:
(1)如果我們通過(guò)注解的形式在TypeHandler類上用@MappedJdbcTypes指定了對(duì)應(yīng)的jdbcType,那么Mybatis會(huì)一一注冊(cè)指定的javaType、jdbcType和TypeHandler的組合,也包括使用這種形式指定了jdbcType為null的情況。現(xiàn)假設(shè)我們有如下這樣一個(gè)StringArrayTypeHandler:
@MappedJdbcTypes({JdbcType.VARCHAR})
public class StringArrayTypeHandler implements TypeHandler<String[]> {
//..中間的實(shí)現(xiàn)代碼省略了
//..
}
然后我們?cè)贛ybatis的配置文件中這樣注冊(cè)它:
<typeHandlers>
<typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler" javaType="[Ljava.lang.String;"/>
</typeHandlers>
則Mybatis在實(shí)際注冊(cè)的時(shí)候是以javaType為String數(shù)組,jdbcType為VARCHAR來(lái)注冊(cè)StringArrayTypeHandler的。
(2)如果沒(méi)有使用@MappedJdbcTypes注解指定對(duì)應(yīng)的jdbcType,那么這個(gè)時(shí)候Mybatis會(huì)把jdbcType置為null,然后注冊(cè)一個(gè)javaType、null和TypeHandler的組合。
3.既沒(méi)有指定javaType屬性,又沒(méi)有指定jdbcType屬性,或者只指定了jdbcType屬性。這種情況又分三種類型:
(1)如果TypeHandler類上使用了注解@MappedTypes指定了對(duì)應(yīng)的javaType,那么Mybatis將一一利用對(duì)應(yīng)的javaType和TypeHandler去以2的方式進(jìn)行注冊(cè)。現(xiàn)假設(shè)我們定義了這樣一個(gè)StringArrayTypeHandler:
@MappedTypes({String[].class})
@MappedJdbcTypes({JdbcType.VARCHAR})
public class StringArrayTypeHandler implements TypeHandler<String[]> {
}
然后,在Mybatis的配置文件中注冊(cè)它時(shí)既不指定它的javaType屬性也不指定它的jdbcType屬性,代碼如下:
<typeHandlers>
<typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler"/>
</typeHandlers>
則這個(gè)時(shí)候Mybatis在注冊(cè)該StringArrayTypeHandler的時(shí)候首先會(huì)判斷它上面有沒(méi)有標(biāo)注@MappedTypes,如果有則把它的MappedTypes一一拿出來(lái)作為javaType,然后以方式2進(jìn)行注冊(cè)。所以這里實(shí)際上Mybatis注冊(cè)的還是javaType為String數(shù)組,jdbcType為VARCHAR這樣一個(gè)組合的TypeHandler。
(2)TypeHandler類上沒(méi)有使用@MappedTypes指定對(duì)應(yīng)的javaType時(shí),如果當(dāng)前的TypeHandler繼承了TypeReference抽象類,Mybatis會(huì)利用TypeReference的getRawType()方法取到當(dāng)前TypeHandler泛型對(duì)應(yīng)的javaType類型,然后利用取得的javaType和TypeHandler以2的方式進(jìn)行注冊(cè),同時(shí)還包括一個(gè)javaType為null以方式2進(jìn)行的注冊(cè)。TypeReference是Mybatis中定義的一個(gè)抽象類,主要是用來(lái)獲取對(duì)應(yīng)的泛型類型。
(3)TypeHandler類上既沒(méi)有標(biāo)注@MappedTypes,又沒(méi)有繼承TypeReference抽象類。這種情況Mybatis會(huì)以null和null的組合注冊(cè)該TypeHandler。
使用package子元素注冊(cè)的TypeHandler會(huì)以上面的方式3進(jìn)行注冊(cè)。
這里我們?nèi)缦伦?cè)我們的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數(shù)組的全類名稱是“[Ljava.lang.String;”,所以上面在注冊(cè)StringArrayTypeHandler時(shí)定義的javaType屬性為“[Ljava.lang.String;”。
1.4Mybatis自動(dòng)獲取TypeHandler
在介紹了Mybatis是如何注冊(cè)TypeHandler之后就介紹一下Mybatis是如何獲取對(duì)應(yīng)的TypeHandler進(jìn)行類型轉(zhuǎn)換的。
如果我們?cè)贛apper.xml文件中配置某一個(gè)屬性或變量的映射關(guān)系時(shí)指定了該屬性對(duì)應(yīng)的javaType和jdbcType,則Mybatis會(huì)從注冊(cè)好的TypeHandler中尋找對(duì)應(yīng)的javaType和jdbcType組合的TypeHandler進(jìn)行處理,這也是Mybatis最基本的獲取TypeHandler進(jìn)行類型轉(zhuǎn)換的方式。假設(shè)Mybatis配置文件中有這么一段TypeHandler的注冊(cè)信息:
<typeHandlers>
<typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler" javaType="[Ljava.lang.String;" jdbcType="VARCHAR"/>
</typeHandlers>
看這樣一個(gè)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中,我們定義了一個(gè)對(duì)應(yīng)字段interests的映射關(guān)系,并且定義了其javaType為“[Ljava.lang.String;”,jdbcType為VARCHAR,這個(gè)時(shí)候Mybatis就會(huì)到已經(jīng)注冊(cè)了的TypeHandler中尋找到能處理javaType和jdbcType對(duì)應(yīng)的類型轉(zhuǎn)換的TypeHandler來(lái)進(jìn)行處理。在這里就會(huì)找到我們注冊(cè)的StringArrayTypeHandler。在上面id為insertUser的insert語(yǔ)句中,我們也為變量interests指定了它的javaType和jdbcType屬性,這時(shí)候Mybatis也會(huì)尋找javaType和jdbcType對(duì)應(yīng)的TypeHandler。上面這樣定義是Mybatis最基本也是最完整地獲取到對(duì)應(yīng)的TypeHandler的方法。這里我們來(lái)對(duì)UserMapper(它的代碼我就不貼出來(lái)了,有Mybatis基礎(chǔ)的都應(yīng)該知道它的代碼)的findById來(lái)做一個(gè)測(cè)試:
@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ù)庫(kù)中VARCHAR類型的字段interests轉(zhuǎn)換為User類字符串?dāng)?shù)組類型的interests屬性,這說(shuō)明我們定義的StringArrayTypeHandler發(fā)生作用了。
除了上面的完整指定一個(gè)變量對(duì)應(yīng)的javaType和jdbcType,讓Mybatis能夠完美的找到對(duì)應(yīng)的TypeHandler之外。我們平常在使用的時(shí)候可能還有以下方式:
1.只指定變量對(duì)應(yīng)的javaType類型。這個(gè)時(shí)候Mybatis會(huì)拿著這個(gè)javaType和jdbcType為null的組合到注冊(cè)的TypeHandler中尋找對(duì)應(yīng)的TypeHandler。這里我們同樣來(lái)做一個(gè)測(cè)試:
(1)不動(dòng)StringArrayTypeHandler的注冊(cè)信息,把我們的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>
這時(shí)候再運(yùn)行上面的測(cè)試程序,輸出結(jié)果如下:
User [age=30, id=20, interests=null, name=張三]
我們可以看到輸出的interests為null,這說(shuō)明Mybatis沒(méi)有使用我們定義的StringArrayTypeHandler來(lái)轉(zhuǎn)換interests。
注意,mybatis3中上面這一例子的結(jié)果有些不同。
經(jīng)測(cè)試mybatis3這里不會(huì)為null,自動(dòng)獲取typeHandler有一套更寬容的機(jī)制
先根據(jù)javaType找出一個(gè)Map集合,然后再根據(jù)jdbcType匹對(duì),若沒(méi)有,尋找jdbcType為null的
若還找不到,就看是不是Map集合中是不是只有一種javaType對(duì)應(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屬性來(lái)注冊(cè)我們的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>
這個(gè)時(shí)候再運(yùn)行上面的測(cè)試代碼,輸出結(jié)果如下:
User [age=30, id=20, interests=[Music, Movie, NBA], name=張三]
這是因?yàn)槲覀兪且詊avaType和null注冊(cè)的StringArrayTypeHandler,然后在需要轉(zhuǎn)換interests時(shí)又是以相同的javaType和null來(lái)尋找的,所以就會(huì)找到我們注冊(cè)的StringArrayTypeHandler來(lái)進(jìn)行類型轉(zhuǎn)換。
2.只指定變量對(duì)應(yīng)的jdbcType類型。這個(gè)時(shí)候Mybatis會(huì)利用我們指定的返回類型和對(duì)應(yīng)的屬性取該屬性在返回類型中對(duì)應(yīng)的javaType,之后再拿著該javaType和我們指定的jdbcType到注冊(cè)的TypeHandler中獲取對(duì)應(yīng)的TypeHandler。這里我們來(lái)看這樣一個(gè)測(cè)試:
保持之前指定javaType和jdbcType的方式注冊(cè)StringArrayTypeHandler,然后在定義interests變量的時(shí)候不指定javaType,只指定jdbcType,這個(gè)時(shí)候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>
這個(gè)時(shí)候繼續(xù)運(yùn)行上面的測(cè)試代碼,輸出結(jié)果如下:
User [age=30, id=20, interests=[Music, Movie, NBA], name=張三]
這個(gè)時(shí)候Mybatis是這樣獲取TypeHandler的:首先它發(fā)現(xiàn)我們的interests沒(méi)有指定javaType,這個(gè)時(shí)候它就會(huì)通過(guò)我們指定的類型User和屬性interests獲取User類的interests屬性對(duì)應(yīng)的java類型,即String數(shù)組,再拿著獲取到的javaType和我們指定的jdbcType即VARCHAR去尋找對(duì)應(yīng)的TypeHandler,這個(gè)時(shí)候就找到了我們之前以String數(shù)組和VARCHAR注冊(cè)好的StringArrayTypeHandler來(lái)處理interests的類型轉(zhuǎn)換。
3.javaType類型和jdbcType類型都不指定。這個(gè)時(shí)候Mybatis會(huì)以方式2中的方式獲取到對(duì)應(yīng)的javaType類型,然后再以方式1獲取到對(duì)應(yīng)的TypeHandler。這里我們也來(lái)做一個(gè)測(cè)試:
(1)首先,注冊(cè)一個(gè)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)系時(shí)既不指定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>
這個(gè)時(shí)候再運(yùn)行上面的測(cè)試代碼,輸出如下:
User [age=30, id=20, interests=[Music, Movie, NBA], name=張三]
這種情況是這樣的:我們以javaType為String數(shù)組和jdbcType為null注冊(cè)了一個(gè)StringArrayTypeHandler,然后在定義interests字段的映射關(guān)系時(shí)我們沒(méi)有指明其對(duì)應(yīng)的javaType和jdbcType,這個(gè)時(shí)候Mybatis會(huì)利用我們指定的User類型和interests屬性獲取到User類的interests屬性對(duì)應(yīng)的java類型,即String數(shù)組,然后結(jié)合jdbcType為null去尋找注冊(cè)的TypeHandler,這樣就找到了StringArrayTypeHandler。經(jīng)StringArrayTypeHandler的處理就把jdbcType為VARCHAR的數(shù)據(jù)轉(zhuǎn)換為javaType為String數(shù)組的數(shù)據(jù),所以輸出結(jié)果如上所示。
4.還有一種形式是我們直接通過(guò)變量的typeHandler屬性指定其對(duì)應(yīng)的TypeHandler,這個(gè)時(shí)候Mybatis就會(huì)使用該用戶自己指定的TypeHandler來(lái)進(jìn)行類型轉(zhuǎn)換,而不再以javaType和jdbcType組合的方式獲取對(duì)應(yīng)的TypeHandler。這里我們也來(lái)做一個(gè)測(cè)試:
(1)首先在Mybatis的配置文件中以javaType和jdbcType配套的方式注冊(cè)一個(gè)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>
按照前面說(shuō)的Mybatis按照變量的javaType和jdbcType來(lái)取對(duì)應(yīng)的TypeHandler的話,這里注冊(cè)的StringArrayTypeHandler只有在指定變量的javaType為字符串?dāng)?shù)組而jdbcType為VARCHAR的情況下才能被獲取到。
(2)然后我們?cè)赨serMapper.xml文件中不指定interests字段對(duì)應(yīng)的javaType和jdbcType,但是通過(guò)typeHandler屬性指定將以StringArrayTypeHandler來(lái)進(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>
運(yùn)行上面的測(cè)試代碼,輸出結(jié)果:
User [age=30, id=20, interests=[Music, Movie, NBA], name=張三]
這是因?yàn)槲覀冎付诉M(jìn)行interests字段的映射關(guān)系時(shí)使用StringArrayTypeHandler來(lái)進(jìn)行類型轉(zhuǎn)換。當(dāng)指定了某一個(gè)字段或變量進(jìn)行映射關(guān)系時(shí)所使用的TypeHandler時(shí),Mybatis在需要進(jìn)行類型轉(zhuǎn)換時(shí)就使用給定的TypeHandler進(jìn)行類型轉(zhuǎn)換,而不會(huì)再通過(guò)javaType和jdbcType的組合去注冊(cè)好的TypeHandler中尋找對(duì)應(yīng)的TypeHandler。
1.5 Mybatis中自動(dòng)注冊(cè)的TypeHandler
對(duì)于一些常用類型的自動(dòng)轉(zhuǎn)換Mybatis已經(jīng)為我們建立了相關(guān)的TypeHandler,并且會(huì)自動(dòng)注冊(cè)它們,這主要包括:
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());