diff --git a/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java b/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java index 97ed03de465..55212792fb6 100644 --- a/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java @@ -37,6 +37,7 @@ import org.apache.ibatis.reflection.factory.ObjectFactory; import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory; import org.apache.ibatis.session.AutoMappingBehavior; +import org.apache.ibatis.session.AutoMappingUnknownColumnBehavior; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.LocalCacheScope; @@ -230,6 +231,7 @@ private void propertiesElement(XNode context) throws Exception { private void settingsElement(Properties props) throws Exception { configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL"))); + configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE"))); configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true)); configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory"))); configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false)); 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 2b27efbe349..eff880b982d 100644 --- a/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java +++ b/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java @@ -476,7 +476,13 @@ private List createAutomaticMappings(ResultSetWrapper if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) { final TypeHandler typeHandler = rsw.getTypeHandler(propertyType, columnName); autoMapping.add(new UnMappedColumAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive())); + } else { + configuration.getAutoMappingUnknownColumnBehavior() + .doAction(mappedStatement, columnName, property, propertyType); } + } else{ + configuration.getAutoMappingUnknownColumnBehavior() + .doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null); } } autoMappingsCache.put(mapKey, autoMapping); diff --git a/src/main/java/org/apache/ibatis/session/AutoMappingUnknownColumnBehavior.java b/src/main/java/org/apache/ibatis/session/AutoMappingUnknownColumnBehavior.java new file mode 100644 index 00000000000..0de49d17f0e --- /dev/null +++ b/src/main/java/org/apache/ibatis/session/AutoMappingUnknownColumnBehavior.java @@ -0,0 +1,91 @@ +/** + * Copyright 2009-2016 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.session; + +import org.apache.ibatis.logging.Log; +import org.apache.ibatis.logging.LogFactory; +import org.apache.ibatis.mapping.MappedStatement; + +/** + * Specify the behavior when detects an unknown column (or unknown property type) of automatic mapping target. + * + * @author Kazuki Shimizu + * @since 3.3.2 + */ +public enum AutoMappingUnknownColumnBehavior { + + /** + * Do nothing (Default). + */ + NONE { + @Override + public void doAction(MappedStatement mappedStatement, String columnName, String property, Class propertyType) { + // do nothing + } + }, + + /** + * Output warning log. + * Note: The log level of {@code 'org.apache.ibatis.session.AutoMappingUnknownColumnBehavior'} must be set to {@code WARN}. + */ + WARNING { + @Override + public void doAction(MappedStatement mappedStatement, String columnName, String property, Class propertyType) { + log.warn(buildMessage(mappedStatement, columnName, property, propertyType)); + } + }, + + /** + * Fail mapping. + * Note: throw {@link SqlSessionException}. + */ + FAILING { + @Override + public void doAction(MappedStatement mappedStatement, String columnName, String property, Class propertyType) { + throw new SqlSessionException(buildMessage(mappedStatement, columnName, property, propertyType)); + } + }; + + /** + * Logger + */ + private static final Log log = LogFactory.getLog(AutoMappingUnknownColumnBehavior.class); + + /** + * Perform the action when detects an unknown column (or unknown property type) of automatic mapping target. + * @param mappedStatement current mapped statement + * @param columnName column name for mapping target + * @param propertyName property name for mapping target + * @param propertyType property type for mapping target (If this argument is not null, {@link org.apache.ibatis.type.TypeHandler} for property type is not registered) + */ + public abstract void doAction(MappedStatement mappedStatement, String columnName, String propertyName, Class propertyType); + + /** + * build error message. + */ + private static String buildMessage(MappedStatement mappedStatement, String columnName, String property, Class propertyType) { + return new StringBuilder("Unknown column is detected on '") + .append(mappedStatement.getId()) + .append("' auto-mapping. Mapping parameters are ") + .append("[") + .append("columnName=").append(columnName) + .append(",").append("propertyName=").append(property) + .append(",").append("propertyType=").append(propertyType != null ? propertyType.getName() : null) + .append("]") + .toString(); + } + +} diff --git a/src/main/java/org/apache/ibatis/session/Configuration.java b/src/main/java/org/apache/ibatis/session/Configuration.java index dd38fc0a1ea..ccd1829e1d7 100644 --- a/src/main/java/org/apache/ibatis/session/Configuration.java +++ b/src/main/java/org/apache/ibatis/session/Configuration.java @@ -117,6 +117,7 @@ public class Configuration { protected Integer defaultFetchSize; protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE; protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL; + protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE; protected Properties variables = new Properties(); protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory(); @@ -305,6 +306,20 @@ public void setAutoMappingBehavior(AutoMappingBehavior autoMappingBehavior) { this.autoMappingBehavior = autoMappingBehavior; } + /** + * @since 3.3.2 + */ + public AutoMappingUnknownColumnBehavior getAutoMappingUnknownColumnBehavior() { + return autoMappingUnknownColumnBehavior; + } + + /** + * @since 3.3.2 + */ + public void setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior) { + this.autoMappingUnknownColumnBehavior = autoMappingUnknownColumnBehavior; + } + public boolean isLazyLoadingEnabled() { return lazyLoadingEnabled; } diff --git a/src/test/java/log4j.properties b/src/test/java/log4j.properties index e9271a5e7ca..2acb6de64fe 100644 --- a/src/test/java/log4j.properties +++ b/src/test/java/log4j.properties @@ -20,7 +20,11 @@ log4j.rootLogger=ERROR, stdout ### Uncomment for MyBatis logging log4j.logger.org.apache.ibatis=ERROR +log4j.logger.org.apache.ibatis.session.AutoMappingUnknownColumnBehavior=WARN, lastEventSavedAppender + ### Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n + +log4j.appender.lastEventSavedAppender=org.apache.ibatis.session.AutoMappingUnknownColumnBehaviorTest$LastEventSavedAppender diff --git a/src/test/java/org/apache/ibatis/builder/CustomizedSettingsMapperConfig.xml b/src/test/java/org/apache/ibatis/builder/CustomizedSettingsMapperConfig.xml index 394d88cdcf4..603169763bd 100644 --- a/src/test/java/org/apache/ibatis/builder/CustomizedSettingsMapperConfig.xml +++ b/src/test/java/org/apache/ibatis/builder/CustomizedSettingsMapperConfig.xml @@ -25,6 +25,7 @@ + diff --git a/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java b/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java index 18c7d3eaae7..4726e3d2e5a 100644 --- a/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java +++ b/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java @@ -34,6 +34,7 @@ import org.apache.ibatis.scripting.defaults.RawLanguageDriver; import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver; import org.apache.ibatis.session.AutoMappingBehavior; +import org.apache.ibatis.session.AutoMappingUnknownColumnBehavior; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.LocalCacheScope; @@ -57,6 +58,7 @@ public void shouldSuccessfullyLoadMinimalXMLConfigFile() throws Exception { Configuration config = builder.parse(); assertNotNull(config); assertThat(config.getAutoMappingBehavior(), is(AutoMappingBehavior.PARTIAL)); + assertThat(config.getAutoMappingUnknownColumnBehavior(), is(AutoMappingUnknownColumnBehavior.NONE)); assertThat(config.isCacheEnabled(), is(true)); assertThat(config.getProxyFactory(), is(instanceOf(JavassistProxyFactory.class))); assertThat(config.isLazyLoadingEnabled(), is(false)); @@ -145,6 +147,7 @@ public void shouldSuccessfullyLoadXMLConfigFile() throws Exception { Configuration config = builder.parse(); assertThat(config.getAutoMappingBehavior(), is(AutoMappingBehavior.NONE)); + assertThat(config.getAutoMappingUnknownColumnBehavior(), is(AutoMappingUnknownColumnBehavior.WARNING)); assertThat(config.isCacheEnabled(), is(false)); assertThat(config.getProxyFactory(), is(instanceOf(CglibProxyFactory.class))); assertThat(config.isLazyLoadingEnabled(), is(true)); diff --git a/src/test/java/org/apache/ibatis/session/AutoMappingUnknownColumnBehaviorTest.java b/src/test/java/org/apache/ibatis/session/AutoMappingUnknownColumnBehaviorTest.java new file mode 100644 index 00000000000..6963e0eb72a --- /dev/null +++ b/src/test/java/org/apache/ibatis/session/AutoMappingUnknownColumnBehaviorTest.java @@ -0,0 +1,158 @@ +/** + * Copyright 2009-2016 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.session; + +import org.apache.ibatis.BaseDataTest; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.domain.blog.Author; +import org.apache.ibatis.exceptions.PersistenceException; +import org.apache.ibatis.mapping.Environment; +import org.apache.ibatis.transaction.TransactionFactory; +import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; +import org.apache.log4j.spi.LoggingEvent; +import org.apache.log4j.varia.NullAppender; +import org.junit.BeforeClass; +import org.junit.Test; + +import javax.sql.DataSource; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNull.nullValue; +import static org.junit.Assert.assertThat; + +/** + * Tests for specify the behavior when detects an unknown column (or unknown property type) of automatic mapping target. + * + * @author Kazuki Shimizu + * @since 3.3.2 + */ +public class AutoMappingUnknownColumnBehaviorTest { + + interface Mapper { + @Select({ + "SELECT ", + " ID,", + " USERNAME as USERNAMEEEE,", // unknown column + " PASSWORD,", + " EMAIL,", + " BIO", + "FROM AUTHOR WHERE ID = #{id}"}) + Author selectAuthor(int id); + + @Select({ + "SELECT ", + " ID,", // unknown property type + " USERNAME", + "FROM AUTHOR WHERE ID = #{id}"}) + SimpleAuthor selectSimpleAuthor(int id); + } + + static class SimpleAuthor { + private AtomicInteger id; // unknown property type + private String username; + + public AtomicInteger getId() { + return id; + } + + public void setId(AtomicInteger id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + } + + public static class LastEventSavedAppender extends NullAppender { + private static LoggingEvent event; + + public void doAppend(LoggingEvent event) { + LastEventSavedAppender.event = event; + } + } + + private static SqlSessionFactory sqlSessionFactory; + + @BeforeClass + public static void setup() throws Exception { + DataSource dataSource = BaseDataTest.createBlogDataSource(); + TransactionFactory transactionFactory = new JdbcTransactionFactory(); + Environment environment = new Environment("Production", transactionFactory, dataSource); + Configuration configuration = new Configuration(environment); + configuration.addMapper(Mapper.class); + sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration); + } + + @Test + public void none() { + sqlSessionFactory.getConfiguration().setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.NONE); + SqlSession session = sqlSessionFactory.openSession(); + try { + Mapper mapper = session.getMapper(Mapper.class); + Author author = mapper.selectAuthor(101); + assertThat(author.getId(), is(101)); + assertThat(author.getUsername(), nullValue()); + } finally { + session.close(); + } + + } + + @Test + public void warningCauseByUnknownPropertyType() { + sqlSessionFactory.getConfiguration().setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.WARNING); + + SqlSession session = sqlSessionFactory.openSession(); + + try { + Mapper mapper = session.getMapper(Mapper.class); + SimpleAuthor author = mapper.selectSimpleAuthor(101); + assertThat(author.getId(), nullValue()); + assertThat(author.getUsername(), is("jim")); + assertThat(LastEventSavedAppender.event.getMessage().toString(), is("Unknown column is detected on 'org.apache.ibatis.session.AutoMappingUnknownColumnBehaviorTest$Mapper.selectSimpleAuthor' auto-mapping. Mapping parameters are [columnName=ID,propertyName=id,propertyType=java.util.concurrent.atomic.AtomicInteger]")); + + } finally { + session.close(); + } + + } + + @Test + public void failingCauseByUnknownColumn() { + sqlSessionFactory.getConfiguration().setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.FAILING); + + SqlSession session = sqlSessionFactory.openSession(); + + try { + Mapper mapper = session.getMapper(Mapper.class); + mapper.selectAuthor(101); + } catch (PersistenceException e) { + assertThat(e.getCause(), instanceOf(SqlSessionException.class)); + assertThat(e.getCause().getMessage(), is("Unknown column is detected on 'org.apache.ibatis.session.AutoMappingUnknownColumnBehaviorTest$Mapper.selectAuthor' auto-mapping. Mapping parameters are [columnName=USERNAMEEEE,propertyName=USERNAMEEEE,propertyType=null]")); + } finally { + session.close(); + } + + } + +}