需求:将数据库存储的原始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直接用也不是不行。