diff --git a/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java b/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java index 52d7b5d822c..083d2c86ef1 100644 --- a/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java +++ b/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java @@ -61,7 +61,7 @@ */ public class DefaultResultSetHandler implements ResultSetHandler { - private static final Object NO_VALUE = new Object(); + private static final Object DEFERED = new Object(); private final Executor executor; private final Configuration configuration; @@ -368,7 +368,11 @@ private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, boolean foundValues = false; final List propertyMappings = resultMap.getPropertyResultMappings(); for (ResultMapping propertyMapping : propertyMappings) { - final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix); + String column = prependPrefix(propertyMapping.getColumn(), columnPrefix); + if (propertyMapping.getNestedResultMapId() != null) { + // the user added a column attribute to a nested result map, ignore it + column = null; + } if (propertyMapping.isCompositeResult() || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) || propertyMapping.getResultSet() != null) { @@ -376,10 +380,12 @@ private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, // issue #541 make property optional final String property = propertyMapping.getProperty(); // issue #377, call setter on nulls - if (value != NO_VALUE && property != null && (value != null || configuration.isCallSettersOnNulls())) { - if (value != null || !metaObject.getSetterType(property).isPrimitive()) { - metaObject.setValue(property, value); - } + if (value != DEFERED + && property != null + && (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive()))) { + metaObject.setValue(property, value); + } + if (value != null || value == DEFERED) { foundValues = true; } } @@ -392,11 +398,8 @@ private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject if (propertyMapping.getNestedQueryId() != null) { return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix); } else if (propertyMapping.getResultSet() != null) { - addPendingChildRelation(rs, metaResultObject, propertyMapping); - return NO_VALUE; - } else if (propertyMapping.getNestedResultMapId() != null) { - // the user added a column attribute to a nested result map, ignore it - return NO_VALUE; + addPendingChildRelation(rs, metaResultObject, propertyMapping); // TODO is that OK? + return DEFERED; } else { final TypeHandler typeHandler = propertyMapping.getTypeHandler(); final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix); @@ -648,17 +651,19 @@ private Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObj final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId); final Class nestedQueryParameterType = nestedQuery.getParameterMap().getType(); final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, propertyMapping, nestedQueryParameterType, columnPrefix); - Object value = NO_VALUE; + Object value = null; if (nestedQueryParameterObject != null) { final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject); final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql); final Class targetType = propertyMapping.getJavaType(); if (executor.isCached(nestedQuery, key)) { executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType); + value = DEFERED; } else { final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql); if (propertyMapping.isLazy()) { lazyLoader.addLoader(property, metaResultObject, resultLoader); + value = DEFERED; } else { value = resultLoader.loadResult(); } diff --git a/src/test/java/org/apache/ibatis/submitted/call_setters_on_nulls/CallSettersOnNullsTest.java b/src/test/java/org/apache/ibatis/submitted/call_setters_on_nulls/CallSettersOnNullsTest.java index fd2b87a261d..8cf627c4889 100644 --- a/src/test/java/org/apache/ibatis/submitted/call_setters_on_nulls/CallSettersOnNullsTest.java +++ b/src/test/java/org/apache/ibatis/submitted/call_setters_on_nulls/CallSettersOnNullsTest.java @@ -107,7 +107,12 @@ public void shouldCallNullOnMapForSingleColumnWithResultMap() { try { Mapper mapper = sqlSession.getMapper(Mapper.class); List> oneColumns = mapper.getNameOnlyMapped(); - Assert.assertNotNull(oneColumns.get(1)); +// Assert.assertNotNull(oneColumns.get(1)); + // TEST changed after fix for #307 + // When callSetterOnNull is true, setters are called with null values + // but if all the values for an object are null + // the object itself should be null (same as default behaviour) + Assert.assertNull(oneColumns.get(1)); } finally { sqlSession.close(); } diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/Binome.java b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/Binome.java new file mode 100644 index 00000000000..0e71be8976b --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/Binome.java @@ -0,0 +1,49 @@ +package org.apache.ibatis.submitted.nestedresulthandler_multiple_association; + +public class Binome { + private T one; + private U two; + + public Binome() { + } + + public Binome(final T one, final U two) { + this.one = one; + this.two = two; + } + + public T getOne() { + return one; + } + + public void setOne(T one) { + this.one = one; + } + + public U getTwo() { + return two; + } + + public void setTwo(U two) { + this.two = two; + } + + @Override + public int hashCode() { + return (one != null ? one.hashCode() : 0) + (two != null ? two.hashCode() : 0); + } + + @Override + public boolean equals(final Object obj) { + if (obj instanceof Binome) { + Binome bin = (Binome) obj; + return one != null && one.equals(bin.getOne()) && two != null && two.equals(bin.getTwo()); + } + return super.equals(obj); + } + + @Override + public String toString() { + return "Binome [one=" + one + ", two=" + two + "]"; + } +} diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/ChildBean.java b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/ChildBean.java new file mode 100644 index 00000000000..6abcb811214 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/ChildBean.java @@ -0,0 +1,27 @@ +package org.apache.ibatis.submitted.nestedresulthandler_multiple_association; + +public class ChildBean { + private Integer id; + private String value; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return "ChildBean [id=" + id + ", value=" + value + "]"; + } +} diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/CreateDB.sql b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/CreateDB.sql new file mode 100644 index 00000000000..029942543ed --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/CreateDB.sql @@ -0,0 +1,45 @@ +-- +-- Copyright 2009-2014 the original author or authors. +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +DROP TABLE parent if exists; +DROP TABLE child if exists; +create table parent( + id integer, + value varchar(20) +); + +create table child( + id integer, + value varchar(20) +); + +create table parent_child( + idparent integer, + idchild_from integer, + idchild_to integer +); + +insert into parent (id, value) values (1, 'parent1'); +insert into parent (id, value) values (2, 'parent2'); + +insert into child (id, value) values (1, 'child1'); +insert into child (id, value) values (2, 'child2'); +insert into child (id, value) values (3, 'child3'); +insert into child (id, value) values (4, 'child4'); + +insert into parent_child (idparent, idchild_from, idchild_to) values (1, 1, 2); +insert into parent_child (idparent, idchild_from, idchild_to) values (2, 2, 3); +insert into parent_child (idparent, idchild_from, idchild_to) values (2, 1, 2); \ No newline at end of file diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/NestedResultHandlerMultipleAssociationTest.java b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/NestedResultHandlerMultipleAssociationTest.java new file mode 100644 index 00000000000..43c125cae45 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/NestedResultHandlerMultipleAssociationTest.java @@ -0,0 +1,89 @@ +/* + * Copyright 2009-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.nestedresulthandler_multiple_association; + +import java.io.Reader; +import java.sql.Connection; +import java.util.List; + +import org.apache.ibatis.io.Resources; +import org.apache.ibatis.jdbc.ScriptRunner; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.session.SqlSessionFactoryBuilder; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class NestedResultHandlerMultipleAssociationTest { + + private static SqlSessionFactory sqlSessionFactory; + + @BeforeClass + public static void setUp() throws Exception { + // create an SqlSessionFactory + Reader reader = Resources.getResourceAsReader("org/apache/ibatis/submitted/nestedresulthandler_multiple_association/mybatis-config.xml"); + sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); + reader.close(); + + // populate in-memory database + SqlSession session = sqlSessionFactory.openSession(); + Connection conn = session.getConnection(); + reader = Resources.getResourceAsReader("org/apache/ibatis/submitted/nestedresulthandler_multiple_association/CreateDB.sql"); + ScriptRunner runner = new ScriptRunner(conn); + runner.setLogWriter(null); + runner.runScript(reader); + reader.close(); + session.close(); + } + + @Test + public void failure() throws Exception { + SqlSession sqlSession = sqlSessionFactory.openSession(); + + // Parents have child going from somewhere to somewhere, they are stored in a Binome object + // In this test we have 2 parents: + // Parent1 is going from Child1 to Child2 + // Parent2 is going from Child2 to Child3 and from Child1 to Child2 + // You'll see a NULL entry in the list instead of the Binome Child1/Child2 + List list = sqlSession.selectList("selectParentBeans"); + for (ParentBean pb : list) { + for (Binome childs : pb.getChilds()) { + Assert.assertNotNull(childs); + Assert.assertNotNull(childs.getOne()); + Assert.assertNotNull(childs.getTwo()); + } + } + + sqlSession.close(); + } + + @Test + public void success() throws Exception { + SqlSession sqlSession = sqlSessionFactory.openSession(); + + ParentBean parent = sqlSession.selectOne("selectParentBeanById", 2); + + // If you only select the Parent2 it works + for (Binome childs : parent.getChilds()) { + Assert.assertNotNull(childs); + Assert.assertNotNull(childs.getOne()); + Assert.assertNotNull(childs.getTwo()); + } + sqlSession.close(); + } + +} diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/ParentBean.java b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/ParentBean.java new file mode 100644 index 00000000000..6ba154a98f1 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/ParentBean.java @@ -0,0 +1,42 @@ +package org.apache.ibatis.submitted.nestedresulthandler_multiple_association; + +import java.util.List; + +public class ParentBean { + private Integer id; + private String value; + private List> childs; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public List> getChilds() { + return childs; + } + + public void setChilds(List> childs) { + this.childs = childs; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("ParentBean [id=" + id + ", value=" + value + "]\nChilds:\n"); + for (Binome binome : childs) { + sb.append("\tChild : ").append(binome).append('\n'); + } + return sb.toString(); + } +} diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/mapper.xml b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/mapper.xml new file mode 100644 index 00000000000..eb7f75775ef --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/mapper.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/mybatis-config.xml b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/mybatis-config.xml new file mode 100644 index 00000000000..6080b46ea6e --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_multiple_association/mybatis-config.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + +