• mybatis typehandler


    建立TypeHandler

    我们知道java有java的数据类型,数据库有数据库的数据类型,那么我们在往数据库中插入数据的时候是如何把java类型当做数据库类型插入数据库,在从数据库读取数据的时候又是如何把数据库类型当做java类型来处理呢?这中间必然要经过一个类型转换。在Mybatis中我们可以定义一个叫做TypeHandler类型处理器的东西,通过它可以实现Java类型跟数据库类型的相互转换。下面将就如何建立自己的TypeHandler做一个简要介绍。

    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;  
    
       
    
    }  

    现在假设我们有一个实体对象User,其中有一个属性interests是String数组类型,如下所示:

    public class User {  
    
       
    
        private int id;  
    
        private String name;  
    
        private int age;  
    
        private String[] interests;  
    
       
    
        public int getId() {  
    
           return id;  
    
        }  
    
       
    
        public void setId(int id) {  
    
           this.id = id;  
    
        }  
    
       
    
        public String getName() {  
    
           return name;  
    
        }  
    
       
    
        public void setName(String name) {  
    
           this.name = name;  
    
        }  
    
       
    
        public int getAge() {  
    
           return age;  
    
        }  
    
       
    
        public void setAge(int age) {  
    
           this.age = age;  
    
        }  
    
       
    
        public String[] getInterests() {  
    
           return interests;  
    
        }  
    
       
    
        public void setInterests(String[] interests) {  
    
           this.interests = interests;  
    
        }  
    
       
    
        @Override  
    
        public String toString() {  
    
           return "User [age=" + age + ", id=" + id + ", interests="  
    
                  + Arrays.toString(interests) + ", name=" + name + "]";  
    
        }  
    
         
    
    }  

    我们需要把它以拼接字符串的形式存到数据库中,然后在取出来的时候又把它还原为一个String数组。这个时候我们就可以给它定义一个TypeHandler专门来处理String数组类型和数据库VARCHAR类型的相互转换。在这里我们建立一个名叫StringArrayTypeHandler的TypeHandler,代码如下所示:

    package com.tiantian.mybatis.handler;  
    
       
    
    import java.sql.CallableStatement;  
    
    import java.sql.PreparedStatement;  
    
    import java.sql.ResultSet;  
    
    import java.sql.SQLException;  
    
    import java.sql.Types;  
    
       
    
    import org.apache.ibatis.type.JdbcType;  
    
    import org.apache.ibatis.type.TypeHandler;  
    
       
    
    public class StringArrayTypeHandler implements TypeHandler<String[]> {  
    
       
    
           public String[] getResult(ResultSet rs, String columnName)  
    
                         throws SQLException {  
    
                  String columnValue = rs.getString(columnName);  
    
                  return this.getStringArray(columnValue);  
    
           }  
    
       
    
           public String[] getResult(ResultSet rs, int columnIndex)  
    
                         throws SQLException {  
    
                  String columnValue = rs.getString(columnIndex);  
    
                  return this.getStringArray(columnValue);  
    
           }  
    
       
    
           public String[] getResult(CallableStatement cs, int columnIndex)  
    
                         throws SQLException {  
    
                  // TODO Auto-generated method stub  
    
                  String columnValue = cs.getString(columnIndex);  
    
                  return this.getStringArray(columnValue);  
    
           }  
    
       
    
           public void setParameter(PreparedStatement ps, int i, String[] parameter,  
    
                         JdbcType jdbcType) throws SQLException {  
    
                  if (parameter == null)  
    
                         ps.setNull(i, Types.VARCHAR);  
    
                  else {  
    
                         StringBuffer result = new StringBuffer();  
    
                         for (String value : parameter)  
    
                                result.append(value).append(",");  
    
                         result.deleteCharAt(result.length()-1);  
    
                         ps.setString(i, result.toString());  
    
                  }  
    
           }  
    
       
    
           private String[] getStringArray(String columnValue) {  
    
                  if (columnValue == null)  
    
                         return null;  
    
                  return columnValue.split(",");  
    
           }  
    
       
    
    }  

    BaseTypeHandler抽象类

           在实现自己的TypeHandler时,除了上面提到的实现最原始的接口之外,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;  
    
       
    
    }  

    我们可以看到BaseTypeHandler对TypeHandler接口的四个方法做了一个简单的选择,把null值的情况都做了一个过滤,核心的取值和设值的方法还是抽象出来了供子类来实现。使用BaseTypeHandler还有一个好处是它继承了另外一个叫做TypeReference的抽象类,通过TypeReference的getRawType()方法可以获取到当前TypeHandler所使用泛型的原始类型。这对Mybatis在注册TypeHandler的时候是非常有好处的。在没有指定javaType的情况下,Mybatis在注册TypeHandler时可以通过它来获取当前TypeHandler所使用泛型的原始类型作为要注册的TypeHandler的javaType类型,这个在讲到Mybatis注册TypeHandler的方式时将讲到。

           当通过继承BaseTypeHandler来实现自己的TypeHandler时,我们的StringArrayTypeHandler应该这样写:

    public class StringArrayTypeHandler extends BaseTypeHandler<String[]> {  
    
       
    
        @Override  
    
        public String[] getNullableResult(ResultSet rs, String columnName)  
    
               throws SQLException {  
    
           return getStringArray(rs.getString(columnName));  
    
        }  
    
       
    
        @Override  
    
        public String[] getNullableResult(ResultSet rs, int columnIndex)  
    
               throws SQLException {  
    
           return this.getStringArray(rs.getString(columnIndex));  
    
        }  
    
       
    
        @Override  
    
        public String[] getNullableResult(CallableStatement cs, int columnIndex)  
    
               throws SQLException {  
    
           return this.getStringArray(cs.getString(columnIndex));  
    
        }  
    
       
    
        @Override  
    
        public void setNonNullParameter(PreparedStatement ps, int i,  
    
               String[] parameter, JdbcType jdbcType) throws SQLException {  
    
           //由于BaseTypeHandler中已经把parameter为null的情况做了处理,所以这里我们就不用再判断parameter是否为空了,直接用就可以了  
    
           StringBuffer result = new StringBuffer();  
    
           for (String value : parameter)  
    
               result.append(value).append(",");  
    
           result.deleteCharAt(result.length()-1);  
    
           ps.setString(i, result.toString());  
    
        }  
    
         
    
        private String[] getStringArray(String columnValue) {  
    
           if (columnValue == null)  
    
               return null;  
    
           return columnValue.split(",");  
    
        }  
    
    }

    注册TypeHandler

           建立了自己的TypeHandler之后就需要把它注册到Mybatis的配置文件中,让Mybatis能够识别并使用它。注册TypeHandler主要有两种方式,一种是通过在Mybatis配置文件中定义typeHandlers元素的子元素typeHandler来注册;另一种是通过在Mybatis配置文件中定义typeHandlers元素的子元素package来注册。使用typeHandler子元素注册时一次只能注册一个TypeHandler,而使用package子元素注册时,Mybatis会把指定包里面的所有TypeHandler都注册为TypeHandler。使用typeHandler子元素注册时我们需要通过它的handler属性来指明当前要注册的TypeHandler的全名称,这个属性是必须要的。另外还有两个附加属性可以指定,一个是javaType,用以指定对应的java类型;另一个是jdbcType,用以指定对应的jdbc类型。使用package子元素注册时需要我们通过它的name属性来指定要扫描的包,如果这个时候我们也需要指定对应TypeHandler的javaType和jdbcType的话就需要我们在TypeHandler类上使用注解来定义了。Mybatis注册TypeHandler最基本的方式就是建立一个javaType、jdbcType和TypeHandler的对应关系。在使用typeHandler子元素进行注册的时候,有三种类型的注册方式:

    1.如果我们指定了javaType和jdbcType,那么Mybatis会注册一个对应javaType和jdbcType的TypeHandler。

    2.如果我们只指定了javaType属性,那么这个时候又分两种情况:

    (1)如果我们通过注解的形式在TypeHandler类上用@MappedJdbcTypes指定了对应的jdbcType,那么Mybatis会一一注册指定的javaType、jdbcType和TypeHandler的组合,也包括使用这种形式指定了jdbcType为null的情况。现假设我们有如下这样一个StringArrayTypeHandler:

    @MappedJdbcTypes({JdbcType.VARCHAR})  
    
    public class StringArrayTypeHandler implements TypeHandler<String[]> {  
    
        //..中间的实现代码省略了  
    
        //..  
    
    }  

    然后我们在Mybatis的配置文件中这样注册它:

    <typeHandlers>  
    
       <typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler" javaType="[Ljava.lang.String;"/>  
    
    </typeHandlers>  

    则Mybatis在实际注册的时候是以javaType为String数组,jdbcType为VARCHAR来注册StringArrayTypeHandler的。

  • 相关阅读:
    linux execl()函数 关于execl()函数族的用法不在赘述,
    mysql日期格式化
    JavaScript超越了Java,c,python等等成为Stack Overflow上最热门的
    GO语言web框架Gin之完全指南
    计算机专业四年本科的课程表是什么样的?
    文化常识大全201901 普通老百姓交的朋友谓“布衣之交”
    JavaScript超越了Java,c,python等等成为Stack Overflow上最热门的
    linux bash吧,还有啥Bourne Again Shell
    TCP、UDP服务器模型 在网络程序里面,通常都是一
    SpringMVC常见面试题总结(超详细回答)
  • 原文地址:https://www.cnblogs.com/Dhouse/p/5977039.html
Copyright © 2020-2023  润新知