MyBatis如何使用泛型JsonTypeHandler按type值将JSON字段映射为不同对象
问题根因
- 你当前自定义的
JsonTypeHandler仅提供带Class<T>参数的构造方法,MyBatis默认调用无参构造实例化TypeHandler,导致泛型实际类型丢失,Jackson反序列化时没有指定目标类,默认返回Map类型 - 你需要根据
type字段动态匹配不同的反序列化目标类,固定配置的BaseResultMap无法实现动态类型绑定
修复方案
步骤1:调整JsonTypeHandler
补充无参构造,兼容MyBatis的实例化逻辑:
public class JsonTypeHandler <T> extends BaseTypeHandler<T> { private static final Logger LOGGER = LoggerFactory.getLogger(JsonTypeHandler.class); private static final ObjectMapper mapper = new ObjectMapper(); private Class<T> clazz; static { mapper.configure(JsonParser.Feature.ALLOW_MISSING_VALUES, false); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } // 新增无参构造,供MyBatis反射实例化时使用 public JsonTypeHandler() { // 获取泛型实际类型 Type superClass = getClass().getGenericSuperclass(); if (superClass instanceof ParameterizedType) { this.clazz = (Class<T>) ((ParameterizedType) superClass).getActualTypeArguments()[0]; } } // 保留原有带Class参数的构造 public JsonTypeHandler(Class<T> clazz) { if (clazz == null) { throw new NullPointerException("Type argument cannot be null"); } this.clazz = clazz; } // 原有toJSON、toObject、参数设置、结果获取方法保持不变,直接复用你现有代码即可 private String toJSON(T object) { try { String string = mapper.writeValueAsString(object); LOGGER.info(">>>> json handler string:{} <<<<",string); return string; } catch (Exception e) { LOGGER.error(">>>> covert object to json string failed, error message: <<<<",e); } return null; } private T toObject(String json, Class<T> clazz) throws IOException { if (json != null && !json.isEmpty()) { return mapper.readValue(json,clazz); } return null; } @Override public void setNonNullParameter(PreparedStatement preparedStatement, int i, T t, JdbcType jdbcType) throws SQLException { try { preparedStatement.setString(i,toJSON(t)); } catch (Exception e) { LOGGER.error(">>>> preparedStatement set string failed, error message:{} <<<<",e.getMessage()); } } @Override public T getNullableResult(ResultSet resultSet, String s) throws SQLException { try { return toObject(resultSet.getString(s), clazz); } catch (IOException e) { LOGGER.error(">>>> convert json string to object failed, error message:{} <<<<",e.getMessage()); } return null; } @Override public T getNullableResult(ResultSet resultSet, int i) throws SQLException { try { return toObject(resultSet.getString(i), clazz); } catch (IOException e) { LOGGER.error(">>>> convert json string to object failed, error message:{} <<<<",e.getMessage()); } return null; } @Override public T getNullableResult(CallableStatement callableStatement, int i) throws SQLException { try { return toObject(callableStatement.getString(i), clazz); } catch (IOException e) { LOGGER.error(">>>> convert json string to object failed, error message:{} <<<<",e.getMessage()); } return null; } }
步骤2:修改Mapper ResultMap配置,添加鉴别器
用MyBatis的discriminator鉴别器根据type字段值,动态绑定不同的反序列化类型:
<mapper namespace="com.example.demo.dao.mapper.TblTestJsonMapper"> <resultMap id="BaseResultMap" type="com.example.demo.dao.po.TestJsonPO"> <id property="id" column="id" jdbcType="BIGINT"/> <result property="type" column="type" jdbcType="INTEGER"/> <!-- 配置鉴别器,根据type值匹配不同的resultMap --> <discriminator javaType="int" column="type"> <case value="1" resultMap="Type1ResultMap"/> <case value="2" resultMap="Type2ResultMap"/> </discriminator> </resultMap> <!-- type=1时的结果映射,指定extInfo反序列化为TestJsonExtInfoPO --> <resultMap id="Type1ResultMap" type="com.example.demo.dao.po.TestJsonPO" extends="BaseResultMap"> <result property="extInfo" column="ext_info" javaType="com.example.demo.dao.po.TestJsonExtInfoPO" jdbcType="VARCHAR" typeHandler="com.example.demo.JsonTypeHandler"/> </resultMap> <!-- type=2时的结果映射,指定extInfo反序列化为TestJsonExtInfoPO2 --> <resultMap id="Type2ResultMap" type="com.example.demo.dao.po.TestJsonPO" extends="BaseResultMap"> <result property="extInfo" column="ext_info" javaType="com.example.demo.dao.po.TestJsonExtInfoPO2" jdbcType="VARCHAR" typeHandler="com.example.demo.JsonTypeHandler"/> </resultMap> <!-- 原有sql、insert、select配置保持不变 --> <sql id="allCols"> `id` ,`type`,ext_info </sql> <insert id="insert" parameterType="com.example.demo.dao.po.TestJsonPO"> INSERT INTO tbl_test_json (`type`, ext_info) values (#{type}, #{extInfo,typeHandler=com.example.demo.JsonTypeHandler}) </insert> <select id="selectByType" resultMap="BaseResultMap"> select <include refid="allCols"/> from tbl_test_json where `type` = #{type} limit 1 </select> </mapper>
步骤3:测试验证
你的原有测试代码无需修改,查询结果的extInfo就会自动匹配对应类型:
@Resource private TblTestJsonMapper<TestJsonExtInfoPO> tblTestJsonMapper1; @Resource private TblTestJsonMapper<TestJsonExtInfoPO2> tblTestJsonMapper2; public void test() { // type=1时extInfo为TestJsonExtInfoPO类型 TestJsonPO<TestJsonExtInfoPO> result1 = tblTestJsonMapper1.selectByType(1); // type=2时extInfo为TestJsonExtInfoPO2类型 TestJsonPO<TestJsonExtInfoPO2> result2 = tblTestJsonMapper2.selectByType(2); }
内容的提问来源于stack exchange,提问作者HongYi
相关产品推荐
相关产品推荐

