在Mybatis中可以定義一個TypeHandler類型,通過它可以實現Java類型跟數據庫類型的相互轉換。
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; }
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; }
案例
向數據庫寫數據,要寫的是一個Date對象,但是寫到數據庫之后,這個Date對象就變成了Date對象所描述的時間到1970年的秒數,然后當從數據庫讀取這個秒數之后,系統又會自動幫我將這個秒數轉為Date對象。
創建一張User表
user實例
public class User { private Long id; private String username; private String password; private Date regTime; ....
在定義TypeHandler對象
package mybatis.handler; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedTypes; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Date; @MappedJdbcTypes(JdbcType.VARCHAR) @MappedTypes(Date.class) public class MyTypeHandler extends BaseTypeHandler<Date> { @Override public void setNonNullParameter(PreparedStatement ps, int i, Date date, JdbcType jdbcType) throws SQLException { ps.setString(i,String.valueOf(date.getTime())); } @Override public Date getNullableResult(ResultSet rs, String columnName) throws SQLException { return new Date(rs.getLong(columnName));//以long的數據類型形式讀取內容 } @Override public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return new Date(rs.getLong(columnIndex)); } @Override public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return cs.getDate(columnIndex); } }
1.@MappedJdbcTypes定義是JdbcType類型,必須要是枚舉類org.apache.ibatis.type.JdbcType所枚舉的數據類型 2.@MappedTypes定義的是JavaType的數據類型,描述哪些Java類型可被攔截。 3.在我們啟用了自定義的這個TypeHandler之后,數據的讀寫都會被這個類所過濾 4.在setNonNullParameter方法中,重新定義要寫往數據庫的數據。 5.在另外三個方法中我們將從數據庫讀出的數據類型進行轉換。
在配置文件中注冊自定義的handler
mybatis配置文件中注冊typeHandler,注冊有兩種不同的方式:1、直接注冊一個包中所有的typeHandler,系統在啟動時會自動掃描包下的所有文件 2、一個類一個類的注冊
<!--需要在別名標簽之后--> <typeHandlers> <typeHandler handler="mybatis.handler.MyTypeHandler"/> </typeHandlers> or <typeHandlers> <package name="mybatis.handler"/> </typeHandlers>
在Mapper中配置
<resultMap id="userResult" type="user"> <result typeHandler="mybatis.handler.MyTypeHandler" javaType="java.util.Date" jdbcType="VARCHAR" property="regTime" column="regTime"/> </resultMap>
select中使用這個resultMap:
<select id="getUser" parameterType="Integer" resultMap="userResult"> select * from user where id=#{id} </select>
想要在插入的時候,啟用自定義的typeHandler,除了在mybatis配置文件中配置的方法,也可以insert節點中簡單配置一下,如下:
<insert id="insertUser" parameterType="org.sang.bean.User"> INSERT INTO user(username,password,regTime)
VALUES (#{username},#{password},#{regTime,javaType=Date,jdbcType=VARCHAR,typeHandler=mybatis.handler.MyDateTypeHandler}) </insert>
插入結果:
讀出結果
總結就是讀取時的配置要和插入時的配置分開,讀取時數據轉換,有兩種配置方式,分別是resultMap和在mybatis配置文件中配置typeHandlers,插入時的配置就是在insert節點中進行配置。