Spring Boot升级至1.3.0后JPA AttributeConverter空指针错误排查
Spring Boot 1.3.0.RELEASE升级后JPA AttributeConverter报错排查
背景
原Spring Boot 1.2.1.RELEASE版本中,使用JPA的AttributeConverter将Map<String, Object>转换为JSON字符串存入数据库可正常工作,升级到1.3.0.RELEASE后,添加包含metaDataAttribute字段的请求时触发内部服务器错误。
需求场景
需将以下JSON格式数据存入实体类对应字段:
metadata: { masked: true, maxLength: 200, formula: "a + b", parentCode: "strAttr_1", currency: "$", }
相关代码
实体类字段定义
@Column(name = "metadata_json",columnDefinition = "json") @Convert(converter = HashMapConverter.class) private Map<String,Object> metaDataAttribute;
HashMapConverter转换器代码
package in.nobroker.hood.crm.entity; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import javax.persistence.AttributeConverter; import java.io.IOException; import java.util.Map; @Slf4j public class HashMapConverter implements AttributeConverter<Map<String, Object>, String> { private final ObjectMapper objectMapper = new ObjectMapper(); @Override public String convertToDatabaseColumn(Map<String, Object> stringObjectMap) { String attributeInfoJson = null; try { attributeInfoJson = objectMapper.writeValueAsString(stringObjectMap); } catch (final JsonProcessingException e) { log.error("JSON writing error", e); } return attributeInfoJson; } @Override public Map<String, Object> convertToEntityAttribute(String s) { Map<String, Object> attributeInfo = null; try { attributeInfo = objectMapper.readValue(s, Map.class); } catch (final IOException e) { log.error("JSON reading error", e); } return attributeInfo; } }
请求示例(Postman发送)
{ "label": "dateAttr_5", "id": "f73434ba-bfaa-49f5-9a41-b86715a985f2", "filter": true, "inUse": true, "editable": true, "mandatory": true, "priority": 0, "textOptions": "a,b,c,d", "type": "DATETIME_FORM_DATA", "filterType": "ABSOLUTE", "metaDataAttribute": { "masked": true, "maxLength": 200, "formula": "a + b", "parentCode": "strAttr_1", "currency": "$" } }
错误日志
argument "content" is null failed to fetch attribute label mappings argument "content" is null at com.fasterxml.jackson.databind.ObjectMapper._assertNotNull(ObjectMapper.java:4693) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3401) at in.nobroker.hood.crm.entity.HashMapConverter.convertToEntityAttribute(HashMapConverter.java:31) at in.nobroker.hood.crm.entity.HashMapConverter.convertToEntityAttribute(HashMapConverter.java:10) at org.hibernate.metamodel.model.convert.internal.JpaAttributeConverterImpl.toDomainValue(JpaAttributeConverterImpl.java:45) at org.hibernate.type.descriptor.converter.AttributeConverterSqlTypeDescriptorAdapter$2.doConversion(AttributeConverterSqlTypeDescriptorAdapter.java:140) ... 186 common frames omitted
原因分析
错误根源是HashMapConverter的convertToEntityAttribute方法接收到的参数s为null,直接传入Jackson的readValue方法触发非空校验异常。
Spring Boot 1.3.0.RELEASE对应的Hibernate版本较1.2.1升级后,对AttributeConverter的null值处理逻辑发生变化:旧版本中数据库字段为null时,会跳过转换器直接返回null;新版本则会调用转换器并传入null,而当前转换器未处理null参数的情况,导致报错。
解决办法
修改HashMapConverter,在两个转换方法中先对null参数进行判断处理:
@Slf4j public class HashMapConverter implements AttributeConverter<Map<String, Object>, String> { private final ObjectMapper objectMapper = new ObjectMapper(); @Override public String convertToDatabaseColumn(Map<String, Object> stringObjectMap) { if (stringObjectMap == null) { return null; } try { return objectMapper.writeValueAsString(stringObjectMap); } catch (final JsonProcessingException e) { log.error("JSON writing error", e); return null; } } @Override public Map<String, Object> convertToEntityAttribute(String s) { if (s == null || s.isEmpty()) { return null; // 或返回new HashMap<>(),根据业务需求选择 } try { return objectMapper.readValue(s, Map.class); } catch (final IOException e) { log.error("JSON reading error", e); return null; } } }
额外优化(可选)
如果需要更严谨的类型转换,可将Map.class替换为TypeReference,避免Jackson将数值等类型转为非预期的LinkedHashMap:
return objectMapper.readValue(s, new TypeReference<Map<String, Object>>() {});
内容的提问来源于stack exchange,提问作者Durgesh Soni
相关产品推荐
相关产品推荐

