MyBatis配置文件(四)--typeHandlers


typeHandlers又叫類型處理器,就像在JDBC中,我們在PreparedStatement中設置預編譯sql所需的參數或執行sql后根據結果集ResultSet對象獲取得到的數據時,需要將數據庫中的類型和java中字段的類型進行轉換一樣,在MyBatis中使用typeHandler來實現。所以說白了,typeHandlers就是用來完成javaType和jdbcType之間的轉換。舉個比較簡單的例子,我創建一個博客表,表中的創建時間和修改時間用VARCHAR類型,但是在我的POJO對象中,創建時間和修改時間的類型是Date,這樣我在向數據庫插入數據時,需要將日期類型轉化成VARCHAR,而從數據庫中查詢出的結果中,又需要將VARCHAR類型轉換成Date.在MyBatis中,使用typeHandlers配置來實現這個轉換過程。和別名一樣,MyBatis中的類型處理器也存在系統定義的和自定義兩種,MyBatis會根據javaType和jdbcType來決定采用哪個typeHandler來處理這些轉換規則,而且系統定義的能滿足大部分需求,可以說是非常好用,用戶只需要自定義一些特有的轉換規則,如枚舉類型。下面分別介紹這兩種typeHandler。

一、系統定義的typeHandler

同上一節的typeAliases,typeHandler也能通過Configuration對象獲取到,如下:

 1 /**
 2      * 獲取類型處理器
 3      */
 4     public static void getTypeHandlers() {
 5         SqlSession sqlSession = getSqlSession();
 6         TypeHandlerRegistry typeHandlerRegistry = sqlSession.getConfiguration().getTypeHandlerRegistry();
 7         Collection<TypeHandler<?>> handlers =  typeHandlerRegistry.getTypeHandlers();
 8         System.out.println(handlers.size());
 9         for (TypeHandler<?> typeHandler : handlers) {
10             System.out.println(typeHandler.getClass().getName());
11         }
12     }

執行結果如下:

 1 39
 2 org.apache.ibatis.type.SqlDateTypeHandler
 3 org.apache.ibatis.type.ClobReaderTypeHandler
 4 org.apache.ibatis.type.LocalTimeTypeHandler
 5 org.apache.ibatis.type.YearMonthTypeHandler
 6 org.apache.ibatis.type.NStringTypeHandler
 7 org.apache.ibatis.type.LocalDateTypeHandler
 8 org.apache.ibatis.type.BigIntegerTypeHandler
 9 org.apache.ibatis.type.OffsetDateTimeTypeHandler
10 org.apache.ibatis.type.ByteObjectArrayTypeHandler
11 org.apache.ibatis.type.ArrayTypeHandler
12 org.apache.ibatis.type.BigDecimalTypeHandler
13 org.apache.ibatis.type.UnknownTypeHandler
14 org.apache.ibatis.type.OffsetTimeTypeHandler
15 org.apache.ibatis.type.ByteArrayTypeHandler
16 org.apache.ibatis.type.DateOnlyTypeHandler
17 org.apache.ibatis.type.JapaneseDateTypeHandler
18 org.apache.ibatis.type.TimeOnlyTypeHandler
19 org.apache.ibatis.type.NClobTypeHandler
20 org.apache.ibatis.type.BooleanTypeHandler
21 org.apache.ibatis.type.BlobTypeHandler
22 org.apache.ibatis.type.YearTypeHandler
23 org.apache.ibatis.type.BlobByteObjectArrayTypeHandler
24 org.apache.ibatis.type.MonthTypeHandler
25 org.apache.ibatis.type.FloatTypeHandler
26 org.apache.ibatis.type.DateTypeHandler
27 org.apache.ibatis.type.ClobTypeHandler
28 org.apache.ibatis.type.BlobInputStreamTypeHandler
29 org.apache.ibatis.type.ByteTypeHandler
30 org.apache.ibatis.type.SqlTimestampTypeHandler
31 org.apache.ibatis.type.ZonedDateTimeTypeHandler
32 org.apache.ibatis.type.IntegerTypeHandler
33 org.apache.ibatis.type.LocalDateTimeTypeHandler
34 org.apache.ibatis.type.CharacterTypeHandler
35 org.apache.ibatis.type.SqlTimeTypeHandler36 org.apache.ibatis.type.DoubleTypeHandler
37 org.apache.ibatis.type.ShortTypeHandler
38 org.apache.ibatis.type.LongTypeHandler
39 org.apache.ibatis.type.InstantTypeHandler
40 org.apache.ibatis.type.StringTypeHandler

從結果來看,系統總過定義了39個類型處理器。

現在選擇其中的StringTypeHandler來進行分析,看看其源代碼

1、獲取StringTypeHandler的源代碼

 1 /**
 2  *    Copyright 2009-2015 the original author or authors.
 3  *
 4  *    Licensed under the Apache License, Version 2.0 (the "License");
 5  *    you may not use this file except in compliance with the License.
 6  *    You may obtain a copy of the License at
 7  *
 8  *       http://www.apache.org/licenses/LICENSE-2.0
 9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 package org.apache.ibatis.type;
17 
18 import java.sql.CallableStatement;
19 import java.sql.PreparedStatement;
20 import java.sql.ResultSet;
21 import java.sql.SQLException;
22 
23 /**
24  * @author Clinton Begin
25  */
26 public class StringTypeHandler extends BaseTypeHandler<String> {
27 
28   @Override
29   public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
30       throws SQLException {
31     ps.setString(i, parameter);
32   }
33 
34   @Override
35   public String getNullableResult(ResultSet rs, String columnName)
36       throws SQLException {
37     return rs.getString(columnName);
38   }
39 
40   @Override
41   public String getNullableResult(ResultSet rs, int columnIndex)
42       throws SQLException {
43     return rs.getString(columnIndex);
44   }
45 
46   @Override
47   public String getNullableResult(CallableStatement cs, int columnIndex)
48       throws SQLException {
49     return cs.getString(columnIndex);
50   }
51 }

從上述代碼可以看出它繼承了一個叫做BaseTypeHandler<String>的類,這個類的范型是String,即javaType,幾個方法如下:

1⃣️setNonNullParameter:這個方法是用來將javaType轉換成jdbcTpe

2⃣️getNullableResult:這個方法用來將從結果集根據列名稱獲取到的數據的jdbcType轉換成javaType

3⃣️getNullableResult:這個方法用來將從結果集根據列索引獲取到的數據的jdbcType轉換成javaType

4⃣️getNullableResult:這個方法用在存儲過程中

其實主要就是完成不同類型之間的轉換,下面來看一下它所繼承的BaseTypeHandler<String>類

2、BaseTypeHandler<String>類源碼

  1 /**
  2  *    Copyright 2009-2015 the original author or authors.
  3  *
  4  *    Licensed under the Apache License, Version 2.0 (the "License");
  5  *    you may not use this file except in compliance with the License.
  6  *    You may obtain a copy of the License at
  7  *
  8  *       http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  *    Unless required by applicable law or agreed to in writing, software
 11  *    distributed under the License is distributed on an "AS IS" BASIS,
 12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  *    See the License for the specific language governing permissions and
 14  *    limitations under the License.
 15  */
 16 package org.apache.ibatis.type;
 17 
 18 import java.sql.CallableStatement;
 19 import java.sql.PreparedStatement;
 20 import java.sql.ResultSet;
 21 import java.sql.SQLException;
 22 
 23 import org.apache.ibatis.executor.result.ResultMapException;
 24 import org.apache.ibatis.session.Configuration;
 25 
 26 /**
 27  * @author Clinton Begin
 28  * @author Simone Tripodi
 29  */
 30 public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
 31 
 32   protected Configuration configuration;
 33 
 34   public void setConfiguration(Configuration c) {
 35     this.configuration = c;
 36   }
 37 
 38   @Override
 39   public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
 40     if (parameter == null) {
 41       if (jdbcType == null) {
 42         throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
 43       }
 44       try {
 45         ps.setNull(i, jdbcType.TYPE_CODE);
 46       } catch (SQLException e) {
 47         throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
 48                 "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
 49                 "Cause: " + e, e);
 50       }
 51     } else {
 52       try {
 53         setNonNullParameter(ps, i, parameter, jdbcType);
 54       } catch (Exception e) {
 55         throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
 56                 "Try setting a different JdbcType for this parameter or a different configuration property. " +
 57                 "Cause: " + e, e);
 58       }
 59     }
 60   }
 61 
 62   @Override
 63   public T getResult(ResultSet rs, String columnName) throws SQLException {
 64     T result;
 65     try {
 66       result = getNullableResult(rs, columnName);
 67     } catch (Exception e) {
 68       throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set.  Cause: " + e, e);
 69     }
 70     if (rs.wasNull()) {
 71       return null;
 72     } else {
 73       return result;
 74     }
 75   }
 76 
 77   @Override
 78   public T getResult(ResultSet rs, int columnIndex) throws SQLException {
 79     T result;
 80     try {
 81       result = getNullableResult(rs, columnIndex);
 82     } catch (Exception e) {
 83       throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from result set.  Cause: " + e, e);
 84     }
 85     if (rs.wasNull()) {
 86       return null;
 87     } else {
 88       return result;
 89     }
 90   }
 91 
 92   @Override
 93   public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
 94     T result;
 95     try {
 96       result = getNullableResult(cs, columnIndex);
 97     } catch (Exception e) {
 98       throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from callable statement.  Cause: " + e, e);
 99     }
100     if (cs.wasNull()) {
101       return null;
102     } else {
103       return result;
104     }
105   }
106 
107   public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
108 
109   public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
110 
111   public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;
112 
113   public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;
114 
115 }

此類是一個抽象類,重寫了四個接口之外,還有四個抽象方法,也就是在StringTypeHandler中實現的接口,那么重寫的四個接口是什么呢?我們去看一下它所實現的TypeHandler<T>接口

3、接口TypeHandler<T>源代碼

 1 /**
 2  *    Copyright 2009-2015 the original author or authors.
 3  *
 4  *    Licensed under the Apache License, Version 2.0 (the "License");
 5  *    you may not use this file except in compliance with the License.
 6  *    You may obtain a copy of the License at
 7  *
 8  *       http://www.apache.org/licenses/LICENSE-2.0
 9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 package org.apache.ibatis.type;
17 
18 import java.sql.CallableStatement;
19 import java.sql.PreparedStatement;
20 import java.sql.ResultSet;
21 import java.sql.SQLException;
22 
23 /**
24  * @author Clinton Begin
25  */
26 public interface TypeHandler<T> {
27 
28   void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
29 
30   T getResult(ResultSet rs, String columnName) throws SQLException;
31 
32   T getResult(ResultSet rs, int columnIndex) throws SQLException;
33 
34   T getResult(CallableStatement cs, int columnIndex) throws SQLException;
35 
36 }

這里也定義了四個接口,一個set方法用來將javaType轉為jdbcType,三個get方法用來將jdbcType轉為javaType,而在BaseTypeHandler中實現的就是這四個方法。

所以當我們自定義自己的typeHandler時有兩種方法:

第一種:繼承BaseTypeHandler類

第二種:實現TypeHandler接口

二、自定義typeHandler

我使用實現TypeHandler接口的方式創建一個MyDateHandler,用來完成javaType中的Date類型與jdbcType中的varchar類型之間的轉化。

 

創建MyDateHandler

 1 package com.daily.handler;
 2 
 3 import java.sql.CallableStatement;
 4 import java.sql.PreparedStatement;
 5 import java.sql.ResultSet;
 6 import java.sql.SQLException;
 7 import java.text.SimpleDateFormat;
 8 import java.util.Date;
 9 
10 import org.apache.ibatis.type.DateTypeHandler;
11 import org.apache.ibatis.type.JdbcType;
12 import org.apache.ibatis.type.TypeHandler;
13 import org.postgresql.jdbc2.optional.SimpleDataSource;
14 
15 /**
16  * 自定義一個TypeHandler用來將javatype的日期類型和jdbctype的VARCHAR進行轉換
17  */
18 public class MyDateHandler implements TypeHandler<Date> {
19     @Override
20     // 設置sql中指定索引的參數,即將javaType轉化為jdbcType
21     public void setParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {
22         //設置數據存儲到數據庫中的格式
23         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
24         ps.setString(i, sdf.format(parameter));
25     }
26 
27     @Override
28     // 根據列名稱從結果集獲取值,並將jdbcType轉換成javaType
29     public Date getResult(ResultSet rs, String columnName) throws SQLException {
30         String columnValue = rs.getString(columnName);
31         if (null != columnValue) {
32             return new Date(Long.valueOf(columnValue));
33         }
34         return null;
35     }
36 
37     @Override
38     // 根據列名稱從結果集獲取值,並將jdbcType轉換成javaType
39     public Date getResult(ResultSet rs, int columnIndex) throws SQLException {
40         String columnValue = rs.getString(columnIndex);
41         if (null != columnValue) {
42             return new Date(Long.valueOf(columnValue));
43         }
44         return null;
45     }
46 
47     @Override
48     public Date getResult(CallableStatement cs, int columnIndex) throws SQLException {
49         String columnValue = cs.getString(columnIndex);
50         if (null != columnValue) {
51             return new Date(Long.valueOf(columnValue));
52         }
53         return null;
54     }
55 
56 }

下面用一個例子來說明這個handler的使用。

1⃣️創建一個blog表,表中的時間用varchar類型

2⃣️創建POJO對象,將create_time和modify_time的類型定義為Date

 1 package com.daily.pojo;
 2 
 3 import java.util.Date;
 4 
 5 public class Blog {
 6     private Integer blogId;
 7 
 8     private String blogTitle;
 9 
10     private String blogContent;
11 
12     private Date createTime;
13 
14     private Date modifyTime;
15 
16     public Integer getBlogId() {
17         return blogId;
18     }
19 
20     public void setBlogId(Integer blogId) {
21         this.blogId = blogId;
22     }
23 
24     public String getBlogTitle() {
25         return blogTitle;
26     }
27 
28     public void setBlogTitle(String blogTitle) {
29         this.blogTitle = blogTitle == null ? null : blogTitle.trim();
30     }
31 
32     public String getBlogContent() {
33         return blogContent;
34     }
35 
36     public void setBlogContent(String blogContent) {
37         this.blogContent = blogContent == null ? null : blogContent.trim();
38     }
39 
40     public Date getCreateTime() {
41         return createTime;
42     }
43 
44     public void setCreateTime(Date createTime) {
45         this.createTime = createTime == null ? null : createTime;
46     }
47 
48     public Date getModifyTime() {
49         return modifyTime;
50     }
51 
52     public void setModifyTime(Date modifyTime) {
53         this.modifyTime = modifyTime == null ? null : modifyTime;
54     }
55 }

3⃣️在mybatis-config.xml文件中注冊剛才自定義的MyDateHandler

1 <!--類型處理器 -->
2     <typeHandlers>
3         <!-- 注冊自定義handler,說明它作用的jdbcType和javaType -->
4         <typeHandler jdbcType="VARCHAR" javaType="date" handler="com.daily.handler.MyDateHandler" />
5     </typeHandlers>

4⃣️在BlogMapper.xml文件中使用

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 3 <mapper namespace="com.daily.dao.BlogMapper">
 4     <resultMap id="BaseResultMap" type="com.daily.pojo.Blog">
 5         <id column="blog_id" jdbcType="INTEGER" property="blogId" />
 6         <result column="blog_title" jdbcType="VARCHAR" property="blogTitle" />
 7         <result column="blog_content" jdbcType="VARCHAR" property="blogContent" />
 8         <!-- 不使用MyDateHandler-->
 9         <result column="create_time" jdbcType="VARCHAR" property="createTime" />
10         <!-- 使用MyDateHandler-->
11         <result column="modify_time" typeHandler="com.daily.handler.MyDateHandler" property="modifyTime" />
12     </resultMap>
13 
14     <insert id="insert" parameterType="com.daily.pojo.Blog">
15         insert into blog (blog_id, blog_title, blog_content,
16         create_time, modify_time)
17         values (#{blogId,jdbcType=INTEGER}, #{blogTitle,jdbcType=VARCHAR},
18         #{blogContent,jdbcType=VARCHAR},
19         #{createTime,jdbcType=VARCHAR}, #{modifyTime,typeHandler=com.daily.handler.MyDateHandler})
20     </insert>
21 </mapper>

5⃣️查看結果

第一種:create_time不設置typeHandler,modify_time設置typeHandler

從上面的結果來看,雖然create_time沒有設置typeHandler,但是結果跟使用了typeHandler的modify_time是一樣的,那么我把兩個都去掉呢?

第二種:兩個都不設置

可以看到結果也是一樣,下面我去掉注冊的MyDateHandler呢?

第三種:去掉MyDateHandler

從上述結果可以看到,當去掉自定義的處理器時,MyBatis會根據結果自動選擇合適的handler進行轉換。

日常開發中,一般不需要定義,使用默認的就可以,除非是像枚舉這種特殊類型就需要自己實現。

以上就是MyBatis配置文件中typeHandlers的配置內容。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM