MyBatis之TypeHandler
在大學寫web應用的時候經常會遇到這麼個問題,當我要插入一條資料,某個資料是Date型別,資料庫中卻是VARCHAR型別,這個時候可能會傻乎乎的先把這個資料自己手動轉換成String型別再插入到資料庫中,其實大可不必。MyBatis為我們提供了更好的方法即是TypeHandler來應對Java和jdbc欄位型別不匹配的情況。MyBatis中內建了不少的TypeHandler,如果我們想要自己自定義一個TypeHandler可以實現TypeHandler介面,也可以繼承BaseTypeHandler類。下面我們實現一個將Java中的Date型別利用我們自定義的ExampleTypeHandler來轉換為JDBC的VARCHAR型別。
我們對MyBatis的介紹先侷限在使用,在會使用過後我們再究其原理、原始碼。
package day_8_mybatis.util; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Date; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; /** * 注意在引入Date所在的包時,是java.util.Date,而不是java.sql.Date,這一點不要搞錯。 * @author turbo * * 2016年10月23日 */ public class ExampleTypeHandler extends BaseTypeHandler<Date> { /* 根據列名,獲取可以為空的結果 * @see org.apache.ibatis.type.BaseTypeHandler#getNullableResult(java.sql.ResultSet, java.lang.String) */ @Override public Date getNullableResult(ResultSet rs, String columnName) throws SQLException { String sqlTimetamp = rs.getString(columnName); if (null != sqlTimetamp){ return new Date(Long.valueOf(sqlTimetamp)); } return null; } /* 根據列索引,獲取可以為空的結果 * @see org.apache.ibatis.type.BaseTypeHandler#getNullableResult(java.sql.ResultSet, int) */ @Override public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException { String sqlTimetamp = rs.getString(columnIndex); if (null != sqlTimetamp){ return new Date(Long.valueOf(sqlTimetamp)); } return null; } /*47 * @see org.apache.ibatis.type.BaseTypeHandler#getNullableResult(java.sql.CallableStatement, int) */ @Override public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { String sqlTimetamp = cs.getString(columnIndex); if (null != sqlTimetamp){ return new Date(Long.valueOf(sqlTimetamp)); } return null; } /* 設定非空引數 * @see org.apache.ibatis.type.BaseTypeHandler#setNonNullParameter(java.sql.PreparedStatement, int, java.lang.Object, org.apache.ibatis.type.JdbcType) */ @Override public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, String.valueOf(parameter.getTime())); } }
我們已經自定義了一個TypeHandler,接著我們要在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> <!-- 注意configuration中各個屬性配置的順序應為:properties,settings,typeAliases,typeHandlers,objectFactory,objectWrapperFactory,reflectorFactory,plugins,environments,databaseIdProvider,mappers)--> <properties> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test"/> <property name="username" value="root"/> <property name="password" value="0000"/> </properties> <typeHandlers> <typeHandler handler="day_8_mybatis.util.ExampleTypeHandler" javaType="java.util.Date" jdbcType="VARCHAR"/> </typeHandlers> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="day_8_mybatis/mapper/UserMapper.xml"/> <mapper resource="day_8_mybatis/mapper/NoteMapper.xml"/> </mappers> </configuration>
注意各個屬性配置有順序之分,不能隨意穿插。
準備工作已經做完了,我們接著按部就班的實現POJO類 Note裡面包含id和date欄位。
package day_8_mybatis.pojo;
import java.util.Date;
/**
* @author turbo
*
* 2016年10月23日
*/
public class Note {
private int id;
private Date date;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
接著是負責與資料庫互動Dao層的NoteMapper介面,我們只舉例查詢和插入。
package day_8_mybatis.mapper;
import day_8_mybatis.pojo.Note;
/**
* @author turbo
*
* 2016年10月23日
*/
public interface NoteMapper {
Note queryNote(int id);
void insertNote(Note note);
}
我們再來看看在NoteMapper.xml中是如何利用我們剛才自定義的TypeHandler。
<?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="day_8_mybatis.mapper.NoteMapper">
<resultMap type="day_8_mybatis.pojo.Note" id="note-base">
<result property="id" column="id"/>
<result property="date" column="date" typeHandler="day_8_mybatis.util.ExampleTypeHandler"/>
</resultMap>
<select id="queryNote" parameterType="int" resultMap="note-base">
select * from note where id = #{id}
</select>
<insert id="insertNote" parameterType="day_8_mybatis.pojo.Note">
insert into note (id, date) values(#{id}, #{date, typeHandler=day_8_mybatis.util.ExampleTypeHandler}) <!--使用我們自定義的TypeHandler-->
</insert>
</mapper>
最後我們在客戶端測試一下。
package day_8_mybatis;
import java.io.IOException;
import java.util.Date;
import org.apache.ibatis.session.SqlSession;
import day_8_mybatis.mapper.NoteMapper;
import day_8_mybatis.pojo.Note;
import day_8_mybatis.util.SessionFactory;
/**
* 客戶端
* @author turbo
*
* 2016年9月11日
*/
public class Main {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws Exception {
String resource = "day_8_mybatis/mybatis-config.xml"; //獲取mybatis配置檔案路徑
SqlSession sqlSession = SessionFactory.getSqlSession(resource); //通過SessionFactory工具類(此工具類為自己構造即util包中的SessionFactory)構造SqlSession
NoteMapper noteMapper = sqlSession.getMapper(NoteMapper.class);
Note note = new Note();
note.setId(1);
note.setDate(new Date());
noteMapper.insertNote(note); //插入
sqlSession.commit(); //注意需要手動提交事務
note = noteMapper.queryNote(2); //查詢
System.out.println(note.getDate());
}
}
注意在34行程式碼,需要手動提交事務,預設是關閉自動提交的,所以必須手動提交。開始沒有提交事務,無論怎麼都沒辦法插入到資料庫,後來debug單步除錯的時候發現了autoCommit=false,才想起來在以前大學的時候也遇到過這個這個問題所以一下就定位問題在哪兒了。
資料庫中只有一個note表,欄位為id型別為int,date欄位為varchar。
至此我們就完成了自定義的TypeHandler,其實MyBatis為我們提供的TypeHandler已經不少了,不過我們還是自己試驗一把,先把MyBatis學會使用,再究其原理。