iBATIS迁移MyBatis时discriminator遇null实例化抽象类问题
MyBatis 3.5.14 discriminator处理null值时实例化抽象类的问题及解决方案
问题背景
从iBATIS迁移至MyBatis 3.5.14时,在resultMap的discriminator使用场景中遇到异常:当discriminator对应的列值为null时,MyBatis会尝试实例化resultMap的type属性指定的抽象类,触发java.lang.InstantiationException(抽象类无法实例化),而该场景在iBATIS中可正常处理。
示例代码
ResultMap配置
<resultMap id="Sample1Result" type="com.test.AbstractSample"> <discriminator javaType="int" column="sample1_type"> <case value="52" resultMap="SampleImpl1Result"/> <case value="53" resultMap="OtherSampleImpl1Result"/> </discriminator> </resultMap> <resultMap id="Sample2Result" type="com.test.AbstractSample"> <discriminator javaType="int" column="sample2_type"> <case value="52" resultMap="SampleImpl2Result"/> <case value="53" resultMap="OtherSampleImpl2Result"/> </discriminator> </resultMap> <resultMap id="AugmentedSampleResult" type="Sample" extends="BaseSample"> ... <collection property="sample1" resultMap="Sample1Result"/> <collection property="sample2" resultMap="Sample2Result"/> </resultMap>
Java类定义
public abstract class AbstractSample { private int id; // getters and setters } public class SampleImpl1 extends AbstractSample { private String value1; // getters and setters } public class OtherSampleImpl1 extends AbstractSample { private String value2; // getters and setters } public class SampleImpl2 extends AbstractSample { private String value3; // getters and setters } public class OtherSampleImpl2 extends AbstractSample { private String value4; // getters and setters } // 用于处理null场景的默认实现类 public class DefaultSample extends AbstractSample { // 可选添加默认字段 }
报错信息
当discriminator列(sample1_type或sample2_type)为null时,抛出如下异常:
Caused by: org.apache.ibatis.reflection.ReflectionException: Error instantiating class com.test.AbstractSample with invalid types () or values (). Cause: java.lang.InstantiationException at org.apache.ibatis.reflection.factory.DefaultObjectFactory.instantiateClass(DefaultObjectFactory.java:86) at org.apache.ibatis.reflection.factory.DefaultObjectFactory.create(DefaultObjectFactory.java:53) at org.apache.ibatis.reflection.factory.DefaultObjectFactory.create(DefaultObjectFactory.java:45) at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.createResultObject(DefaultResultSetHandler.java:687) at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.createResultObject(DefaultResultSetHandler.java:659) at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.getRowValue(DefaultResultSetHandler.java:440) at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.applyNestedResultMappings(DefaultResultSetHandler.java:1070) at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.getRowValue(DefaultResultSetHandler.java:449) at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValuesForNestedResultMap(DefaultResultSetHandler.java:1026) at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValues(DefaultResultSetHandler.java:335) at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSet(DefaultResultSetHandler.java:310) at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSets(DefaultResultSetHandler.java:202) at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:66) at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:80) at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:65) at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:336) at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:158) at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:110) at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:90) at org.mybatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:154) at org.mybatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:147) at org.mybatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:142) at org.mybatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:75) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:425) ... 60 common frames omitted Caused by: java.lang.InstantiationException: null at java.base/jdk.internal.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:48) at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502) at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486) at org.apache.ibatis.reflection.factory.DefaultObjectFactory.instantiateClass(DefaultObjectFactory.java:62)
现有可行方案
通过在discriminator中添加value="null"的case配置,并指定对应的具体默认实现类的resultMap,可解决该问题。修改后的配置如下:
<resultMap id="Sample1Result" type="com.test.AbstractSample"> <discriminator javaType="int" column="sample1_type"> <case value="5240" resultMap="SampleImpl1Result"/> <case value="5241" resultMap="OtherSampleImpl1Result"/> <case value="null" resultMap="DefaultSample"/> <!-- 处理null场景 --> </discriminator> </resultMap> <resultMap id="Sample2Result" type="com.test.AbstractSample"> <discriminator javaType="int" column="sample2_type"> <case value="5240" resultMap="SampleImpl2Result"/> <case value="5241" resultMap="OtherSampleImpl2Result"/> <case value="null" resultMap="DefaultSample"/> <!-- 处理null场景 --> </discriminator> </resultMap> <resultMap id="AugmentedSampleResult" type="Sample" extends="BaseSample"> ... <collection property="sample1" resultMap="Sample1Result"/> <collection property="sample2" resultMap="Sample2Result"/> </resultMap>
疑问与建议请求
是否存在无需为每个抽象映射添加默认case和对应实现类的配置方式,能让MyBatis在discriminator列值为null时避免实例化抽象类?或者添加null默认case是唯一可行的解决方案?希望得到相关建议。
内容的提问来源于stack exchange,提问作者Anshu
相关产品推荐
相关产品推荐

