精华内容
下载资源
问答
  • 一、概述   Mybatis类型转换模块,除了定义了一些列的类型处理器(类型转换器)外,还提供了一些其他类,比如:类型...MappedJdbcTypes 注解类 用于指明类型处理器可以处理的JdbcType中的类型集合 MappedTypes 注...
    一、概述

      Mybatis类型转换模块,除了定义了一些列的类型处理器(类型转换器)外,还提供了一些其他类,比如:类型处理器注册器类、别名注解类等。主要包含了以下类:

    1. Alias 注解类
      别名注解
    2. JdbcType 枚举类
      JdbcType枚举类,对应数据库表中字段的类型
    3. MappedJdbcTypes 注解类
      用于指明类型处理器可以处理的JdbcType中的类型集合
    4. MappedTypes 注解类
      用于指明该TypeHandler实现类能够处理的Java 类型的集合
    5. SimpleTypeRegistry 简单类型注册器
      用于判断哪些类是简单类型,和JAVA的基本数据类型不完全一样
    6. TypeAliasRegistry 类别名注册器
      用于取代复杂的类型全限定名,mybatis中用于映射器配置文件中进行参数类型与返回结果类型的设置。默认完成了大量基础类型别名的注册。
    7. TypeException 异常类
    8. TypeHandlerRegistry 类型处理器注册器
      主要完成类型处理器的注册功能,同时也能对类型处理器进行统筹管理,其内部定义了集合来进行类型处理器的存取,同时定义了存取方法。默认完成了大量常见类型处理器的注册。
    二、类详解
    1、类型别名
    • 用法
      类型别名是为Java类型设置一个简单的名字。它只和 XML配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如:
    <typeAliases>
      <typeAlias alias="Author" type="domain.blog.Author"/>
      <typeAlias alias="Blog" type="domain.blog.Blog"/>
      <typeAlias alias="Comment" type="domain.blog.Comment"/>
      <typeAlias alias="Post" type="domain.blog.Post"/>
      <typeAlias alias="Section" type="domain.blog.Section"/>
      <typeAlias alias="Tag" type="domain.blog.Tag"/>
    </typeAliases>
    

      当这样配置时,Blog可以用在任何使用domain.blog.Blog的地方。

      也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

    <typeAliases>
      <package name="domain.blog"/>
    </typeAliases>
    

      每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。看下面的例子:

     @Alias("author")
     public class Author {
           ...
     }
    

    注:上面类型别名的用法实例,来源Mybatis官方文档。

    • 别名注解 Alias

    根据上面实例可以知道,注解@Alias主要用来表示该类对应的简写名称,需要配合Mybatis的配置文件来使用。

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface Alias {
      String value();
    }
    

    TypeAliasRegistry.registerAlias()方法用来处理别名注解,主要是解析注解,并把简写名和对应的java类型注册到类型别名注册器中。

    public void registerAlias(Class<?> type) {
        String alias = type.getSimpleName();
        //判断需要注册别名的类上,是否有Alias注解,如果有就使用注解中的值,否则就用类的简名
        Alias aliasAnnotation = type.getAnnotation(Alias.class);
        if (aliasAnnotation != null) {
          alias = aliasAnnotation.value();
        } 
        //调用通用的别名注册方法实现类别名的注册
        registerAlias(alias, type);
      }
    
    2、JdbcType 枚举类

      JdbcType这个枚举类型表示了在JDBC中的数据类型,该枚举类中定义了TYPE_CODE宇段,表示JDBC类型在java.sql.Types中相应的常量编码,并通过一个静态集合codeLookup (Map<Integer,JdbcType>类型〉记录了常量编码与JdbcType 之间的对应关系。

      这个枚举类的代码结构主要包括了

    1. 枚举值的定义,枚举值定义的时候,把对应的java.sql.Types常量编码,存储到了对应的TYPE_CODE字段中;
    2. 字段TYPE_CODE和codeLookup。其中,TYPE_CODE表示当前枚举值对应的在java.sql.Types中相应的常量编码;codeLookup记录了常量编码与JdbcType 之间的对应关系。
    3. 静态代码块 主要是把所有枚举值和java.sql.Types中相应的常量编码建立一一对应的关系,并存储到codeLookup对象中。
    4. 构造函数,提供定义枚举值的时候,可以和java.sql.Types中相应的常量编码建立对应关系。
    5. forCode()方法,提供根据java.sql.Types中相应的常量编码查询JdbcType的方法,供其他地方引用。
    public enum JdbcType {
    
      ARRAY(Types.ARRAY),
      BIT(Types.BIT),
      TINYINT(Types.TINYINT),
      SMALLINT(Types.SMALLINT),
      INTEGER(Types.INTEGER),
      BIGINT(Types.BIGINT),
      FLOAT(Types.FLOAT),
      REAL(Types.REAL),
      DOUBLE(Types.DOUBLE),
      NUMERIC(Types.NUMERIC),
      DECIMAL(Types.DECIMAL),
      CHAR(Types.CHAR),
      VARCHAR(Types.VARCHAR),
      LONGVARCHAR(Types.LONGVARCHAR),
      DATE(Types.DATE),
      TIME(Types.TIME),
      TIMESTAMP(Types.TIMESTAMP),
      BINARY(Types.BINARY),
      VARBINARY(Types.VARBINARY),
      LONGVARBINARY(Types.LONGVARBINARY),
      NULL(Types.NULL),
      OTHER(Types.OTHER),
      BLOB(Types.BLOB),
      CLOB(Types.CLOB),
      BOOLEAN(Types.BOOLEAN),
      CURSOR(-10), // Oracle
      UNDEFINED(Integer.MIN_VALUE + 1000),
      NVARCHAR(Types.NVARCHAR), // JDK6
      NCHAR(Types.NCHAR), // JDK6
      NCLOB(Types.NCLOB), // JDK6
      STRUCT(Types.STRUCT),
      JAVA_OBJECT(Types.JAVA_OBJECT),
      DISTINCT(Types.DISTINCT),
      REF(Types.REF),
      DATALINK(Types.DATALINK),
      ROWID(Types.ROWID), // JDK6
      LONGNVARCHAR(Types.LONGNVARCHAR), // JDK6
      SQLXML(Types.SQLXML), // JDK6
      DATETIMEOFFSET(-155); // SQL Server 2008
    
      public final int TYPE_CODE;
      private static Map<Integer,JdbcType> codeLookup = new HashMap<Integer,JdbcType>();
    
      static {
        for (JdbcType type : JdbcType.values()) {
          codeLookup.put(type.TYPE_CODE, type);
        }
      }
    
      JdbcType(int code) {
        this.TYPE_CODE = code;
      }
    
      public static JdbcType forCode(int code)  {
        return codeLookup.get(code);
      }
    
    }
    
    
    3、@MappedTypes、@MappedJdbcTypes

      @MappedTypes注解用于指明该TypeHandler实现类能够处理的Java 类型的集合,@MappedJdbcTypes 注解用于指明该TypeHandler 实现类能够处理的JDBC类型集合。

    • @MappedTypes注解
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface MappedTypes {
      Class<?>[] value();
    }
    
    • @MappedTypes注解解析器,用于注册相关类型处理器。代码如下:
     public void register(Class<?> typeHandlerClass) {
        boolean mappedTypeFound = false;
        MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
        if (mappedTypes != null) {
          for (Class<?> javaTypeClass : mappedTypes.value()) {
            register(javaTypeClass, typeHandlerClass);
            mappedTypeFound = true;
          }
        }
        if (!mappedTypeFound) {
          register(getInstance(null, typeHandlerClass));
        }
      }
    

    Mybatis中不止这一处解析该注解,仅用作分析说明

    • @MappedJdbcTypes
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface MappedJdbcTypes {
      JdbcType[] value();
      boolean includeNullJdbcType() default false;
    }
    
    • @MappedJdbcTypes注解解析器,用于注册相关类型处理器。代码如下:
    /**
       * 将类型处理器注册到对应的Java类型
       * 解析类型处理器是否包含MappedJdbcTypes注解,如果有,把注解对应的所有jdbcType注册到对应的Map中,
       * 否则,默认jdbcType=null,然后注册到对应的Map中。
       * @param javaType
       * @param typeHandler
       */
      private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
    	//获取类型处理器上的注解MappedJdbcTypes的值
        MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
        if (mappedJdbcTypes != null) {//注解不为空,说明类型处理器上有该注解
          //MappedJdbcTypes注解的value是数组,循环把该注解中包含的jdbcType值注册到对应的Map对象中
          for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
            register(javaType, handledJdbcType, typeHandler);
          }
          if (mappedJdbcTypes.includeNullJdbcType()) {//如果有注解,且注解包含jdbcType=null的情况,将该类型注册到对应的Map对象中
            register(javaType, null, typeHandler);
          }
        } else {//如果注解为空,说明没有注解,即默认jdbcType=null,并注册到对应的Map对象中
          register(javaType, null, typeHandler);
        }
      }
    
    
    4、注册器 SimpleTypeRegistry

      简单类型注册器,主要用于判断哪些类是简单类型,和JAVA的基本数据类型不完全一样。这里简单类型主要包括了String、Byte、Short、Character、Integer、Long、Float、Double、Boolean、Date、Class、BigInteger、BigDecimal。提供了一个isSimpleType()方法,用来判断类是否属于简单类型。

    public class SimpleTypeRegistry {
    
      private static final Set<Class<?>> SIMPLE_TYPE_SET = new HashSet<Class<?>>();
    
      static {
        SIMPLE_TYPE_SET.add(String.class);
        SIMPLE_TYPE_SET.add(Byte.class);
        SIMPLE_TYPE_SET.add(Short.class);
        SIMPLE_TYPE_SET.add(Character.class);
        SIMPLE_TYPE_SET.add(Integer.class);
        SIMPLE_TYPE_SET.add(Long.class);
        SIMPLE_TYPE_SET.add(Float.class);
        SIMPLE_TYPE_SET.add(Double.class);
        SIMPLE_TYPE_SET.add(Boolean.class);
        SIMPLE_TYPE_SET.add(Date.class);
        SIMPLE_TYPE_SET.add(Class.class);
        SIMPLE_TYPE_SET.add(BigInteger.class);
        SIMPLE_TYPE_SET.add(BigDecimal.class);
      }
    
      private SimpleTypeRegistry() {
        // Prevent Instantiation
      }
    
      public static boolean isSimpleType(Class<?> clazz) {
        return SIMPLE_TYPE_SET.contains(clazz);
      }
    
    }
    
    
    5、注册器 TypeAliasRegistry

      类别名注册器。用于取代复杂的类型全限定名,mybatis中用于映射器配置文件中进行参数类型与返回结果类型的设置。默认完成了大量基础类型别名的注册。

    • 字段
        类别名注册器TypeAliasRegistry中,只有一个字段TYPE_ALIASES,该字段用来存储的别名和实际类型映射关系的地方,通过HashMap键值对实现。
    private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();
    
    • 构造函数
        构造函数完成了大量基础类型别名的注册。需要注意的是,除了这里进行了别名的注册,在创建Configuration实例的时候在其无参构造器中也注册了一些类别名。
    public TypeAliasRegistry() {
    	//字符串类型
        registerAlias("string", String.class);
        //基本包装类型
        registerAlias("byte", Byte.class);
        registerAlias("long", Long.class);
        
       	//略
      }
    

    Configuration无参构造函数

    public Configuration() {
        typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
        typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
    
        typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
        typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
        typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
        
    	//略
      }
    
    • registerAlias(String alias, Class<?> value)方法
        该方法,主要实现建立别名与对应类型的一一对应关系,本质上就是一个Map对象赋值的操作。其中,添加了别名为null、或者重复添加的判断逻辑。
     public void registerAlias(String alias, Class<?> value) {
        if (alias == null) {//别名不允许为空,否则抛出异常
          throw new TypeException("The parameter alias cannot be null");
        }
        // issue #748
        String key = alias.toLowerCase(Locale.ENGLISH);
        //如果已经存在key了,且value和之前不一致,报错
        if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
          throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
        }
        //把该别名和类型注册到HashMap对象中
        TYPE_ALIASES.put(key, value);
      }
    
    • registerAliases(String packageName)、registerAliases(String packageName, Class<?> superType)类
        该方法主要实现根据包名进行解析别名的方式。其中registerAliases(String packageName)是一个重载简化参数的方法,实际上是由registerAliases(String packageName, Class<?> superType)方法实现功能。其中通过ResolverUtil工具类,获取指定包下符合条件的所有类,然后循环处理进行注册,其中用到了registerAlias(Class<?> type)方法。
    /**
       * 包统一注册方式,对应的是如下的设置方式
       * <typeAliases>
       *  	<package name="com.xx.xx.xx"/>
       * </typeAliases>
       * @param packageName
       */
      public void registerAliases(String packageName){
        registerAliases(packageName, Object.class);
      }
    
    /**
       * 包统一注册方式,实现检索需要注册别名的类,并调用registerAlias(Class<?> type)实现注册
       * 表示注册指定包名下的所有类,它调用了第二个方法,其第二个参数目的是限定要注册的类的来源,只有继承自给定类型的类才能被注册,
       * 这里赋值为Object.class表示其下的所有类均在别名注册的考虑范围。
       * @param packageName
       * @param superType
       */
      public void registerAliases(String packageName, Class<?> superType){
        ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
        resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
        Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
        for(Class<?> type : typeSet){
          // Ignore inner classes and interfaces (including package-info.java)
          // Skip also inner classes. See issue #6
          //排除匿名类、接口、内部类等
          if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
        	  //注册别名
        	  registerAlias(type);
          }
        }
      }
    
    • registerAlias(Class<?> type)方法
        根据对应的类,进行类别名的注册。其中,用到了@Alias注解。当该类上有注解时,就用注解中的值,没有的话,就用类的简写名称。
    public void registerAlias(Class<?> type) {
        String alias = type.getSimpleName();
        //判断需要注册别名的类上,是否有Alias注解,如果有就使用注解中的值,否则就用类的简名
        Alias aliasAnnotation = type.getAnnotation(Alias.class);
        if (aliasAnnotation != null) {
          alias = aliasAnnotation.value();
        } 
        //调用通用的别名注册方法实现类别名的注册
        registerAlias(alias, type);
      }
    
    • registerAlias(String alias, String value)方法
        主要实现逐个注册,因为在配置中已经将目标与所要设置的别名名称都指定好了,因此只需要直接执行核心注册方法即可完成注册工作,这种方式适合少量的类型注册情况,但是一旦需要注册的类型较多,工作就会显得很是复杂繁琐。为了简化工作我们可以采用之前第一种方式,要采用这种方式就要在架构编码时有意的将需要进行类型别名注册的类放置到统一的包下。
    /**
       * 逐个注册方式,对应的是如下的设置方式
       * 	<typeAliases>
       *  		<typeAlias type="com.xxx.xx.Role" alias="role" />
       *	</typeAliases>
       *因为在配置中已经将目标与所要设置的别名名称都指定好了,因此只需要直接执行核心注册方法即可完成注册工作,
       *这种方式适合少量的类型注册情况,但是一旦需要注册的类型较多,工作就会显得很是复杂繁琐,
       *为了简化工作我们可以采用之前第一种方式,要采用这种方式就要在架构编码时有意的将需要进行类型别名注册的类放置到统一的包下。
       * @param alias
       * @param value
       */
      public void registerAlias(String alias, String value) {
        try {
          registerAlias(alias, Resources.classForName(value));
        } catch (ClassNotFoundException e) {
          throw new TypeException("Error registering type alias "+alias+" for "+value+". Cause: " + e, e);
        }
      }
    

    -resolveAlias(String string)方法
      前面分析的都是registerAlias()方法及其重载方法,主要实现别名的注册。该方法主要用来解析别名。通过别名获取集合中保存的别名对应的值,即类的类型。

    @SuppressWarnings("unchecked")
      // throws class cast exception as well if types cannot be assigned
      public <T> Class<T> resolveAlias(String string) {
        try {
          if (string == null) {//别名如果为空,就直接返回null
            return null;
          }
          // issue #748
          String key = string.toLowerCase(Locale.ENGLISH);
          Class<T> value;
          if (TYPE_ALIASES.containsKey(key)) {//如果在别名中注册了,就直接获取别名对应的类类型
            value = (Class<T>) TYPE_ALIASES.get(key);
          } else {
        	找不到,再试着将String直接转成Class(这样怪不得我们也可以直接用java.lang.Integer的方式定义,也可以就int这么定义)
            value = (Class<T>) Resources.classForName(string);
          }
          return value;
        } catch (ClassNotFoundException e) {
          throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
        }
      }
    
    6、注册器 TypeHandlerRegistry

      类型处理器注册器。主要完成类型处理器的注册功能,同时也能对类型处理器进行统筹管理,其内部定义了集合来进行类型处理器的存取,同时定义了存取方法。默认完成了大量常见类型处理器的注册。

    • 字段

    类型处理器TypeHandlerRegistry 中,主要包括了一下字段:

    1. JDBC_TYPE_HANDLER_MAP
      这是一个枚举Map集合,其内部是以JdbcType枚举类中枚举值为键创建的一种集合,即它是以数据库类型为键来保存类型处理器,将类型处理器注册到对应的数据库类型上。
    2. TYPE_HANDLER_MAP、
      这是一个嵌套Map集合,内层集合是以数据库类型为键保存处理器,外层集合为以Java类型来保存对应的数据库类型及其处理器,这个集合将三者联系起来,是真正进行三者对应关系匹配的集合。
    3. UNKNOWN_TYPE_HANDLER
      这个是上一篇中分析到的类型处理器,在在实际使用的过程中,根据JavaType和JdbcType来确定的处理器类型。
    4. ALL_TYPE_HANDLERS_MAP
      这个集合中保存着所有的类型处理器,是以类型处理器的类类型为键值保存的,它可以统筹所有的类型处理器(带有统计的效果)
    5. NULL_TYPE_HANDLER_MAP
      空的 以JdbcType枚举类中枚举值为键创建的一种集合.用于判断一个Map<JdbcType, TypeHandler<?>>实例是否为空。
    6. defaultEnumTypeHandler
      默认的枚举类型处理器
      private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
    
      private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new ConcurrentHashMap<Type, Map<JdbcType, TypeHandler<?>>>();
    
      private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
    
      private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();
    
      private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();
    
      private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;
    
    • 构造函数

    构造函数,主要通过register()的几个重载方法,实现了对默认Java类型或JDBC类型的数据,进行类型处理器的注册功能。主要用到了三类方法,下面会分别分析。

    1. register(Class javaType, TypeHandler<? extends T> typeHandler)
    2. register(Class type, JdbcType jdbcType, TypeHandler<? extends T> handler).
    3. register(JdbcType jdbcType, TypeHandler<?> handler)
    public TypeHandlerRegistry() {
    	//逻辑值、布尔值
        register(Boolean.class, new BooleanTypeHandler());
        register(boolean.class, new BooleanTypeHandler());
        register(JdbcType.BOOLEAN, new BooleanTypeHandler());
        register(JdbcType.BIT, new BooleanTypeHandler());
        
    	//略
    	
        //字符串,以下是为同一个类型的多种变种注册到多个不同的handler
        register(Reader.class, new ClobReaderTypeHandler());
        register(String.class, new StringTypeHandler());
        register(String.class, JdbcType.CHAR, new StringTypeHandler());
        register(String.class, JdbcType.CLOB, new ClobTypeHandler());
        register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
        register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
        register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
        register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
        register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
        register(JdbcType.CHAR, new StringTypeHandler());
        register(JdbcType.VARCHAR, new StringTypeHandler());
        register(JdbcType.CLOB, new ClobTypeHandler());
        register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
        register(JdbcType.NVARCHAR, new NStringTypeHandler());
        register(JdbcType.NCHAR, new NStringTypeHandler());
        register(JdbcType.NCLOB, new NClobTypeHandler());
    
        
    	//略
    	
      }
    
    • register()方法及其重载方法
      register()方法及其重载方法中,除了几个核心的处理方法外,其他的都是调用底层方法而实现,下面主要分析了核心的几个方法:
    1. register(JdbcType jdbcType, TypeHandler<?> handler)
      建立jdbcType与类型处理器的映射关系,即把该映射关系注册到JDBC_TYPE_HANDLER_MAP集合对象中。
     public void register(JdbcType jdbcType, TypeHandler<?> handler) {
        JDBC_TYPE_HANDLER_MAP.put(jdbcType, handler);
      }
    
    1. register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler)
      该方法首先把类型、jdbcTpe、类型处理器对应的关系注册到TYPE_HANDLER_MAP集合中,然后把类型与类型处理器对应关系注册到ALL_TYPE_HANDLERS_MAP集合中。
    private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
        if (javaType != null) {//javaType不为空时,为空时不注册到TYPE_HANDLER_MAP集合
          //根据javaType获取jdbcType与类型处理键值对的集合
          Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
          if (map == null || map == NULL_TYPE_HANDLER_MAP) {//如果不存在,即没有被注册
        	//新建jdbcType与类型处理器键值对对象,并把该对象注册到TYPE_HANDLER_MAP对象
            map = new HashMap<JdbcType, TypeHandler<?>>();
            TYPE_HANDLER_MAP.put(javaType, map);
          }
          //重置map中的jdbcType和类型处理器,当原来map为空时,也是在这里进行了赋值,如果map不为空,即覆盖原来的值
          map.put(jdbcType, handler);
        }
        //把javaType与类型处理器的映射关系注册到ALL_TYPE_HANDLERS_MAP对象中
        ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
      }
    
    1. register(Type javaType, TypeHandler<? extends T> typeHandler)
      建立类型处理器注册与Java类型的对应关系。解析类型处理器是否包含MappedJdbcTypes注解,如果有,把注解对应的所有jdbcType注册到对应的Map中,否则,默认jdbcType=null,然后注册到对应的Map中。底层通过register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler)方法实现了数据的初始化。
    private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
    	//获取类型处理器上的注解MappedJdbcTypes的值
        MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
        if (mappedJdbcTypes != null) {//注解不为空,说明类型处理器上有该注解
          //MappedJdbcTypes注解的value是数组,循环把该注解中包含的jdbcType值注册到对应的Map对象中
          for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
            register(javaType, handledJdbcType, typeHandler);
          }
          if (mappedJdbcTypes.includeNullJdbcType()) {//如果有注解,且注解包含jdbcType=null的情况,将该类型注册到对应的Map对象中
            register(javaType, null, typeHandler);
          }
        } else {//如果注解为空,说明没有注解,即默认jdbcType=null,并注册到对应的Map对象中
          register(javaType, null, typeHandler);
        }
      }
    
    1. register(TypeHandler typeHandler)
      根据注解MappedTypes建立类类型与类型处理器的对应关系。
    @SuppressWarnings("unchecked")
      public <T> void register(TypeHandler<T> typeHandler) {
        boolean mappedTypeFound = false;
        MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);
        if (mappedTypes != null) {
          for (Class<?> handledType : mappedTypes.value()) {
            register(handledType, typeHandler);
            mappedTypeFound = true;
          }
        }
        // @since 3.1.0 - try to auto-discover the mapped type
        if (!mappedTypeFound && typeHandler instanceof TypeReference) {
          try {
            TypeReference<T> typeReference = (TypeReference<T>) typeHandler;
            register(typeReference.getRawType(), typeHandler);
            mappedTypeFound = true;
          } catch (Throwable t) {
            // maybe users define the TypeReference with a different type and are not assignable, so just ignore it
          }
        }
        if (!mappedTypeFound) {
          register((Class<T>) null, typeHandler);
        }
      }
    
    
    1. register(String packageName)
      底层还是通过register(Class<?> typeHandlerClass)方法实现。
     public void register(String packageName) {
        ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
        resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
        Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses();
        for (Class<?> type : handlerSet) {
          //Ignore inner classes and interfaces (including package-info.java) and abstract classes
          if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
            register(type);
          }
        }
      }
    
    • getTypeHandler(Type type, JdbcType jdbcType)方法
      根据javaType和jdbcType获取类型处理器的底层方法
    @SuppressWarnings("unchecked")
      private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
        if (ParamMap.class.equals(type)) {//等于ParamMap类型时,直接返回null.
          return null;
        }
        //根据type查询jdbcHandlerMap
        Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
        //存储类型处理器
        TypeHandler<?> handler = null;
        if (jdbcHandlerMap != null) {
          //查询type和jdbcType对应的类型处理器
          handler = jdbcHandlerMap.get(jdbcType);
          if (handler == null) {//如果类型处理器为空,继续查询jdbcType=null对应的类型处理器
            handler = jdbcHandlerMap.get(null);
          }
          if (handler == null) {
            // #591
            handler = pickSoleHandler(jdbcHandlerMap);
          }
        }
        // type drives generics here
        return (TypeHandler<T>) handler;
      }
    
    • getJdbcHandlerMap(Type type)
      根据javaType查询jdbcType与类型处理器对应关系的键值对
    private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMap(Type type) {
    	//根据javaType从TYPE_HANDLER_MAP获取jdbcType与类型处理器对应关系的键值对
        Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
        if (NULL_TYPE_HANDLER_MAP.equals(jdbcHandlerMap)) {//如果等于空集合,直接返回null
          return null;
        }
        if (jdbcHandlerMap == null && type instanceof Class) {//key:ifnull,如果为空,继续查询该类型超类对应的jdbcHandlerMap
          Class<?> clazz = (Class<?>) type;
          if (clazz.isEnum()) {//处理枚举类型处理器
        	//获取枚举类型处理器
            jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(clazz, clazz);
            if (jdbcHandlerMap == null) {//如果枚举类型处理器为空,
              //把默认的枚举类型处理器defaultEnumTypeHandler注册进去
              register(clazz, getInstance(clazz, defaultEnumTypeHandler));
              //返回上面注册的类型处理器
              return TYPE_HANDLER_MAP.get(clazz);
            }
          } else {//根据超类获取对应的jdbcHandlerMap
            jdbcHandlerMap = getJdbcHandlerMapForSuperclass(clazz);
          }
        }
        //把查询结果更新到TYPE_HANDLER_MAP集合中(当执行了key:ifnull语句时,就需要把最新的结果更新到TYPE_HANDLER_MAP)
        TYPE_HANDLER_MAP.put(type, jdbcHandlerMap == null ? NULL_TYPE_HANDLER_MAP : jdbcHandlerMap);
        return jdbcHandlerMap;
      }
    
    • **getJdbcHandlerMapForEnumInterfaces(Class<?> clazz, Class<?> enumClazz) **
      获取枚举类型的类型处理器
    private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForEnumInterfaces(Class<?> clazz, Class<?> enumClazz) {
        for (Class<?> iface : clazz.getInterfaces()) {
          Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(iface);
          if (jdbcHandlerMap == null) {
            jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(iface, enumClazz);
          }
          if (jdbcHandlerMap != null) {
            // Found a type handler regsiterd to a super interface
            HashMap<JdbcType, TypeHandler<?>> newMap = new HashMap<JdbcType, TypeHandler<?>>();
            for (Entry<JdbcType, TypeHandler<?>> entry : jdbcHandlerMap.entrySet()) {
              // Create a type handler instance with enum type as a constructor arg
              newMap.put(entry.getKey(), getInstance(enumClazz, entry.getValue().getClass()));
            }
            return newMap;
          }
        }
        return null;
      }
    
    • getJdbcHandlerMapForSuperclass(Class<?> clazz)
      根据超类获取jdbcHandlerMap.
    private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForSuperclass(Class<?> clazz) {
        Class<?> superclass =  clazz.getSuperclass();
        if (superclass == null || Object.class.equals(superclass)) {//如果没有超类或者超类是Object
          return null;
        }
        //获取超类对应的jdbcHandlerMap
        Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(superclass);
        if (jdbcHandlerMap != null) {//如果获取到就直接返回
          return jdbcHandlerMap;
        } else {//如果没有获取到,就继续递归查询上级超类对应的jdbcHandlerMap
          return getJdbcHandlerMapForSuperclass(superclass);
        }
      }
    
    7、异常类TypeException

      Mybatis类型转换模块的异常处理类,比较简单,不做分析。

    展开全文
  • 卷 文档 的文件夹 PATH 列表 卷序列号为 000C-BB91 E:. │ config.properties │ Dao.java │ GeneratorDemo.java │ hibernate.cfg.xml │ HibernateDaoImpl.java │ HibernateSessionFactory.java ...
  • //MappedJdbcTypes 指定数据库里的类型 @MappedJdbcTypes(JdbcType.BLOB) //MappedTypes 指定要转换的类型 @MappedTypes(value = String.class) public class BloBTypeHandler extends BaseTypeHandler<String>...

    将MySQL中的blob类型转换成Java中的String类型

    //MappedJdbcTypes 指定数据库里的类型
    @MappedJdbcTypes(JdbcType.BLOB)
    //MappedTypes 指定要转换的类型
    @MappedTypes(value = String.class)
    public class BloBTypeHandler extends BaseTypeHandler<String> {
    
        private static final String DEFAULT_CHARSET = "UTF-8";
    
    
    //一个setxxx方法,表示向PreparedStatement里面设置值。三个getxxx方法,一个是根据列名获取值,一个是根据列索引位置获取值,最后一个是存储过程
        @Override
        public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
            try {
                ps.setBlob(i, new ByteArrayInputStream(parameter.getBytes(DEFAULT_CHARSET)));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
            return blobToString(rs.getBlob(columnName));
        }
    
        @Override
        public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
            return blobToString(rs.getBlob(columnIndex));
        }
    
        @Override
        public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
            return blobToString(cs.getBlob(columnIndex));
        }
    
    
        private String blobToString(Blob blob) {
            String result = null;
            try {
                byte[] returnValue = null;
                if (null != blob) {
                    returnValue = blob.getBytes(1, (int) blob.length());
                }
                if (null != returnValue) {
                    result = new String(returnValue, DEFAULT_CHARSET);
                }
            } catch (Exception e) {
                throw new RuntimeException("Blob Encoding Error!");
            }
            return result;
        }
    }
    

    yml配置文件

    mybatis:
      configuration:
        map-underscore-to-camel-case: true
       //指定BloBTypeHandler所在的包位置
      type-handlers-package: com.xxx.handler
    

    mapper

        @Select("SELECT * FROM wx_public_user WHERE wxId=#{wxId} LIMIT #{page},#{pageNumber}")
        @Results(value = {@Result(property = "nickname", column = "nickname", javaType = String.class, jdbcType = JdbcType.BLOB)})
        List<WxMpUser> findWxID(String wxId, int page, int pageNumber);
    
    展开全文
  • @MappedJdbcTypes(JdbcType.DECIMAL) public class MyBigDecimalTypeHandler extends BigDecimalTypeHandler { @Override public BigDecimal getNullableResult(ResultSet rs, String columnName) throws ...

    1.配置Mybatis处理Decimal类型类

    @MappedJdbcTypes(JdbcType.DECIMAL)
    public class MyBigDecimalTypeHandler extends BigDecimalTypeHandler {
    
        @Override
        public BigDecimal getNullableResult(ResultSet rs, String columnName) throws SQLException {
            BigDecimal result =super.getNullableResult(rs, columnName)==null?BigDecimal.ZERO.setScale(2, BigDecimal.ROUND_HALF_UP):super.getNullableResult(rs, columnName).setScale(2, BigDecimal.ROUND_HALF_UP);
            return  result;
        }
    
        @Override
        public BigDecimal getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
            return super.getNullableResult(rs, columnIndex)==null?BigDecimal.ZERO.setScale(2, BigDecimal.ROUND_HALF_UP):super.getNullableResult(rs, columnIndex).setScale(2, BigDecimal.ROUND_HALF_UP);
        }
    
        @Override
        public BigDecimal getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
            return super.getNullableResult(cs, columnIndex)==null?BigDecimal.ZERO.setScale(2, BigDecimal.ROUND_HALF_UP):super.getNullableResult(cs, columnIndex).setScale(2, BigDecimal.ROUND_HALF_UP);
        }
    }
    

    2.注册

    在yml文件配置,此处我用的是mybatis-plus的配置,使用mybatis原生的采用mybatis的type-handlers-package
    在这里插入图片描述

    3.Springboot中Bigdecimal以json格式返回前端依然丢失小数点

    @JsonFormat(shape = JsonFormat.Shape.STRING)
    private BigDecimal test;
    
    

    就是在把数据给前端的时候,把数据转换成string类型,这样就不会丢失小数点后面的(.00)数据。需要注意的是,前端接收到的是string类型数据,如果涉及到数据计算问题,需要前端进行数据转换!

    展开全文
  • 自定义个TypeHandler 然后把setParameter的...@MappedJdbcTypes(JdbcType.VARCHAR) public class StringTypeCustomizeHandler implements TypeHandler<String> { @Override public void setParameter(Prepare

    自定义个TypeHandler 然后把setParameter的setString改成setObject

    
    @Component
    @MappedTypes(String.class)
    @MappedJdbcTypes(JdbcType.VARCHAR)
    public class StringTypeCustomizeHandler implements TypeHandler<String> {
        @Override
        public void setParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {
            preparedStatement.setObject(i, s);
        }
    
        @Override
        public String getResult(ResultSet resultSet, String s) throws SQLException {
            return resultSet.getString(s);
        }
    
        @Override
        public String getResult(ResultSet resultSet, int i) throws SQLException {
            return resultSet.getString(i);
        }
    
        @Override
        public String getResult(CallableStatement callableStatement, int i) throws SQLException {
            return callableStatement.getString(i);
        }
    }
    

    springboot中指定TypeHandler的路径

    #用到了mybatis-plus
    mybatis-plus.type-handlers-package=com.xiaobu.handlers
    

    SqlSessionFactory也需要指定

        @Autowired
        StringTypeCustomizeHandler stringTypeCustomizeHandler;
    
            TypeHandler<?>[]  typeHandlers=new TypeHandler[]{stringTypeCustomizeHandler};
            sqlSessionFactory.setTypeHandlers(typeHandlers);
    

    然后mapper.xml里面指定类型

    
        <result column="name" typeHandler="com.xiaobu.handlers.StringTypeCustomizeHandler" property="name" />
    
                    #{name,typeHandler=com.xiaobu.handlers.StringTypeCustomizeHandler}
    
    

    https://community.cloudera.com/t5/Support-Questions/HIVE-PARAMETER-QUERY-DATA-TYPE-ERR-NON-SUPPORT-DATA-TYPE/td-p/58674

    展开全文
  • @MappedJdbcTypes(JdbcType.VARCHAR) @MappedTypes({String.class}) public class MyStringTypeHandler extends BaseTypeHandler<String> { @Override public void setNonNullParameter(PreparedStatement ...
  • mybatis-plus 处理JGeometry数据类型 ...@MappedJdbcTypes({JdbcType.STRUCT}) public class SdoTypeHandler implements TypeHandler<JGeometry> { @Override public void setParameter(PreparedS
  • @MappedJdbcTypes(JdbcType.ARRAY) public class ArrayTypeHandler extends BaseTypeHandler<Object[]> { private static final String TYPE_NAME_VARCHAR = "varchar"; private static final String TYPE_...
  • 需求: 数据入库前过滤emoji表情 ...@MappedJdbcTypes({JdbcType.VARCHAR}) public class EmojiStringTypeHandler extends StringTypeHandler { @Override public void setNonNullParameter(Prepar
  • 实现一个自定义版本的...@MappedJdbcTypes(JdbcType.INTEGER) @MappedTypes(SexEnum.class) public class MyEnumTypeHandler implements TypeHandler<SexEnum> { @Override public void setParameter(Prep...
  • Mybatis通过Type...@MappedJdbcTypes(JdbcType.VARCHAR) @MappedTypes({List.class}) public class ListTypeHandler extends BaseTypeHandler<List> { @Override public void setNonNullParameter(Prepared
  • 问题: mybatis-spring-boot-starter版本2.0.1 通过type-handlers-package配置自定义处理器路径无效 处理器代码: ...@MappedJdbcTypes({JdbcType.INTEGER}) public class CodeMsgEnumTypeHandler<T ex...
  • ... import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedTypes;.
  • 这样的代码网上有很多,但是本人亲测有bug, ... 1 @MappedJdbcTypes(JdbcType.BLOB) 2 public class BlobAndStringTypeHandler extends BaseTypeHandler<String> { 3 4 private static final Stri...
  • // import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type....import org.apache.ibatis.type.MappedJdbcTypes; import java.sql.CallableStatement; import java.sql.PreparedSt...
  • 1、首先,自定义typeHandler /** * Created by bo on 2019/1/6. * java mybatis映射...@MappedJdbcTypes(JdbcType.ARRAY) @MappedTypes(String[].class) public class ArrayTypeHandler extends BaseTyp...
  • 前置 mybatis plus postgresql 问题 最近使用pgsql时使用了数组类型和json类型,但是在插入数据或查询的时候不像mysql那么...@MappedJdbcTypes(JdbcType.ARRAY) public class ArrayType2Handler extends BaseTypeH...
  • 数据库中存入JSON数据

    2020-06-28 18:28:41
    1.创建一个json格式处理类 ... ... import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType;...import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedTyp
  • 欢迎使用Markdown编辑器 Mysql 5.7中新增了Json类型,但是Mybatis中并没有很好地支持,必须...@MappedJdbcTypes(JdbcType.OTHER) @MappedTypes(GeoPoint.class) public class GeoPointTypeHandler extends BaseTyp...
  • 1.首先是写方法生成一个自定义转换器 ... import org.apache.ibatis.type.BaseTypeHandler;...import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedTypes; import java.sql.Call
  • 1、新建自定义typeHandler类CustomTypeHandler并...@MappedJdbcTypes({ JdbcType.TIMESTAMP }) @MappedTypes({ String.class }) 3、重写父类方法,并实现自己的需求 4、对参数进行拦截 5、mybatis的mapper...
  • TypeHandler

    2017-11-24 14:13:00
    配置注册 mybatis.type-handlers-package=... 使用 ...@MappedJdbcTypes({JdbcType.CLOB}) @MappedTypes({String.class}) public class TextHandler implements TypeHandler<String> { ...
  • mybatis使用typeHandler

    2020-05-12 22:34:42
    我实体类中有一个Object[]属性,存到数据库时呀...@MappedJdbcTypes(value = JdbcType.VARCHAR) public class ArrayStringHandler extends BaseTypeHandler<Object[]> { /** * 数组转string并保存到数据库 *
  • MyBatis Plus 自动类型转换之TypeHandler

    千次阅读 2020-08-20 10:18:40
    自动类型转换 数据库里有一个ids字段,里面存放的都是一些其他表的主键id,逗号分隔。 ...@MappedJdbcTypes(JdbcType.VARCHAR) public class SetTypeHandler extends BaseTypeHandler<Set> {
  • 踩坑记录 项目为支持枚举直接映射成数据库字段,...@MappedJdbcTypes(JdbcType.CHAR) public class EnumTypeHandler <E extends Enum<E>> extends BaseTypeHandler<E> { //代码细节 } springb...
  • ###mybatis自定义类型转换器 在开发权限系统时,需要存储某个角色对应的权限信息,权限信息通过,分割,填入权限的键值。例如数据库存储如下: 类型转换器代码如下: ...import org.apache.ibatis.type.MappedJdbcTypes
  • 1、自定义一个转换类型,获取数据库数据并输出数据时,把数据库的Varchar类型转为java的String[]类型 前提(1) 定义一个类...@MappedTypes({String[].class})和@MappedJdbcTypes({JdbcType.V...
  • 2、使用@MappedTypes定义处理的java类型 使用@MappedJdbcTypes定义jdbcType类型 3、在自定义结果集标签或者参数处理的时候声明使用自定义 TypeHandler进行处理 或者在全局配置TypeHandler要处理的javaType 事例,...
  • 文章目录自定义类型转换器实现步骤代码实现使用示例 ...通过@MappedJdbcTypes和@MappedTypes注解指定互相映射的数据类型 实现接口方法(通过PreparedStatement和ResultSet进行参数类型转换) 代码实现 /**
  • 自定义TypeHandler处理枚举  我们可以通过自定义TypeHandler的形式来... 2、使用@MappedTypes定义处理的java类型 使用@MappedJdbcTypes定义jdbcType类型  3、在自定义结果集标签或者参数处理的时候声明使用自定义...

空空如也

空空如也

1 2
收藏数 38
精华内容 15
关键字:

mappedjdbctypes