Mybatis读取和存储json类型的数据

目录

    • 一、测试使用JSONObject来获取json
    • 二、设置@TableName的autoResultMap为true,@TableField的typeHandler为JacksonTypeHandler.class
    • 三、设置xml当中的resultMap
    • 四、JacksonTypeHandler讲解
    • 五、新增假如是JSONObject异常问题
    • 六、遇到转义的问题

      不管数据库当中是以json还是longtext数据类型来存json,都可以在mybatis当中使用string来接数据。这一点毋庸置疑!但是想要使用JSONObject类型的字段来取值是否可以呢?

      一、测试使用JSONObject来获取json

      接下来我们来测试一下,我用的是mybatis-plus框架,mybatis-plus和mybatis是一样的,无非就是mybatis-plus封装好了一些crud方法。但是对于手写xml来说两个框架是一样的。

      实体类如下:这里的JSONObject我用的hutool工具包的,JSONObject一般引用的json框架都有

      测试接口如下:这里一共写了两个接口,一个接口是手写的,一个是调用的mybatis-plus当中提供的BaseMapper的selectList方法

      查询结果如下:假如想要使用JSONObject来映射数据库当中的json数据,不做任何配置是取不到的。

      这个是访问mybatis-plus当中提供的查询方法

      得出结论:在不做任何配置的情况下,不管是手写的xml接口还是用mybatis-plus自带的查询接口,都是无法将json数据映射到JSONObject类型的字段当中的。

      二、设置@TableName的autoResultMap为true,@TableField的typeHandler为JacksonTypeHandler.class

      两个接口测试如下:调整过后,mybatis-plus当中自带的接口是可以将json数据映射到JSONObject类型的字段当中的(不管是longtext类型存储的json还是json类型存储的json数据)

      对于mybatis-plus框架我们将@TableName的autoResultMap为true,然后@TableField的typeHandler为JacksonTypeHandler.class之后,调用mybatis-plus自带的查询接口是可以将json数据映射到JSONObject类型的字段当中的。

      注意:如果@TableName的autoResultMap不设置为true,那么设置typeHandler不会生效

      三、设置xml当中的resultMap

      使用mybatis,有两个属性标签可以提供结果映射。

      虽然resultType 属性在大部分情况下都够用,但是在一些特殊情况下无能为力,比如属性名和列名不一致,为一些连接的复杂语句编写映射代码。遇到这些情况,我们要使用标签,一份 能够代替实现同等功能的数千行代码。

      resultMap 元素是 MyBatis 中最重要最强大的元素。resultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

      注意:这里去掉了属性,用代替,二者只能选择其中的一个。

      其实在上面设置@TableName的autoResultMap为true,@TableField的typeHandler为JacksonTypeHandler.class等同于在xml当中配置resultMap的某个属性使用JacksonTypeHandler。只不过mybatis-plus这块使用的是注解的形式,而mybatis应该是没有注解方式的。所以他只能用以下方式:

               

      查询结果如下:

      四、JacksonTypeHandler讲解

      JacksonTypeHandler是mybatis-plus提供的,不管是使用注解方式还是使用xml方式当中都使用到了JacksonTypeHandler类。那么这个类到低是干什么的?假如我使用的是mybatis而并不是mybatis-plus该怎么办?

      JacksonTypeHandler就是专门用来做数据映射转换的。是mybatis-plus的,但是实际上继承的是BaseTypeHandler,而BaseTypeHandler是mybatis的,那么我们也就可以基于BaseTypeHandler来自己写一个。

      import com.fasterxml.jackson.databind.ObjectMapper;
      import org.apache.ibatis.type.BaseTypeHandler;
      import org.apache.ibatis.type.JdbcType;
      import java.sql.CallableStatement;
      import java.sql.PreparedStatement;
      import java.sql.ResultSet;
      import java.sql.SQLException;
      public class JsonTypeHandler extends BaseTypeHandler { private static final ObjectMapper mapper = new ObjectMapper();
          private Class clazz;
          public JsonTypeHandler(Class clazz) { if (clazz == null) throw new IllegalArgumentException("Type argument cannot be null");
              this.clazz = clazz;
          }
          @Override
          public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, this.toJson(parameter));
          }
          @Override
          public T getNullableResult(ResultSet rs, String columnName) throws SQLException { return this.toObject(rs.getString(columnName), clazz);
          }
          @Override
          public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return this.toObject(rs.getString(columnIndex), clazz);
          }
          @Override
          public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return this.toObject(cs.getString(columnIndex), clazz);
          }
          private String toJson(T object) { try { return mapper.writeValueAsString(object);
              } catch (Exception e) { throw new RuntimeException(e);
              }
          }
          private T toObject(String content, Class clazz) { if (content != null && !content.isEmpty()) { try { // 核心转换,将数据库读取的字符串,转换为指定的class类型
                      return (T) mapper.readValue(content, clazz);
                  } catch (Exception e) { throw new RuntimeException(e);
                  }
              } else { return null;
              }
          }
      }
      

      五、新增假如是JSONObject异常问题

      而对于 typeHandler 属性,MyBatis 只支持写在 2 个地方:

      • 定义在 resultMap 里,作用于查询结果的封装
      • 定义在 insert 和 update 语句的 #{property} 中的 property后面

        (例:#{property,typehandler=xxx.xxx.xxx}),并且只作用于当前 设置值

        假如不配置是会报错的!

        六、遇到转义的问题

        在使用JSONObject作为Java对象的类型存值和取值是可以原样返回的:

        那假如是使用的String存值和取值会发生什么样的场景呢?

        首先使用String来直接接json对象肯定是不可以的,直接会报400异常。

        针对于这个问题有两种方案,一种是对json建立Java对象,还有一种是直接使用JSONObject

        假如使用字符串来查询会出现转义的问题

        遇到这个问题可以使用commons-lang3包下的StringEscapeUtils.unescapeJava进行转换一下

        查询结果如下: