【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 JsonTypeHandlerextends BaseTypeHandler {
    private static final ObjectMapper MAPPER = new ObjectMapper();
    private Class clazz;

    //这个构造器是用来获取类型的,如果是泛型的typehandler必须要写
    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,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 extends BaseTypeHandler> {
    private static final ObjectMapper MAPPER = new ObjectMapper();
    private Class> clazz;

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

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

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

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

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

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

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

附上Mapper.xml里的部分代码


        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
    


听说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(){});

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

 

 

你可能感兴趣的:(网络与网站)