[Java]mybatis处理集合属性—get方法的问题

1,119 阅读2分钟

结论

mybatis组装集合属性的时候,会先调用属性的get方法,如果该属性不为null,则不会调用set方法设值,造成数据丢失。正常的属性不会,仅限集合

问题开始的地方

有一个DTO的属性是数组,下游有个新系统需要该值为非null,我这里临时修改了get方法,判断如果为null的话,返回new ArrayList();

    public List<PluginVersion> getVersionList() {
        return versionList == null ? new ArrayList() : versionList;
    }

问题解决了,但是接着就发现所有查询出来的该字段数据都变成了空数组

原因

先查了一下sql,能看到有一些是有值的,所以不是sql查询的问题,那就是mybatis组装的问题

正常来讲,改写属性的默认值或者get值,并不会影响mybatis塞值,之前也这么用过,但是后续跟进却发现集合属性是不一样的

package org.apache.ibatis.executor.resultset;

public class DefaultResultSetHandler implements ResultSetHandler {

    private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, String columnPrefix, Object partialObject) throws SQLException {
        String resultMapId = resultMap.getId();
        Object rowValue = partialObject;
        if (partialObject != null) {
            MetaObject metaObject = this.configuration.newMetaObject(partialObject);
            this.putAncestor(partialObject, resultMapId, columnPrefix);
            this.applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);
            this.ancestorObjects.remove(resultMapId);
        } else {
            ResultLoaderMap lazyLoader = new ResultLoaderMap();
            rowValue = this.createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
            if (rowValue != null && !this.hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
                MetaObject metaObject = this.configuration.newMetaObject(rowValue);
                boolean foundValues = this.useConstructorMappings;
                if (this.shouldApplyAutomaticMappings(resultMap, true)) {
                    foundValues = this.applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
                }
                
                //这一行是塞普通属性的值
                foundValues = this.applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
                this.putAncestor(rowValue, resultMapId, columnPrefix);
                //这一行是塞集合属性的值
                foundValues = this.applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
                this.ancestorObjects.remove(resultMapId);
                foundValues = lazyLoader.size() > 0 || foundValues;
                rowValue = !foundValues && !this.configuration.isReturnInstanceForEmptyRow() ? null : rowValue;
            }

            if (combinedKey != CacheKey.NULL_CACHE_KEY) {
                this.nestedResultObjects.put(combinedKey, rowValue);
            }
        }

        return rowValue;
    }

}


    private boolean applyNestedResultMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String parentPrefix, CacheKey parentRowKey, boolean newObject) {
                
                ...
                
                CacheKey combinedKey = this.combineKeys(rowKey, parentRowKey);
                Object rowValue = this.nestedResultObjects.get(combinedKey);
                boolean knownValue = rowValue != null;
                //塞值
                this.instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);
                if (this.anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw)) {
                    rowValue = this.getRowValue(rsw, nestedResultMap, combinedKey, columnPrefix, rowValue);
                
                ...
    }


    private Object instantiateCollectionPropertyIfAppropriate(ResultMapping resultMapping, MetaObject metaObject) {
        String propertyName = resultMapping.getProperty();
        Object propertyValue = metaObject.getValue(propertyName);
        if (propertyValue == null) {
            Class<?> type = resultMapping.getJavaType();
            
            //只有为null的情况下才会塞值
            if (type == null) {
                type = metaObject.getSetterType(propertyName);
            }

            try {
                if (this.objectFactory.isCollection(type)) {
                    propertyValue = this.objectFactory.create(type);
                    metaObject.setValue(propertyName, propertyValue);
                    return propertyValue;
                }
            } catch (Exception var7) {
                throw new ExecutorException("Error instantiating collection property for result '" + resultMapping.getProperty() + "'.  Cause: " + var7, var7);
            }
        } else if (this.objectFactory.isCollection(propertyValue.getClass())) {
            return propertyValue;
        }

        return null;
    }



注释上已经很清楚了,集合类的属性会判断是否已经有值,如果没有的话才会塞值,如果已经有默认的非null值,那么就不会调用get方法去塞值

所以我们在改写get、set方法的时候需要格外注意,现在的建议是不要再去改这两个默认方法了