zoukankan      html  css  js  c++  java
  • mybatis typeHandler类型转换器

     typeHandler类型转换器  

      在JDBC中,需要在PreparedStatement对象中设置那些已经预编译过的SQL语句的参数。执行SQL后,会通过ResultSet对象获取得到数据库的数据,而这些MyBatis是根据数据的类型通过typeHandler来实现的。在typeHandler中,分为jdbcType和javaType,其中jdbcType用于定义数据库类型,而javaType用于定义Java类型,那么typeHandler的作用就是承担jdbcType和javaType之间的相互转换,如图4-1所示。在很多情况下我们并不需要去配置typeHandler、jdbcType、javaType,因为MyBatis会探测应该使用什么类型的typeHandler进行处理,但是有些场景无法探测到。对于那些需要使用自定义枚举的场景,或者数据库使用特殊数据类型的场景,可以使用自定义的typeHandler去处理类型之间的转换问题。

      和别名一样,在MyBatis中存在系统定义typeHandler和自定义typeHandler。MyBatis会根据javaType和数据库的jdbcType来决定采用哪个typeHandler处理这些转换规则。系统提供的typeHandler能覆盖大部分场景的要求,但是有些情况下是不够的,比如我们有特殊的转换规则,枚举类就是这样。

    系统定义的typeHandler

      MyBatis内部定义了许多有用的typeHandler,如表所示。

      

      这些就是MyBatis系统已经创建好的typeHandler。在大部分的情况下无须显式地声明jdbcType和javaType,或者用typeHandler去指定对应的typeHandler来实现数据类型转换,因为MyBatis系统会自己探测。有时候需要修改一些转换规则,比如枚举类往往需要自己去编写规则。

      在MyBatis中typeHandler都要实现接口org.apache.ibatis.type.TypeHandler,首先让我们先看看这个接口的定义,如代码清单4-9所示。
      代码清单4-9:TypeHandler.java

    public interface TypeHandler<T> {
        //T是泛型,专指javaType,比如我们需要String的时候,那么实现类可以写为implements TypeHandler<String>。
        //setParameter方法,是使用typeHandler通过PreparedStatement对象进行设置SQL参数的时候使用的具体方法,其中i是参数在SQL的下标,parameter是参数,jdbcType是数据库类型。
        void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
    
        //其中有3个getResult的方法,它的作用是从JDBC结果集中获取数据进行转换,要么使用列名(columnName)要么使用下标(columnIndex)获取数据库的数据,其中最后一个getResult方法是存储过程专用的。
        T getResult(ResultSet rs, String columnName) throws SQLException;
    
        T getResult(ResultSet rs, int columnIndex) throws SQLException;
    
        T getResult(CallableStatement var1, int columnIndex) throws SQLException;
    }

      在编写typeHandler前,先来研究一下MyBatis系统的typeHandler是如何实现的,所以有必要先研究一下MyBatis系统的typeHandler。如果读者打开源码,就可以发现它们都继承了org.apache.ibatis.type.BaseTypeHandler,如代码清单4-10所示。
      代码清单4-10:BaseTypeHandler.java

    /**
     * BaseTypeHandler是个抽象类,需要子类去实现其定义的4个抽象方法,而它本身实现了typeHandler接口的4个方法。
     * @param <T>
     */
    public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
        protected Configuration configuration;
    
        public BaseTypeHandler() {
        }
    
        public void setConfiguration(Configuration c) {
            this.configuration = c;
        }
    
        //setParameter方法,当参数parameter和jdbcType同时为空时,MyBatis将抛出异常。如果能明确jdbcType,则会进行空设置;如果参数不为空,那么它将采用setNonNullPa-rameter方法设置参数。
        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 var7) {
                    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: " + var7, var7); } } else { try { this.setNonNullParameter(ps, i, parameter, jdbcType); } catch (Exception var6) { throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . "
    + "Try setting a different JdbcType for this parameter or a different configuration property. " + "Cause: " + var6, var6); } } } //getResult方法,非空结果集是通过getNullableResult方法获取的。如果判断为空,则返回null。 public T getResult(ResultSet rs, String columnName) throws SQLException { Object result; try { result = this.getNullableResult(rs, columnName); } catch (Exception var5) { throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + var5, var5); } return rs.wasNull() ? null : result; } public T getResult(ResultSet rs, int columnIndex) throws SQLException { Object result; try { result = this.getNullableResult(rs, columnIndex); } catch (Exception var5) { throw new ResultMapException("Error attempting to get column #" + columnIndex + " from result set. Cause: " + var5, var5); } return rs.wasNull() ? null : result; } public T getResult(CallableStatement cs, int columnIndex) throws SQLException { Object result; try { result = this.getNullableResult(cs, columnIndex); } catch (Exception var5) { throw new ResultMapException("Error attempting to get column #" + columnIndex + " from callable statement. Cause: " + var5, var5); } return cs.wasNull() ? null : result; } public abstract void setNonNullParameter(PreparedStatement var1, int var2, T var3, JdbcType var4) throws SQLException; //getNullableResult方法用于存储过程。 public abstract T getNullableResult(ResultSet var1, String var2) throws SQLException; public abstract T getNullableResult(ResultSet var1, int var2) throws SQLException; public abstract T getNullableResult(CallableStatement var1, int var2) throws SQLException; }

      

      MyBatis使用最多的typeHanlder之一——StringTypeHandler。它用于字符串转换,其源码如代码清单4-11所示。
      代码清单4-11:StringTypeHanlder

    public class StringTypeHandler extends BaseTypeHandler<String> {
        public StringTypeHandler() {
        }
    
        public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
            ps.setString(i, parameter);
        }
    
        public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
            return rs.getString(columnName);
        }
    
        public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
            return rs.getString(columnIndex);
        }
    
        public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
            return cs.getString(columnIndex);
        }
    }

      显然它实现了BaseTypeHandler的4个抽象方法,代码也非常简单。
      在这里,MyBatis把javaType和jdbcType相互转换,那么它们是如何注册的呢?在MyBatis中采用org.apache.ibatis.type.TypeHandlerRegistry类对象的register方法进行注册,如代码清单4-12所示。
      代码清单4-12:系统注册typeHanlder

    public TypeHandlerRegistry() {
        register(Boolean.class, new BooleanTypeHandler());
        register(boolean.class, new BooleanTypeHandler());
        //....
        register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
        register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
        //....
    }

    这样就实现了用代码的形式注册typeHandler。注意,自定义的typeHandler一般不会使用代码注册,而是通过配置或扫描

    mybatis 自定义typeHandler

    mybatis 枚举typeHandler

  • 相关阅读:
    洛谷 P1396 营救(二分答案)
    洛谷 P1807 最长路(toposort)
    洛谷 P1137 旅行计划(toposort)
    [bzoj3032][TYVJ P1924]七夕祭(环形均分纸牌,货仓选址)
    BZOJ3043 IncDec Sequence (差分)
    BZOJ2200 道路与航线(dijk+拓扑排序)
    P1462 通往奥格瑞玛的道路(二分+dijkstra)
    P1020 导弹拦截 O(nlogn)做法
    背包整理(01背包,完全背包,多重背包,分组背包)(待更新)
    [模板]树链剖分
  • 原文地址:https://www.cnblogs.com/ooo0/p/10914091.html
Copyright © 2011-2022 走看看