枚举类型

iBatis的扩展组件主要有TypeHandlerCallback、CacheController、DataSourceFactory、 TransactionConfig。其中TypeHandlerCallback可以实现自定义的类型处理逻辑,以便处理非标准数据库、驱动程序和 (或)数据类型。

 

场景举例

有如下数据表,请注意字段status的类型及含义:

 

Sql代码   收藏代码
  1. CREATE DATABASE ibatis;  
  2. USE test_ibatis;  
  3. CREATE TABLE person  
  4. (  
  5.    id INT NOT NULL AUTO_INCREMENT,  
  6.    name VARCHAR(20) NOT NULL comment '姓名',  
  7.    status TINYINT NOT NULL comment '状态(1可用,-1不可用,0默认)',  
  8.    PRIMARY KEY (id)  
  9. )  
  10. ENGINE=InnoDB  
  11. DEFAULT CHARACTER SET=utf8;  

 

对应的实体模型类里将status定义为枚举型,如:

 

Java代码   收藏代码
  1. public class Person {  
  2.   
  3.     private int id;  
  4.     private String name;  
  5.     private Status status; // 注意这里  
  6.   
  7.     // getters and setters...  
  8. }  

 

注:如果将status定义为整型,虽然与表中的字段类型一致了,但在赋值时setStatus(int status),只能传数字,不易被理解,没有人会明白应该传什么值,1或-1又代表了什么含义,且易产生脏数据,比如有意无意间会传321。

 

用枚举型给status赋值,既直观又限定范围。如:

 

Java代码   收藏代码
  1. ......  
  2. Person person = new Person();  
  3. person.setName("yumin");  
  4. person.setStatus(Status.AVAILABLE);; // 注意这里  
  5. boolean result = DAO.insert(person);  
  6. .......  

 

代码里出现的枚举型Status,可以这样进行定义:

 

Java代码   收藏代码
  1. public enum Status {  
  2.     // 默认0, 可用1, 不可用-1  
  3.     DEFAULT(0), AVAILABLE(1), UNAVAILABLE(-1);  
  4. }  

 

但按上述做法执行写入操作时,发生了如下异常:

 

Java代码   收藏代码
  1. Caused by: java.sql.SQLException:   
  2. Incorrect integer value: 'AVAILABLE' for column 'status' at row 1  

 

类型不匹配,AVAILABLE用在了整型字段status。

 

异常出现的症结在于,数据表里的字段是用整型存储这没问题,但Java代码里应该用枚举型会更友好,两者间在数据类型上就存在一定的矛盾,即"javaType=enum"而"jdbcType=integer"。这时就需用到TypeHandlerCallback,通过实现该接口来处理中间的转换。

 

题外话:还有一种做法,定义Person类时,定义两个status,第一个status为整型,但取消setStatus方法,只负责在数据表存 储时用,另一个叫status2为枚举型,负责与外部交互,赋值时只能用status2,内部将status2转换为status,这样既能保证友好又能 通过转换保持类型的一致。但暂时让我们看看若通过iBatis的扩展应该如何实现。

 

实例演示

TypeHandlerCallback接口,下面是它的源代码:

 

Java代码   收藏代码
  1. public abstract interface TypeHandlerCallback {  
  2.   
  3.       public abstract void setParameter(ParameterSetter setter, Object object)   
  4. throws SQLException;  
  5.   
  6.       public abstract Object getResult(ResultGetter getter) throws SQLException;  
  7.   
  8.       public abstract Object valueOf(String string);  
  9. }  

 

接下来我们一起Step-by-step操作 ——

 

第一步:基于TypeHandlerCallback实现类

 

Java代码   收藏代码
  1. /** 
  2.  *  
  3.  */  
  4. package me.yumin.java.ibatis.handler;  
  5.   
  6. import com.ibatis.sqlmap.client.extensions.ParameterSetter;  
  7. import com.ibatis.sqlmap.client.extensions.ResultGetter;  
  8. import com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;  
  9. import java.sql.SQLException;  
  10. import java.sql.Types;  
  11. import me.yumin.java.ibatis.enumtype.Status;  
  12.   
  13. /** 
  14.  * @author yumin 
  15.  *  
  16.  */  
  17. public class StatusHandler implements TypeHandlerCallback {  
  18.   
  19.     @Override  
  20.     public void setParameter(ParameterSetter setter, Object parameter)   
  21. throws SQLException {  
  22.   
  23.         /* 
  24.          * 在sqlMap中配parameterMap的属性typeHandler 
  25.          * 和sqlMapConfig中配全局typeHandler时才会触发 
  26.          */  
  27.         if (null == parameter) {  
  28.             setter.setNull(Types.INTEGER); // 若没有传值则null,表字段不允许则异常  
  29.         } else {  
  30.             setter.setInt(((Status) parameter).getValue());  
  31.         }  
  32.     }  
  33.   
  34.     @Override  
  35.     public Object getResult(ResultGetter getter) throws SQLException {  
  36.   
  37.         /* 
  38.          * 仅在sqlMap中配置resultMap的属性typeHandler 
  39.          * 和在sqlMapConfig中配全局typeHandler才会触发 
  40.          */  
  41.         Object result = null;  
  42.   
  43.         //if (!getter.wasNull() && null != getter.getObject()) {   
  44.         // 上面有问题,修复如下.wasNull是判断前一个字段是否为null  
  45.         if (null != getter.getObject()) {  
  46.             result = getStatus(getter.getInt());  
  47.         }  
  48.   
  49.         return result;  
  50.     }  
  51.   
  52.     @Override  
  53.     public Object valueOf(String string) {  
  54.   
  55.         /* 
  56.          * 处理属性或参数为null情况 
  57.          * 入参即为配置项nullValue 
  58.          */  
  59.         Object result = null;  
  60.         int integer = 0;  
  61.   
  62.         if (null != string && 0 < string.length()) {  
  63.             try {  
  64.                 integer = Integer.parseInt(string);  
  65.             } catch (NumberFormatException e) {  
  66.                 e.printStackTrace();  
  67.             }  
  68.         }  
  69.         result = getStatus(integer);  
  70.   
  71.         return result;  
  72.     }  
  73.   
  74.     /** 
  75.      *  
  76.      * @param value 
  77.      * @return 
  78.      */  
  79.     private Object getStatus(int value) {  
  80.   
  81.         Object result = null;  
  82.   
  83.         for (Status status : Status.values()) {  
  84.             if (value == status.getValue()) {  
  85.                 result = status;  
  86.                 break;  
  87.             }  
  88.         }  
  89.   
  90.         return result;  
  91.     }  
  92.   
  93. }  
 

第二步:在sqlMap中指定属性typeHandler

所有完整的sqlMap配置文件如下:

 

Xml代码   收藏代码
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd">  
  3. <sqlMap namespace="Person">  
  4.   
  5.     <typeAlias alias="Person" type="me.yumin.java.ibatis.domain.Person" />  
  6.     <parameterMap class="Person" id="person">  
  7.         <parameter property="name" />  
  8.         <parameter property="status" typeHandler="me.yumin.java.ibatis.handler.StatusHandler" />  
  9.         <!-- 若没有使用parameterMap且未给属性指定typeHandler  
  10.              则在给SQL传值时不会调Handler中的setParameter方法 -->  
  11.     </parameterMap>  
  12.     <resultMap class="Person" id="person">  
  13.         <result property="id" column="id" />  
  14.         <result property="name" column="name" />  
  15.         <result property="status" column="status" typeHandler="me.yumin.java.ibatis.handler.StatusHandler" nullValue="0" />  
  16.         <!-- 同上若没用resultMap且未给属性指定typeHandler  
  17.              则在返回表数据时不会调Handler中的getResult方法 -->  
  18.     </resultMap>  
  19.   
  20.     <insert id="insert" parameterMap="person">  
  21.         <![CDATA[ 
  22.             INSERT INTO person (name, status) VALUES (?, ?) 
  23.         ]]>  
  24.         <selectKey keyProperty="id" resultClass="java.lang.Integer">  
  25.             SELECT LAST_INSERT_ID()  
  26.         </selectKey>  
  27.     </insert>  
  28.   
  29.     <select id="query" parameterClass="java.lang.Integer" resultMap="person">  
  30.         <![CDATA[ 
  31.             SELECT id, name, status FROM person WHERE id=#id# 
  32.         ]]>  
  33.     </select>  
  34.   
  35. </sqlMap>  
 

主要处理过程是,因在sqlMap中给属性配置了typeHandler,所以在写或改数据前,先调用setParameter方法,再生成SQL,这时已将枚举型转换为整型;在读取数据后映射类时,会调用getResult方法,将整型转换为枚举型。

 

完整的示例请见附件,导入到IDE即可运行,IBatisTest是测试用例。

 

上述实例满足了一开始的需求,即Java类中将属性定义为枚举型,在存储时仍用整型,既友好直观地让使用者给类赋值又满足了存储需要。应用场景还有 很多,如:对所有字符串型数据先压缩再存储,则可在sqlMapConfig中定义全局typeHandler, 凡"javaType=java.lang.String"时,都通过该扩展在写之前先压缩,读取后先解压再返回。


若将Handler定义在sqlMapConfig,则该typeHandler是全局可用的。

 

全局typeHandler定义:

 

Xml代码   收藏代码
  1. <sqlMapConfig>  
  2.     ......  
  3.     <typeHandler javaType="package.Type" callback="package.Handler" />  
  4.     ......  
  5. </sqlMapConfig> 

你可能感兴趣的:(枚举类型)