MyBatis自定义TypeHandler的getResult方法未触发问题排查
问题:MyBatis自定义TypeHandler的getNullableResult方法未触发,列表字段无法正确映射
我知道复杂嵌套的实体结构会提升开发复杂度,但当前业务场景下必须解决这个问题。我的实体存在对象嵌套和对象列表的结构,具体如下:
实体定义
基础实体BaseEntity
@Data public class BaseEntity { private Long id; private String name; private String description; }
嵌套实体ImpactEntity
@Data public class ImpactEntity implements Structable{ private String ref; private String ASOF; private Long deliverTime; private String SLAMissed; private BaseEntity client; private BaseEntity application; private BaseEntity deliverable; }
最终实体IncidentEntity
@Data public class IncidentEntity { private String PID; private String ID; private String NID; private String REF; private String RREF; private String ASOF; private Long StartTime; private Long endTime; private String description; private String active; private Long createAt; private List<ImpactEntity> impacts; }
MyBatis Mapper配置
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="org.well.valley.mapper.IncidentMapper" > <resultMap id="IncidentEntity" type="org.well.valley.entity.IncidentEntity"> <result column="PID" jdbcType="VARCHAR" property="PID" /> <result column="ID" jdbcType="VARCHAR" property="ID" /> <result column="NID" jdbcType="VARCHAR" property="NID" /> <result column="REF" jdbcType="VARCHAR" property="REF" /> <result column="RREF" jdbcType="VARCHAR" property="RREF" /> <result column="ASOF" jdbcType="VARCHAR" property="ASOF" /> <result column="STARTTIME" jdbcType="INTEGER" property="startTime" /> <result column="ENDTIME" jdbcType="INTEGER" property="endTime" /> <result column="DESCRIPTION" jdbcType="VARCHAR" property="description" /> <result column="ACTIVE" jdbcType="VARCHAR" property="active" /> <result column="CREATEAt" jdbcType="INTEGER" property="createAt" /> <result property="impacts" column="IMPACTS" typeHandler="org.well.valley.utils.typehandlers.OracleTypeHandler" /> </resultMap> <select id="get" statementType="CALLABLE" parameterType="org.well.valley.entity.IncidentEntity"> call get_incidents( #{ASOF, jdbcType=VARCHAR}, #{ref, jdbcType=VARCHAR}, #{active, jdbcType=VARCHAR}, #{outParam.c1, mode=OUT, jdbcType=CURSOR, javaType=java.sql.ResultSet, resultMap=IncidentEntity} ) </select> </mapper>
自定义OracleTypeHandler实现
我写了一个通用型的OracleTypeHandler来处理impacts字段,计划支持包括List在内的结构(目前List的处理逻辑还未实现):
public class OracleTypeHandler<T> extends BaseTypeHandler<T> { private Logger logger = LoggerFactory.getLogger(getClass()); private ObjectMapper objectMapper = new ObjectMapper(); private DBMapperBean getDBMapperBean() { return SpringContext.getBean(DBMapperBean.class); } private Class<T> type; protected int getIndex(ResultSetMetaData data, String columnName) throws SQLException { logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>11111111111111111111111111"); for (int i = 1; i <= data.getColumnCount(); i++) { String c = data.getColumnName(i); if(columnName.equals(c)) { return i; } } return -1; } public OracleTypeHandler(Class<T> type) { if (type == null) { throw new IllegalArgumentException("Type argument cannot be null"); } this.type = type; logger.info(">>>>>>>>>>>>>>>>>>>>>>Type is:{}", type); } @Override public void setNonNullParameter(PreparedStatement ps, int index, T parameter, JdbcType jdbcType) throws SQLException { logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>333333333333333333333333"); Connection conn = ps.getConnection(); if(parameter.getClass().isArray()) { if(parameter instanceof String[]) { Array array = ((OracleConnection) conn).createOracleArray("STRINGARRAY", parameter); ps.setArray(index, array); array.free(); } } } @Override public T getNullableResult(ResultSet resultSet, String columnName) throws SQLException { ResultSetMetaData metaData = resultSet.getMetaData(); logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>444444444444444444444444444444"); int index = getIndex(metaData, columnName); String columnClassName = metaData.getColumnClassName(index); if(columnClassName.equals("oracle.jdbc.OracleStruct")) { java.sql.Struct o = (java.sql.Struct)resultSet.getObject(index); return extractObject(o); }else if(columnClassName.equals("oracle.jdbc.OracleArray")) { Array array = resultSet.getArray(columnName); Object r = array.getArray(); array.free(); return (T) r; } return null; } private String typeNameConvert(String type, String target) { if(target.equals("DBTYPE")) { return type.concat("TYPE"); }else { return type.substring(0, type.length()-4); } } private T extractObject(Struct o) throws SQLException { String typeName = o.getSQLTypeName(); typeName = typeName.substring(typeName.lastIndexOf(".")+1);DBMapperBean dbMapperBean = getDBMapperBean(); typeName = typeNameConvert(typeName, "JAVATYPE"); if(typeName.endsWith("ARRAY")) { switch (typeName){ case "STRINGARRAY": break; } }else { HashMap <String, Class> map = dbMapperBean.getMap(); try { Class<?> cls = (Class<?>) map.get(typeName); Structable obj = (Structable) cls.getDeclaredConstructor().newInstance(); Object[] attrs = o.getAttributes(); List<Field> fields = obj.getOrderedField(obj.getClass().getDeclaredFields()); int i = 0; for (Field f : fields) { f.setAccessible(true); Object source = attrs[i]; if(source == null) { f.set(obj, null); } else { Class targetType = f.getType(); Class sourceType = source.getClass(); if (Number.class.isAssignableFrom(targetType) && Number.class.isAssignableFrom(sourceType)) { if (targetType == Long.class) { source = Long.parseLong(source.toString()); } } try { f.set(obj, source); } catch (IllegalAccessException e) { e.printStackTrace(); } } i++; } return (T) obj; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } return null; } @Override public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException { logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>66666666666666666"); Struct struct =(java.sql.Struct) rs.getObject(columnIndex); return extractObject(struct); } @Override public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7777777777777777777"); Struct struct =(java.sql.Struct) cs.getObject(columnIndex); return extractObject(struct); } }
现象与疑问
调用存储过程前,OracleTypeHandler的构造函数已被正常调用,日志输出如下:
19:58:28.439 INFO o.w.v.u.t.OracleTypeHandler - >>>>>>>>>>>>>>>>>>>>>>Type is:class org.well.valley.entity.BaseEntity 19:58:33.692 INFO o.w.v.u.t.OracleTypeHandler - >>>>>>>>>>>>>>>>>>>>>>Type is:class org.well.valley.entity.BaseEntity 20:02:43.477 INFO o.w.v.u.t.OracleTypeHandler - >>>>>>>>>>>>>>>>>>>>>>Type is:class org.well.valley.entity.BaseEntity 20:02:47.161 INFO o.w.v.u.t.OracleTypeHandler - >>>>>>>>>>>>>>>>>>>>>>Type is:class org.well.valley.entity.BaseEntity 20:03:00.370 INFO o.w.v.u.t.OracleTypeHandler - >>>>>>>>>>>>>>>>>>>>>>Type is:class [Ljava.lang.String; 20:03:16.080 INFO o.w.v.u.t.OracleTypeHandler - >>>>>>>>>>>>>>>>>>>>>>Type is:interface java.util.List
但调用存储过程后,TypeHandler中的三个getNullableResult方法均未被触发——我原本期望通过列名获取结果的getNullableResult(ResultSet resultSet, String columnName)会被执行。目前impacts返回空列表,但直接调用数据库存储过程能得到正确结果。
想请教:
getNullableResult方法未触发的原因是什么?- MyBatis此时使用哪个TypeHandler处理
impacts列表?
内容的提问来源于stack exchange,提问作者Ginger_Chacha
相关产品推荐
相关产品推荐

