栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Mybatis标签之 typeHandlers 使用及解析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Mybatis标签之 typeHandlers 使用及解析

Mybatis标签之 typeHandlers 使用及解析

文章目录
  • Mybatis标签之 typeHandlers 使用及解析
  • 一、typeHandler 作用
  • 二、typeHandler 的使用
  • 三、TypeHandlerRegistry
  • 四、自定义TypeHandler的解析注册

一、typeHandler 作用

由于Java 类型和数据库的 JDBC 类型不是一一对应的(比如 String 与 varchar), 所以我们把 Java 对象转换为数据库的值,和把数据库的值转换成 Java 对象,需要经过 一定的转换,这两个方向的转换就要用到 TypeHandler,那么有人可能在想了,我们平时没有做任何关于TypeHandler的配置,为什么实体类对象里面的String属性,可以保存成数据库里面的varchar字段或者保存成char字段呢,这是因为MyBatis中已经内置了很多TypeHandler。

二、typeHandler 的使用

这里以这么一个需求来举例说明: 如果我们的对象里面有复杂对象,比如 BlogComment 里面包括了一个 Comment 对象,这个 时候 Comment 对象的全部属性不能直接映射到数据库的一个字段。创建一个 TypeHandler,可以将Comment 转换为 json 字符串,保存到数据库的 VARCHAR 类型中。在从数据库查询的时候,再转换为原来的Comment 对象。

BlogComment实体类:

@Data
public class BlogComment{

    
    private Integer bid;
    
    private String name;
    
    private Integer authorId;
    
    private Comment comment;

}

Comment实体类:

@Data
public class Comment {

    
    Integer commentId;
    
    Integer bid;
    
    String content;

}

1. 编写CommentTypeHandler

public class CommentTypeHandler extends baseTypeHandler {

    
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int columnIndex, Object parameter, JdbcType jdbcType) throws SQLException {
        if(parameter instanceof Comment){
            preparedStatement.setString(columnIndex, JSONObject.toJSONString(parameter));
        } else {
            preparedStatement.setObject(columnIndex, parameter);
        }
    }

    @Override
    public Comment getNullableResult(ResultSet resultSet, String columnName) throws SQLException {
        if(StringUtils.isEmpty(resultSet.getString(columnName))){
            return null;
        }
        return JSONObject.parseObject(resultSet.getString(columnName), Comment.class);
    }

    @Override
    public Comment getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException {
        if(StringUtils.isEmpty(resultSet.getString(columnIndex))){
           return null;
        }
        return JSONObject.parseObject(resultSet.getString(columnIndex), Comment.class);
    }

    @Override
    public Comment getNullableResult(CallableStatement callableStatement, int columnIndex) throws SQLException {
        if(StringUtils.isEmpty(callableStatement.getString(columnIndex))){
            return null;
        }
        return JSONObject.parseObject(callableStatement.getString(columnIndex), Comment.class);
    }

}
 

2. 在mybatis-config.xml 文件中注册CommentTypeHandler

    
        
    

** 3. 在需要使用的字段上指定CommentTypeHandler**
比如插入值的时候使用:

    
        insert into blog (bid, name, author_id, comment)
        values (
            #{bid,jdbcType=INTEGER},
            #{name,jdbcType=VARCHAR},
            #{authorId,jdbcType=INTEGER},
            #{comment,jdbcType=VARCHAR, typeHandler=com.zdp.type.CommentTypeHandler}
        )
    

在查询的时候使用:

    
        
        
        
        
    

    
        select * from blog where bid = #{bid}
    

4. 测试类
插入测试类

    @Test
    public void testInsertComment() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession();
        try {
            BlogCommentMapper mapper = session.getMapper(BlogCommentMapper.class);
            BlogComment blog = new BlogComment();
            blog.setAuthorId(3);
            blog.setName("name...");
            blog.setBid(14);
            Comment comment = new Comment();
            comment.setBid(233);
            comment.setCommentId(999);
            comment.setContent("评论....啊啊啊啊啊啊啊嗷嗷嗷");
            blog.setComment(comment);
            int i = mapper.insertBlog(blog);
            session.commit();
        } finally {
            session.close();
        }
    }

查询测试类

    @Test
    public void testQueryOne() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        SqlSession session = sqlSessionFactory.openSession();
        try {
            BlogCommentMapper mapper = session.getMapper(BlogCommentMapper.class);
            BlogComment blog = mapper.selectBlogById(14);
            System.out.println(blog);
        } finally {
            session.close();
        }
    }

** 5. 结果**
插入结果

查询结果

三、TypeHandlerRegistry

Configuration配置类初始化的时候会去将MyBatis内置的TypeHandler注册到TypeHandlerRegitstry中,这些内置的TypeHandler都在MyBatis的type包下面

public class Configuration {
	// ....
  	protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);
  	// ....
}

TypeHandlerRegistry 构造函数

  public TypeHandlerRegistry(Configuration configuration) {
    this.unknownTypeHandler = new UnknownTypeHandler(configuration);

    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());

    register(Byte.class, new ByteTypeHandler());
    register(byte.class, new ByteTypeHandler());
    register(JdbcType.TINYINT, new ByteTypeHandler());

    register(Short.class, new ShortTypeHandler());
    register(short.class, new ShortTypeHandler());
    register(JdbcType.SMALLINT, new ShortTypeHandler());

    register(Integer.class, new IntegerTypeHandler());
    register(int.class, new IntegerTypeHandler());
    register(JdbcType.INTEGER, new IntegerTypeHandler());

    register(Long.class, new LongTypeHandler());
    register(long.class, new LongTypeHandler());

    register(Float.class, new FloatTypeHandler());
    register(float.class, new FloatTypeHandler());
    register(JdbcType.FLOAT, new FloatTypeHandler());

    register(Double.class, new DoubleTypeHandler());
    register(double.class, new DoubleTypeHandler());
    register(JdbcType.DOUBLE, new DoubleTypeHandler());

    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 StringTypeHandler());
    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 StringTypeHandler());
    register(JdbcType.NVARCHAR, new NStringTypeHandler());
    register(JdbcType.NCHAR, new NStringTypeHandler());
    register(JdbcType.NCLOB, new NClobTypeHandler());

    register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
    register(JdbcType.ARRAY, new ArrayTypeHandler());

    register(BigInteger.class, new BigIntegerTypeHandler());
    register(JdbcType.BIGINT, new LongTypeHandler());

    register(BigDecimal.class, new BigDecimalTypeHandler());
    register(JdbcType.REAL, new BigDecimalTypeHandler());
    register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
    register(JdbcType.NUMERIC, new BigDecimalTypeHandler());

    register(InputStream.class, new BlobInputStreamTypeHandler());
    register(Byte[].class, new ByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
    register(byte[].class, new ByteArrayTypeHandler());
    register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
    register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.BLOB, new BlobTypeHandler());

    register(Object.class, unknownTypeHandler);
    register(Object.class, JdbcType.OTHER, unknownTypeHandler);
    register(JdbcType.OTHER, unknownTypeHandler);

    register(Date.class, new DateTypeHandler());
    register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
    register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
    register(JdbcType.TIMESTAMP, new DateTypeHandler());
    register(JdbcType.DATE, new DateOnlyTypeHandler());
    register(JdbcType.TIME, new TimeOnlyTypeHandler());

    register(java.sql.Date.class, new SqlDateTypeHandler());
    register(java.sql.Time.class, new SqlTimeTypeHandler());
    register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());

    register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler());

    register(Instant.class, new InstantTypeHandler());
    register(LocalDateTime.class, new LocalDateTimeTypeHandler());
    register(LocalDate.class, new LocalDateTypeHandler());
    register(LocalTime.class, new LocalTimeTypeHandler());
    register(OffsetDateTime.class, new OffsetDateTimeTypeHandler());
    register(OffsetTime.class, new OffsetTimeTypeHandler());
    register(ZonedDateTime.class, new ZonedDateTimeTypeHandler());
    register(Month.class, new MonthTypeHandler());
    register(Year.class, new YearTypeHandler());
    register(YearMonth.class, new YearMonthTypeHandler());
    register(JapaneseDate.class, new JapaneseDateTypeHandler());

    // issue #273
    register(Character.class, new CharacterTypeHandler());
    register(char.class, new CharacterTypeHandler());
  }
四、自定义TypeHandler的解析注册

从上面我们可看出MyBatis为我们提供了很多内置的TypeHandler,为我们带来了很大的便利,接下来我们看一下我们自定义的TypeHandler在MyBatis中是如何进行解析注册的

当我们在使用SqlSessionFactoryBuilder的build方法 构建 SqlSessionFactory 的时候,会对Mybatis的核心配置文件进行解析

        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

在SqlSessionFactoryBuilder的build方法中会使用 XMLConfigBuilder 的 parse()方法对配置文件进行解析

public class SqlSessionFactoryBuilder {
	// ....
	  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
	//.....
}

在 parse()方法中,parseConfiguration()方法会选取configuration根标签开始解析

  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

typeHandlerElement()方法对typeHandlers 标签进行解析

  private void parseConfiguration(XNode root) {
    try {
      //....这里只看typeHandlers的解析
      typeHandlerElement(root.evalNode("typeHandlers"));
      //....
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

自定义TypeHandler的注册,在MyBatis中提供了typeHandler的两种配置方式,一种是通过package,

	
    
        
    

一种通过typeHandler

	
    
        
    
  private void typeHandlerElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          //读取配置的包路径
          String typeHandlerPackage = child.getStringAttribute("name");
          //将该包路径下的typeHandler都注册到TypeHandlerRegistry 中
          typeHandlerRegistry.register(typeHandlerPackage);
        } else {
          //读取各标签下的属性配置
          String javaTypeName = child.getStringAttribute("javaType");
          String jdbcTypeName = child.getStringAttribute("jdbcType");
          String handlerTypeName = child.getStringAttribute("handler");
          Class javaTypeClass = resolveClass(javaTypeName);
          JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
          Class typeHandlerClass = resolveClass(handlerTypeName);
          if (javaTypeClass != null) {
            if (jdbcType == null) {
              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
            } else {
              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
            }
          } else {
            typeHandlerRegistry.register(typeHandlerClass);
          }
        }
      }
    }
  }

当typeHandlers标签解析完成后,会将自定义的TypeHandler保存到 TypeHandlerRegistry 的 allTypeHandlersMap里面

我们已经知道MyBatis是如何解析typeHandler的了,那么在查询的时候,MyBatis是如何调用我们自定义的TypeHandler将结果集进行映射的呢?我们接着看
在MyBatis执行查询的时候会调用query()方法在query()方法中调用execute()方法获取到查询结果集之后,会使用ResultSetHandler的handleResultSets()方法进行结果集的转换

  @Override
  public  List query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleResultSets(ps);
  }
@Override
  public List handleResultSets(Statement stmt) throws SQLException {
	// 。。。。
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      //处理结果集
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
	// 。。。。
  }
 

在handleResultSet() 方法中调用了handleRowValue() 方法处理行数据

  private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List multipleResults, ResultMapping parentMapping) throws SQLException {
    try {
      if (parentMapping != null) {
        handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
      } else {
        if (resultHandler == null) {
          DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
          //处理行数据,将数据库中的行数据和Java对象的属性进行映射赋值处理
          handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
          multipleResults.add(defaultResultHandler.getResultList());
        } else {
          handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
        }
      }
    } finally {
      // issue #228 (close resultsets)
      closeResultSet(rsw.getResultSet());
    }
  }
 
  public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
	  //....
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
   	  //....
  }
  private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
	  //....
      Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
	  //....
    }
  }
  private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
	  //....
      foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
      //....
    }
    return rowValue;
  }
  private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, metaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
		//....
        Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
		//....
  }

最后在获取属性映射值的时候,调用了typeHandler.getResult(rs, column);方法,这里返回的typeHandler就是我们在xml中配置的typeHandler的实例,这里我们断点看一下

  private Object getPropertyMappingValue(ResultSet rs, metaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
      throws SQLException {
    if (propertyMapping.getNestedQueryId() != null) {
      return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
    } else if (propertyMapping.getResultSet() != null) {
      addPendingChildRelation(rs, metaResultObject, propertyMapping);   // TODO is that OK?
      return DEFERRED;
    } else {
      final TypeHandler typeHandler = propertyMapping.getTypeHandler();
      final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
      return typeHandler.getResult(rs, column);
    }
  }


最后调用我们自己实现的CommentTypeHandler 中的 getNullableResult() 对结果集进行转换

public class CommentTypeHandler extends baseTypeHandler {
    @Override
    public Comment getNullableResult(ResultSet resultSet, String columnName) throws SQLException {
        if(StringUtils.isEmpty(resultSet.getString(columnName))){
            return null;
        }
        return JSONObject.parseObject(resultSet.getString(columnName), Comment.class);
    }
}
 

以上就是对typeHandlers标签的简单解析了,希望对你有点帮助!

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号