MyBatis自定義類型處理器(typeHandler)
我們執行sql
語句通過PreparedStatement
語句實現,PreparedStatement
會設置?
值,類型處理器幫PreparedStatement
找到對應的set
方法,到底是選擇setInt
、setString
或setDate...
注意:類型處理器默認可以處理基本的數據類型以及對應的包裝類,uitl.Date、sql.Date
等,但無法默認處理自定義類型。
當MyBatis
將一個Java
對象作爲輸入參數執行INSERT
語句操作時,它會創建一個PreparedStatement
對象,並且使用setXXX()
方法對佔位符設置相應的參數值。這裏,XXX
可以是Int
,String
,Date
等Java
對象屬性類型的任意一個。示例如下:
<insert id="insertStudent" parameterType="Student">
INSERT INTO STUDENTS(STUD_ID,NAME,EMAIL,DOB)
VALUES(#{studId},#{name},#{email},#{dob})
</insert>
爲了執行這個語句,MyBatis
將採取以下一系列動作:
創建一個有佔位符的PreparedStatement接口,如下:
PreparedStatement ps = connection.prepareStatement("INSERT INTO STUDENTS(STUD_ID,NAME,EMAIL,DOB) VALUES(?,?,?,?)");
檢查Student
對象的屬性studId
的類型,然後使用合適setXXX()
方法去設置參數值。這裏studId
是Integer
類型,所以會使用setInt()
方法:
ps.setInt(1,student.getStudId());
類似地,對於name
和email
屬性都是String
類型的,MyBatis
使用setString()
方法設置參數。
至於dob
屬性, MyBatis
會使用setDate()
方法設置dob
處佔位符位置的值。MyBaits
會將java.util.Date
類型轉換爲java.sql.Timestamp
並設值:
ps.setTimestamp(4, new Timestamp((student.getDob()).getTime()));
但MyBatis
是怎麼知道對於Integer
類型屬性使用setInt()
和String
類型屬性使用setString()
方法呢?其實MyBatis
是通過使用類型處理器typeHandlers
來決定這麼做的。
MyBatis
對於以下的類型使用內建的類型處理器:所有的基本數據類型、基本類型的包裝類型、byte[]
、java.util.Date
、java.sql.Date
、java,sql.Time
、java.sql.Timestamp
、java枚舉類型
等。所以當MyBatis
發現屬性的類型屬於上述類型,他會使用對應的類型處理器將值設置到PreparedStatement
中,同樣地,當SQL
結果集封裝成java
類對象的時候,也有類似的過程。
那如果有一個自定義的類型,怎麼存儲存儲到數據庫呢?示例如下:假設表STUDENTS
有一個 PHONE
字段,類型爲 VARCHAR2(15)
,而 Student
類有一個自定義類型PhoneNumber
,這個類型包括三個屬性:國家編號、區號、電話號碼。
public class PhoneNumber{
private String countryCode;
private String stateCode;
private String number;
public PhoneNumber(){
}
public PhoneNumber(String countryCode, String stateCode, String number) {
this.countryCode = countryCode;
this.stateCode = stateCode;
this.number = number;
}
public String getAsString() {
return countryCode + "-" + stateCode + "-" + number;
}
// Setters and getters
}
public class Student{
private Integer id;
private String name;
private String email;
private PhoneNumber phone;
// Setters and getters
}
StudentMapper.xml
配置如下:
<insert id="insertStudent" parameter Type="Student">
insert into students(name,email,phone)
values(#{name},#{email},#{phone})
</insert>
這個xml
文件裏面,參數對象中的屬性phone
的值需要傳遞給#{phone}
,而參數對象的屬性phone
是 PhoneNumber
類型。但是,MyBatis
並不知道該怎樣來處理這個類型的對象。爲了讓MyBatis
明白怎樣處理這個自定義的Java
對象類型,如PhoneNumber
,我們可以創建一個自定義的類型處理器,MyBatis
提供了抽象類BaseTypeHandler<T>
,我們可以繼承此類創建自定義類型處理器。代碼如下:
package com.au.typehandlers;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import com.briup.pojo.PhoneNumber;
public class PhoneTypeHandler extends BaseTypeHandler<PhoneNumber>{
//遇到PhoneNumber參數的時候應該如何在ps中設置值
@Override
public void setNonNullParameter(PreparedStatement ps, int i, PhoneNumber parameter, JdbcType jdbcType)
throws SQLException {
ps.setString(i, parameter.getAsString());
}
//查詢中遇到PhoneNumber類型的應該如何封裝(使用列名封裝)
@Override
public PhoneNumber getNullableResult(ResultSet rs, String columnName) throws SQLException {
return new PhoneNumber(rs.getString(columnName));
}
//查詢中遇到PhoneNumber類型的應該如何封裝(使用列的下標)
@Override
public PhoneNumber getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return new PhoneNumber(rs.getString(columnIndex));
}
//CallableStatement使用中遇到了PhoneNumber類型的應該如何封裝
@Override
public PhoneNumber getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return new PhoneNumber(cs.getString(columnIndex));
}
}
注意:使用ps.setString()
和rs.getString()
方法是因爲在數據庫的表中phone
列是VARCHAR
類型。
最後需要在mybatis-config.xml
中註冊這個類型處理器:
<?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="application.properties" />
<typeHandlers>
<typeHandler handler="com.au.typehandlers.PhoneTypeHandler" />
</typeHandlers>
</configuration>
註冊PhoneTypeHandler
後,MyBatis
就能夠將Phone
類型的對象值存儲到VARCHAR
類型的列上。