本文部落格地址:http://blog.csdn.net/soonfly/article/details/65628372 (轉載請註明出處)
我們在寫mapper映射器的設定檔時,不經意間已經用到類型轉換,不過是mybatis幫我們完成的。
<update id="update" parameterType="twm.mybatisdemo.pojo.User"> update user set username=#{username},password=#{password},address=#{address} where id=#{id}</update>
像上面例子,只需要向update方法傳入一個user對象,mybatis利用反射拆開user對象,然後根據對象中的欄位在預先處理語句(PreparedStatement)中設定參數,並且根據欄位的類型,使用setXXX()方式設定相應的值。XXX可以是Integer,String,Date等Java類型。
同理,在從結果集(ResultSet)中取出一個值時,將使用rs.getInt、rs.getString、rs.getTimeStamp等方法將資料轉換為Java對象。
那麼問題來了,javaType和jdbcType的轉換關係由誰來定呢。這就是類型處理器(type handlers)的功能所在。
比如java.lang.String轉成JDBC.Varchar,java.lang.Integer轉成JDBC.int。MyBatis使用內建的類型處理器能轉換所有的基礎資料型別 (Elementary Data Type)、基本類型的封裝類型、byte[] 、java.util.Date、java.sql.Date、java,sql.Time、java.sql.Timestamp、java枚舉類型等。
不過對於自訂的類型怎麼辦呢。
假設上面的address在資料庫欄位類型是varchar(50),但是在JAVA twm.mybatisdemo.pojo.User類中的address欄位並不是String類型,而是一個自訂的Address類型:
public class Address { String province; String city; public Address() {} public Address(String province, String city) { this.province = province; this.city = city; } //getter and setter......}
碰到這種情況,mybatis基本懵逼了。。。
如果啟動並執行話就報錯誤:
Type handler was null on parameter mapping for property ‘address’. It
was either not specified and/or could not be found for the javaType /
jdbcType combination specified.
address欄位需要傳遞給#{address},而address欄位是Address類型的,MyBatis並不知道該怎樣來處理這個類型的對象。
因此,需要建立一個自訂的類型處理器(TypeHandler)了
1、建立類型處理器:
class AddressTypeHandler extends BaseTypeHandler<Address>{}
通過ide自動產生代碼,可以看到父類BaseTypeHandler有四個方法需要我們實現,包括三個get方法,一個set方法。
2、實現get方法
三個get方法都是用於將資料庫獲得的記錄集裡的address欄位轉成java Address類型的對象。
所以我們首先給Address類增加一個構造方法,用途:根據字串產生一個執行個體對象。
//假設我們儲存在db中的字串是以","號分隔省市關係的public Address(String address) { if (address != null) { String[] segments = address.split(","); if (segments.length > 1) { this.province = segments[0]; this.city = segments[1]; } else if (segments.length > 0) { this.city = segments[0]; } } }
然後實現AddressTypeHandler類中的三個get方法:(有強迫症,不喜歡看arg0,arg1。所以順便也改一下)
@Overridepublic Address getNullableResult(ResultSet rSet, String columnName) throws SQLException { return new Address(rSet.getString(columnName));}@Overridepublic Address getNullableResult(ResultSet rSet, int columnIndex) throws SQLException { return new Address(rSet.getString(columnIndex));}@Overridepublic Address getNullableResult(CallableStatement cStatement, int columnIndex) throws SQLException { return new Address(cStatement.getString(columnIndex));}
3、實現set方法
set方法是用來將java類型轉成資料庫儲存的類型。
這裡我們先實現一下Address類的toString()方法(如果toString另有它用,那麼就另外用一個方法名)
@Overridepublic String toString() { return this.province + "," + this.city;}
然後實現AddressTypeHandler類中的setNonNullParameter
@Overridepublic void setNonNullParameter(PreparedStatement pStatement, int index, Address address, JdbcType jdbcType) throws SQLException { pStatement.setString(index, address.toString());}
4、在myBatis設定檔中註冊該類
<!-- 註冊自訂類型處理器 --><typeHandlers> <typeHandler handler="twm.mybatisdemo.type.AddressTypeHandler" /></typeHandlers>
這裡有個小插曲,我當時在設定檔最後插入了這段,結果提示錯誤:
The content of element type “configuration” must match
“(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)”.
原來在configuration裡的標籤,必須按照提示的這個順序寫,不然就報錯。
因此typeHandlers必須放在environments前面,typeAliases後面。
回顧一下,是不是感覺跟序列化/還原序列化這套路很相似。
本文部落格地址:http://blog.csdn.net/soonfly/article/details/65628372 (轉載請註明出處)