`
tkiicpp
  • 浏览: 83208 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

ibatis 支持枚举类型

阅读更多

ibatis 支持枚举类型

很多应用中,数据库表结构都会存在一些状态字段。在关系性数据库中,一般会用VARCHAR类型。使用ibatis的应用,传统做法,往往会使用 String的属性,与之对应。
例如一张member表,结构设计如下:

其中status为状态字段。

ibatis中,使用class MemberPO 与之mapping,设计往往如下:
<!-- <br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->public   class  MemberPO  implements  Serializable {
    
private  Integer id;
    
private  String loginId;
    
private  String password;
    
private  String name;
    
private  String profile;
    
private  Date gmtCreated;
    
private  Date gmtModified;
    
private  String status;

   
// getter/setters

缺点:
1)不直观,没人会知道status具体有哪些值。在缺乏文档,并且历史悠久的系统中,只能使用“select distinct(status) from member”,才能得到想要的数据。如果是在千万级数据中,代价太大了;
2)类型不安全,如果有人不小心拼写错误,将会导致错误状态。假设上面列子中,status只允许ENABLED/DISABLED,如果一不小 心,memberPO.setStatus("ENABLEDD"),那么将会造成脏数据。

既然jdk5之后,引入了enum,是否可以让ibatis支持enum类型呢?事实上,最新的ibatis版本,已经支持enum类型(本文使用的是 2.3.4.726版本--mvn repsitory上最新的版本)。
以上代码可以修改成:
1)Status类:
<!-- <br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->public   enum  Status {

    
/**  enabled  */
    ENABLED,

    
/**  disabled  */
    DISABLED;

2)MemberPO类:
<!-- <br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->public   class  MemberPO  implements  Serializable {
    
private  Integer id;
    
private  String loginId;
    
private  String password;
    
private  String name;
    
private  String profile;
    
private  Date gmtCreated;
    
private  Date gmtModified;
    
private  Status status;

    
// getter/setters

除此之外,其他均无需改动。
为什么呢?ibatis如何知道VARCHAR/Enum的mapping呢?
看过ibatis源码的同学,知道,ibatis是通过jdbcType/javaType得到对应的TypeHandler做mapping处理的。 ibatis有基本类型的TypeHandler,比如StringTypeHandler,IntegerTypeHandler等等。在最新版本中, 为了支持enum,增加了一个EnumTypeHandler。

并且在TypeHandlerFactory中,加了对enum类型的判断,请看:
<!-- <br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->public  TypeHandler getTypeHandler(Class type, String jdbcType) {
    Map jdbcHandlerMap 
=  (Map) typeHandlerMap.get(type);
    TypeHandler handler 
=   null ;
    
if  (jdbcHandlerMap  !=   null ) {
      handler 
=  (TypeHandler) jdbcHandlerMap.get(jdbcType);
      
if  (handler  ==   null ) {
        handler 
=  (TypeHandler) jdbcHandlerMap.get( null );
      }
    }
    
if  (handler  ==   null   &&  type  !=   null   &&  Enum. class .isAssignableFrom(type)) {
      handler 
=   new  EnumTypeHandler(type);
    }
    
return  handler;
  }
ibatis使用了取巧的方法,当取不到基本类型的handler时候,判断javaType是否是Enum类型--Enum. class .isAssignableFrom(type), 如果是,则使用 EnumTypeHandler进行mapping处理。

为什么说它取巧,原因是早期ibatis设计过程中,自定义的接口无法得到具体的java class type。故早期的ibatis中,要实现对enmu的支持,非常苦难。而新版本中,为了达到这个功能,作者直接修改了 TypeHandlerFactory的实现,打了一个补丁,如下:
<!-- <br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->if  (handler  ==   null   &&  type  !=   null   &&  Enum. class .isAssignableFrom(type)) {
      handler 
=   new  EnumTypeHandler(type);
}
这个设计有悖于和早前的设计思想。早期,TypeHandler都是通过public void register(Class type, String jdbcType, TypeHandler handler)方式事先注册到factory中的,而这次,是在运行期,通过new方法动态得到EnumTypeHandler。
当然,新版本ibatis能支持enum,已经是一件开心的事情了。

Status枚举类除了描述状态,就足够了吗?回想起很多应用,我是做web开发的,在view层(velocity,jsp,等),见多了类似这样的代 码:
<!-- <br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->#if($member.getStatus()==Status.ENABLED) 开通#elseif($member.getStatus()==Status.DISABLED)关闭#end

< select >
  
< option  value ="ENABLED"  #if($member.getStatus() ==Status.ENABLED)  selected ="selected" #end  > 开通 </ option >
  
< option  value ="DISABLED"  #if($member.getStatus() ==Status.DISABLED)  selected ="selected" #end  > 关闭 </ option >
</ select >

web层需要多少个页面,就需要维护多少份这样的代码;以后每添加/删除一种状态,多个地方都需要修改,还要担心逻辑不一致。

而事实上,关于状态的信息描述,按照职责分,就应该由枚举类来维护:
1)制定一个接口,EnumDescription.java
<!-- <br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->public   interface  EnumDescription {

    
public  String getDescription();

}
2)写一个ResourceBundleUtil.java,通过Properties文件得到描述信息:
<!-- <br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->public   class  ResourceBundleUtil {

    
private  ResourceBundle resourceBundle;

    
public  ResourceBundleUtil(String resource) {
    
this .resourceBundle  =  ResourceBundle.getBundle(resource);
    }

    
public  ResourceBundleUtil(String resource, Locale locale) {
    
this .resourceBundle  =  ResourceBundle.getBundle(resource, locale);
    }

    
public  String getProperty(String key) {
    
return  resourceBundle.getString(key);
    }

}
3)Status等枚举类实现EnumDescription:
<!-- <br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->public   enum  Status  implements  EnumDescription {

    
/**  enabled  */
    ENABLED,

    
/**  disabled  */
    DISABLED;

    
private   static  ResourceBundleUtil util  =   new  ResourceBundleUtil(Status. class .getName());

    
public  String getDescription() {
       
return  util.getProperty( this .toString());
    }

}

这样,有什么好处:
1)通过Properties文件,支持国际化。
2)描述信息统一由自己来维护,方便维护,并且显示层逻辑简化,如:
<!-- <br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->$member.getStatus().getDescription()

< select >
  #foreach($status in $Status.values())
    
< option  value ="$status"  #if($member.getStatus() ==$status)selected="selected"#end  > $status.getDescription() </ option >
  #end
</ select >

##############################################################################
那么使用老版本ibatis的客户怎么办呢?就像我们公司使用ibatis 2.3.0,难道只能眼馋着?解决方案:
1)升级到最新版本。 :)
2)ibatis提供了TypeHandler/TypeHandlerCallback接口,针对每种枚举类型,写相应的TypeHandler /TypeHandlerCallback的接口实现即可--工作量大,重复的劳动力。
主要是早期ibatis TypeHandler无法得到javaType类型,无法从jdbc value转成对应的枚举。在我看来,TypeHandler是作mapping用的,它至少有权知道javaType。
3)实现伪枚举类型(允许继承)来实现状态类型安全,而抛弃jdk5的方式--不方便日后升级。


不知道大家是否还有更好的方案?

本文涉及演示代码如下:
演示代码
workspace file encoding:utf-8
build tool: maven
repository:spring/2.5.5;ibatis/2.3.4.726

posted @ 2008-11-05 23:08 stone2083 阅读(1405) | 评论 (0)编 辑  收藏

 

=======================另外一篇============================

  ibatis中使用Enum 

Refer: http://opensource.atlassian.com/confluence/oss/display/IBATIS/How+do+I+use+a+Custom+Type+Handler+with+complex+property+or+Type+Safe+Enumeration

这个实现给每个Enum对象一个数字值,所以可以把这个数字值存入到数据库中,或者从数据库中取出这个数字值然后恢复这个Enum对象。

1、定义一个所以 Enum的interface

package com.demo;

public interface HasValueInterface {
    public Integer getValue();
}

2、定义一个Enum类型

package com.demo;

public enum Status implements HasValueInterface{
    ALL(0),

    GOOD(1),

    BAD(2);
   
    private final int status;

    Status(int status) {
        this.status = status;
    }

    public Integer getValue() {
        return status;
    }
}

3、定义一个抽象的typehandler

package com.demo;
 
import java.sql.SQLException;
import java.sql.Types;
 
import com.ibatis.sqlmap.client.extensions.ParameterSetter;
import com.ibatis.sqlmap.client.extensions.ResultGetter;
import com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;
import com.demo.HasValueInterface;
 
public abstract class HasValueTypeHandler implements TypeHandlerCallback {
     
    public abstract HasValueInterface[] getEnums();
 
    public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {
        if (parameter == null) {
            setter.setNull(Types.INTEGER);
        } else {
            HasValueInterface status = (HasValueInterface) parameter;
            setter.setInt(status.getValue());
        }
    }
 
    public Object getResult(ResultGetter getter) throws SQLException {

        /*千万要注意!!!

         * When calling getter.getNull before actually getting the column's value (getter.getInt(),
         * getNull() will always return true, since no value has been fetched yet.
         * The check for null has to be done after getting the value.
         */

        int value = getter.getInt();
        if (getter.wasNull())
            return null;
        
        for (HasValueInterface status : getEnums()) {
            if (status.getValue().equals(value)) {
                return status;
            }
        }
        throw new UnsupportedOperationException("No such status");
    }
 
    public Object valueOf(String s) {
        return s;
    }
 
}

4、给Status实现一个typehandler

package com.demo;
 
import com.demo.Status;
import com.demo.HasValueInterface;
 
public class StatusTypeHandler extends HasValueTypeHandler {
     
    public HasValueInterface[] getEnums() {
        return Status.values();
    }
 
}

 

分享到:
评论

相关推荐

    1.springbood+mybatis项目demo2.mybatis自定义枚举类型的转换器以及各种使用场景

    MyBatis默认并不支持直接将数据库中的字符串值映射到枚举类型上,因此我们需要自定义一个TypeHandler。 以下是一个简单的自定义枚举类型转换器示例: ```java import org.apache.ibatis.type.BaseTypeHandler; ...

    ibatis 配置文件详解

    4. **typeHandlers**:类型处理器,用于处理不同类型的数据,如日期类型、枚举类型等。 5. **environment**:环境配置,包括事务管理和数据源的配置。 6. **mappers**:映射器的配置,用于指定SQL映射文件的位置,...

    iBatis入门Helloworld

    3. `sex`: 性别,可设置为枚举类型(如CHAR)或字符串,例如'M'或'F'。 4. `age`: 年龄,整型,表示用户年龄。 接下来,我们将逐步了解iBatis的核心组件和工作流程: 1. **配置文件(mybatis-config.xml)**:这是...

    IBatisNet.Common.1.6.2、IBatis.DataAccess.1.9.2、IBatis.DataMapper.1.6.2

    它提供了对XML配置文件处理、数据类型转换、日志记录等基础设施的支持。例如,它可能包含用于数据库操作的抽象类,便于数据访问层的开发,以及对SQL语句的动态构建和执行。 2. **IBatis.DataAccess**: 这部分是...

    ibatis学习资料

    - **特定类型转换器**:定义特定类型的转换规则,例如日期类型或枚举类型的处理。 - **实体映射**:引入具体的映射文件,每个文件对应一类实体的映射关系。 #### SqlMap标签详解 - **类别名**:为Java类型定义别名...

    Ibatis学习笔记,文档,资源6合1

    在实际项目中,Ibatis支持事务管理,可以通过SqlSession的beginTransaction、commit和rollback方法来控制事务的提交和回滚。同时,Ibatis还支持连接池,如Druid或C3P0,以提高数据库连接的复用率,减少系统资源的...

    MyBatis(ibatis)学习文档.docx

    MyBatis的枚举类型处理器,用于处理Java枚举类型与数据库表之间的映射关系。 object, MyBatis的对象是指Java对象,用于表示业务逻辑中的实体对象。MyBatis的对象可以与数据库表之间建立映射关系,从而实现数据的...

    bbs.rar_BBS java_bbs_ibatis tomcat 5.5_java BBS_struts1.2

    1. **JDK 1.5**:这是Java语言的一个重要版本,引入了诸如自动装箱/拆箱、枚举类型、可变参数、foreach循环、泛型、注解等新特性。JDK 1.5的使用意味着代码将利用这些功能,提高开发效率和代码可读性。 2. **Struts...

    javasqlTypes数据库字段类型java数据类型的对应关系

    - `BOOLEAN` 对应于Java的`boolean`或`Boolean`,虽然不是所有数据库都支持`BOOLEAN`类型。 4. **iBATIS Data Mapper** - iBATIS 是一个流行的持久层框架,它允许将SQL语句直接写在XML配置文件中,同时自动处理...

    淘宝实习笔记--webx学习之旅

    枚举类型、简单数据类型、类及其属性和关联都可以按照特定模式映射到数据库表。 5. **类映射**:每个类对应一个数据库表,属性(单值、多值)和继承关系可以通过不同的映射策略转换。例如,单值属性直接映射为表列...

    Spring2.5-Reference_zh_CN.txt

    - **Java 5支持**:Spring 2.5全面支持Java 5,利用其新特性如泛型、枚举等提高了开发效率。 #### 九、其他相关知识点 - **依赖淘汰**:Spring提供了一套机制来管理不再推荐使用的API,帮助开发者进行平滑迁移。 - ...

    spring源码

    6. 数据访问:Spring2.5对数据访问层进行了优化,提供了更好的JDBC抽象,包括对命名参数的支持,以及对ORM框架如Hibernate和iBatis的进一步集成。 7. 集成其他技术:Spring2.5加强了与其他技术的集成,比如支持...

    Java技术体系图.pdf

    - **枚举类**:Java中的枚举类型,用于定义一组固定的值,常用于表示常量。 - **可变参数**:允许方法接受不定数量的参数。 - **可变返回类型**:Java 8引入的Optional类,用于处理可能为空的返回值,减少空指针...

    SSI框架+权限(Oracle)

    **JDK1.5**,也称为Java SE 5.0,引入了许多重要的新特性,例如泛型、枚举、注解(Annotation)和类型安全的集合。这些特性极大地提升了代码的可读性、类型安全性及维护性,对现代Java开发有着深远影响。 在实际...

    JAVA技术体系.pdf

    - **具体框架**:Struts1和2提供表现层支持,Spring提供业务层服务,Hibernate和iBATIS处理数据持久化。 4. **Java高级软件工程师**: - **开源技术与框架**:如工作流引擎、规则引擎、搜索引擎、缓存、任务调度...

    JAVA技术体系

    - **枚举类**:学习如何定义和使用枚举类型。 - **可变参数与返回类型**:了解可变参数列表和可变返回类型的功能。 - **增强循环**:使用增强for循环简化遍历过程。 - **静态导入**:简化代码中的静态方法调用。 ##...

    Java软件开发工程师面试题宝典.pdf

    3. **Spring MVC与Struts MVC**:两者都是用于构建MVC架构的应用框架,Spring MVC更现代且灵活,支持依赖注入,而Struts则相对老旧但社区支持广泛。了解它们的差异有助于选择合适的开发工具。 4. **Spring和iBatis*...

    Java软件开发工程师面试题宝典.doc

    与Struts相比,Spring MVC更轻量级,支持依赖注入(DI)和面向切面编程(AOP)。 4. **Spring和iBatis**:Spring是一个全面的企业级应用框架,包括IoC(Inversion of Control)和AOP。iBatis是一个SQL映射框架,...

Global site tag (gtag.js) - Google Analytics