【Mybatis】用TypeHandler将数据库中存储的json字符串处理为对象,包括对象含List以及复杂对象的情况, 并满足泛型可转成多种对象

需求:将数据库存储的原始json字符串取出来解析成对象以便后续的数据处理。

一般方法:由于json在数据库是以字符串方式存储可以,可以先取出到字符串再挨个进行解析,但当字段比较多,且大概率会出现空字段的情况,代码可能略为繁琐,当然这样也行,那就不必再看下去了。

另一种方法:很常见的就会使用TypeHandler,网上示例也比较多,不过真要深入下去还是得看官方文档,题目上的问题就是从官方文档中找到答案的。

首先是一般情况下json转对象的适应多情况的泛型写法,如果对象没有List这种,用这个就可以了。

import cn.transform.datatype.*;
import cn.transform.dto.DTOFhirPatient;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

//以下这行注释必须要有,否则会报SqlSessionFactory的异常错误,value后面写json要转的对象的类型,用,隔开
@MappedTypes(value = {Identifier.class,HumanName.class,ContactPoint.class,Address.class,Attachment.class,PatientContact.class,PatientCommunication.class,Reference.class,PatientLink.class,CodeableConcept.class})
@MappedJdbcTypes(value = {JdbcType.VARCHAR},includeNullJdbcType = true)
public class JsonTypeHandler<T extends Object>extends BaseTypeHandler<T> {
    private static final ObjectMapper MAPPER = new ObjectMapper();
    private Class<T> clazz;

    //这个构造器是用来获取类型的,如果是泛型的typehandler必须要写
    public JsonTypeHandler(Class<T> 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,toJson(parameter));
    }

    @Override
    public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
        if(columnName==null||columnName.isEmpty()){
            return null;
        }
        return (T) toObject(rs.getString(columnName),clazz);
    }

    @Override
    public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return (T) toObject(rs.getString(columnIndex),clazz);
    }

    @Override
    public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return (T) 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 {
                return (T) MAPPER.readValue(content, clazz);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } else {
            return null;
        }
    }
}

上面这个代码是我从网上搜来的,加了两个注释后终于不报错了。

接下来是我在上面代码的基础上,结合官(da)方(lao)文(zhi)档(dian)改出来的json转集合的泛型typehandler

import cn.transform.datatype.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.type.*;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

@MappedTypes(value = {Identifier.class,HumanName.class,ContactPoint.class,Address.class,Attachment.class,PatientContact.class,PatientCommunication.class,Reference.class,PatientLink.class})
@MappedJdbcTypes(value = {JdbcType.VARCHAR},includeNullJdbcType = true)
public class ArrayListTypeHandler<T extends Object> extends BaseTypeHandler<List<T>> {
    private static final ObjectMapper MAPPER = new ObjectMapper();
    private Class<List<T>> clazz;

    public ArrayListTypeHandler(Class<List<T>> clazz) {
        if(clazz == null){
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        this.clazz=clazz;
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, List<T> parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i,toJson(parameter));
    }

    @Override
    public List<T> getNullableResult(ResultSet rs, String columnName) throws SQLException {
//特别要注意在转集合的情况下,第二个参数是List.class,而不是clazz会报奇怪的错误,我在这被坑了老久
        return toObject(rs.getString(columnName),List.class);
    }

    @Override
    public List<T> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return toObject(rs.getString(columnIndex),List.class);
    }

    @Override
    public List<T> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return toObject(cs.getString(columnIndex),List.class);
    }

    private String toJson(List<T> object) {
        try {
            return MAPPER.writeValueAsString(object);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private List<T> toObject(String content, Class<?> clazz) {
        if (content != null && !content.isEmpty()) {
            try {
                return (List<T>) MAPPER.readValue(content, clazz);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } else {
            return null;
        }
    }
}

附上Mapper.xml里的部分代码

<resultMap id="HandlerResultMap" type="cn.transform.dto.DTOFhirPatient" >
        <id column="id" property="id" jdbcType="INTEGER" />
        <result column="patient_id" property="patientId" jdbcType="VARCHAR" />
        <result column="upload_time" property="uploadTime" jdbcType="TIMESTAMP" />
        <result column="update_time" property="updateTime" jdbcType="TIMESTAMP" />
        <result column="operator" property="operator" jdbcType="VARCHAR" />
        <result column="reason_void" property="reasonVoid" jdbcType="VARCHAR" />
        <result column="version" property="version" jdbcType="INTEGER" />
        <!-- 当要转的对象是List时,后面typeHandler就用ArrayListTypeHandler,javaType是可以不用加,这是我死马当活马医时乱加上去的-->
        <result column="identifier" property="identifier" jdbcType="VARCHAR" javaType="cn.transform.datatype.Identifier" typeHandler="cn.transform.handler.ArrayListTypeHandler"/>
        <!--<collection property="identifier" resultMap="IdentifierResultMap"/>-->
        <result column="active" property="active" jdbcType="BIT" />
        <result column="name" property="name" jdbcType="VARCHAR" javaType="cn.transform.datatype.HumanName" typeHandler="cn.transform.handler.ArrayListTypeHandler"/>
        <result column="telecom" property="telecom" jdbcType="VARCHAR" javaType="cn.transform.datatype.ContactPoint" typeHandler="cn.transform.handler.ArrayListTypeHandler"/>
        <result column="gender" property="gender" jdbcType="VARCHAR" />
        <result column="birth_date" property="birthDate" jdbcType="VARCHAR" />
        <result column="deceased_boolean" property="deceasedBoolean" jdbcType="BIT" />
        <result column="deceased_dateTime" property="deceasedDatetime" jdbcType="VARCHAR" />
        <result column="address" property="address" jdbcType="VARCHAR" javaType="cn.transform.datatype.Address" typeHandler="cn.transform.handler.ArrayListTypeHandler"/>
        <!--当你要转的不是集合时用JsonTypeHandler即可-->
        <result column="marital_status" property="maritalStatus" jdbcType="VARCHAR" javaType="cn.transform.datatype.CodeableConcept" typeHandler="cn.transform.handler.JsonTypeHandler"/>
        <result column="multiple_birth_boolean" property="multipleBirthBoolean" jdbcType="BIT" />
        <result column="multiple_birth_integer" property="multipleBirthInteger" jdbcType="INTEGER" />
        <result column="photo" property="photo" jdbcType="VARCHAR" javaType="cn.transform.datatype.Attachment" typeHandler="cn.transform.handler.ArrayListTypeHandler"/>
        <result column="contact" property="contact" jdbcType="VARCHAR" javaType="cn.transform.datatype.ContactPoint" typeHandler="cn.transform.handler.ArrayListTypeHandler"/>
        <result column="communication" property="communication" jdbcType="VARCHAR" javaType="cn.transform.datatype.PatientCommunication" typeHandler="cn.transform.handler.ArrayListTypeHandler"/>
        <result column="general_practitioner" property="generalPractitioner" jdbcType="VARCHAR" javaType="cn.transform.datatype.Reference" typeHandler="cn.transform.handler.ArrayListTypeHandler"/>
        <result column="managing_organization" property="managingOrganization" jdbcType="VARCHAR" javaType="cn.transform.datatype.Reference" typeHandler="cn.transform.handler.JsonTypeHandler"/>
        <result column="link" property="link" jdbcType="VARCHAR" javaType="cn.transform.datatype.PatientLink" typeHandler="cn.transform.handler.ArrayListTypeHandler"/>
    </resultMap>

<select id="selectJsonByPatientId" parameterType="string" resultMap="HandlerResultMap">
        select
        <include refid="Base_Column_List"/>
        from fhir_patient_info
        where patient_id = #{patientId,jdbcType=VARCHAR}
    </select>

听说application.yml里也要配置下,不知道不配会发生啥


# mybatis 配置
mybatis:
  mapper-locations: classpath*:/**/**/*Mapper.xml
  type-aliases-package: cn.*.model
  config-location: classpath:mybatis.xml

对了,如果要转换的对象或集合里是复杂类型,对象的属性还是对象这种, 就会出现处理后的对象取出来的属性都是linkedhashmap类型.

因为在typehandler中用的是jackson, 所以基于Jackson的性质会把其变成LinkedHashMap,毕竟Jackson也不知道你要解析成啥样。又因为TypeReference里不能加泛型,所以我只能在typehandler用那个class的方法, 否则是可以用TypeReference指定复杂对象。当然如果你只是单纯想转一种复杂类型那也就不必麻烦泛型了,typereference也能派上用场就方便多了。

回到正题,解决linkedhashmap这种,只能解铃还需系铃人,用jackson的convertValue

//POJO为要转的类型
POJO pojo=objectMapper.convertValue(复杂对象里的linkedhashmap字符串,new TypeReference<POJO>(){});

 在用的时候多转一步,但感觉就失去了用typehandler的初衷,不过linkedHashMap直接用也不是不行。

 

 

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
要将MyBatisJSON字符串转换为对象,你需要进行以下步骤: 1. 首先,在你的实体类定义对应的属性,确保属性的类型与数据库存储JSON字符串所表示的类型相匹配。在你的实体类,你应该有一个名为limiting的属性,类型为Object。 2. 然后,在你的实体类的构造函数,将limiting参数的类型设置为Object,而不是ParameterLimiting。这是因为在数据库存储的是JSON字符串,而不是具体的ParameterLimiting对象。 3. 接下来,创建一个类型处理器(TypeHandler)来完成从JSON字符串对象的转换。你可以继承BaseTypeHandler类,并指定泛型为ParameterLimiting。在该类型处理,你需要实现一些方法,例如setNonNullParameter用于将对象转换为JSON字符串并写入数据库,getNullableResult用于从数据库读取JSON字符串并将其转换为对象。在setNonNullParameter方法,你可以使用JSON.toJSONString方法将对象转换为JSON字符串,并调用PreparedStatement的setObject方法将其写入数据库。 4. 最后,在你的MyBatis的配置文件,将该类型处理器注册为对应属性的类型处理器。你需要在<mappers>标签下添加一个<typeHandlers>标签,并在其配置你的类型处理器。在配置,你需要指定对应属性的Java类型以及该属性在数据库的JDBC类型。 总结起来,将MyBatisJSON字符串转换为对象的过程包括:在实体类定义属性,修改构造函数,创建类型处理器,并在配置文件注册该类型处理器。这样,在查询结果集时,MyBatis就会自动将数据库JSON字符串转换为对应的对象
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值