From 3838379a172aa69b738c3b7a267ffde538f548a3 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Sat, 2 Mar 2019 11:08:16 +0900 Subject: [PATCH 1/2] Allow any variable name on ognl expression when specify single value object as parameter object Fixes gh-1486 --- .../scripting/xmltags/DynamicContext.java | 23 +-- .../ibatis/submitted/dynsql/DynSqlMapper.java | 22 ++- .../ibatis/submitted/dynsql/DynSqlMapper.xml | 35 ++++- .../ibatis/submitted/dynsql/DynSqlTest.java | 133 ++++++++++++++++++ 4 files changed, 202 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java b/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java index b9096c2ce3d..47f897a5205 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java @@ -45,9 +45,10 @@ public class DynamicContext { public DynamicContext(Configuration configuration, Object parameterObject) { if (parameterObject != null && !(parameterObject instanceof Map)) { MetaObject metaObject = configuration.newMetaObject(parameterObject); - bindings = new ContextMap(metaObject); + boolean existsTypeHandler = configuration.getTypeHandlerRegistry().hasTypeHandler(parameterObject.getClass()); + bindings = new ContextMap(metaObject, existsTypeHandler); } else { - bindings = new ContextMap(null); + bindings = new ContextMap(null, false); } bindings.put(PARAMETER_OBJECT_KEY, parameterObject); bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId()); @@ -75,11 +76,12 @@ public int getUniqueNumber() { static class ContextMap extends HashMap { private static final long serialVersionUID = 2977601501966151582L; + private final MetaObject parameterMetaObject; + private final boolean fallbackParameterObject; - private MetaObject parameterMetaObject; - - public ContextMap(MetaObject parameterMetaObject) { + public ContextMap(MetaObject parameterMetaObject, boolean fallbackParameterObject) { this.parameterMetaObject = parameterMetaObject; + this.fallbackParameterObject = fallbackParameterObject; } @Override @@ -89,12 +91,17 @@ public Object get(Object key) { return super.get(strKey); } - if (parameterMetaObject != null) { + if (parameterMetaObject == null) { + return null; + } + + if (fallbackParameterObject) { + return parameterMetaObject.hasGetter(strKey) ? + parameterMetaObject.getValue(strKey) : parameterMetaObject.getOriginalObject(); + } else { // issue #61 do not modify the context when reading return parameterMetaObject.getValue(strKey); } - - return null; } } diff --git a/src/test/java/org/apache/ibatis/submitted/dynsql/DynSqlMapper.java b/src/test/java/org/apache/ibatis/submitted/dynsql/DynSqlMapper.java index c22a743d2b2..131614970ff 100644 --- a/src/test/java/org/apache/ibatis/submitted/dynsql/DynSqlMapper.java +++ b/src/test/java/org/apache/ibatis/submitted/dynsql/DynSqlMapper.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2015 the original author or authors. + * Copyright 2009-2019 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. @@ -17,6 +17,26 @@ import org.apache.ibatis.annotations.Param; +import java.util.List; + public interface DynSqlMapper { String selectDescription(@Param("p") String p); + + List selectDescriptionById(Integer id); + List selectDescriptionByConditions(Conditions conditions); + List selectDescriptionByConditions2(Conditions conditions); + List selectDescriptionByConditions3(Conditions conditions); + + class Conditions { + private Integer id; + + public void setId(Integer id) { + this.id = id; + } + + public Integer getId() { + return id; + } + } + } diff --git a/src/test/java/org/apache/ibatis/submitted/dynsql/DynSqlMapper.xml b/src/test/java/org/apache/ibatis/submitted/dynsql/DynSqlMapper.xml index 825558aca25..ecebf1c086d 100644 --- a/src/test/java/org/apache/ibatis/submitted/dynsql/DynSqlMapper.xml +++ b/src/test/java/org/apache/ibatis/submitted/dynsql/DynSqlMapper.xml @@ -1,7 +1,7 @@ - @@ -30,4 +29,36 @@ WHERE id = 3 + + + + + + + + \ No newline at end of file diff --git a/src/test/java/org/apache/ibatis/submitted/dynsql/DynSqlTest.java b/src/test/java/org/apache/ibatis/submitted/dynsql/DynSqlTest.java index 2698bdc5dd6..c05d521c72a 100644 --- a/src/test/java/org/apache/ibatis/submitted/dynsql/DynSqlTest.java +++ b/src/test/java/org/apache/ibatis/submitted/dynsql/DynSqlTest.java @@ -18,16 +18,24 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.apache.ibatis.BaseDataTest; +import org.apache.ibatis.exceptions.PersistenceException; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.TypeHandler; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import java.io.IOException; import java.io.Reader; import java.math.BigDecimal; import java.math.BigInteger; +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -133,4 +141,129 @@ void testBindNull() { } } + /** + * Verify that can specify any variable name for parameter object when parameter is value object that a type handler exists. + * + * https://github.com/mybatis/mybatis-3/issues/1486 + */ + @Test + void testValueObjectWithoutParamAnnotation() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + DynSqlMapper mapper = sqlSession.getMapper(DynSqlMapper.class); + List descriptions = mapper.selectDescriptionById(3); + assertEquals(1, descriptions.size()); + assertEquals("Pebbles", descriptions.get(0)); + + assertEquals(7, mapper.selectDescriptionById(null).size()); + } + } + + /** + * Variations for with https://github.com/mybatis/mybatis-3/issues/1486 + */ + @Test + void testNonValueObjectWithoutParamAnnotation() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + DynSqlMapper mapper = sqlSession.getMapper(DynSqlMapper.class); + DynSqlMapper.Conditions conditions = new DynSqlMapper.Conditions(); + conditions.setId(3); + List descriptions = mapper.selectDescriptionByConditions(conditions); + assertEquals(1, descriptions.size()); + assertEquals("Pebbles", descriptions.get(0)); + + assertEquals(7, mapper.selectDescriptionByConditions(null).size()); + assertEquals(7, mapper.selectDescriptionByConditions(new DynSqlMapper.Conditions()).size()); + } + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + DynSqlMapper mapper = sqlSession.getMapper(DynSqlMapper.class); + DynSqlMapper.Conditions conditions = new DynSqlMapper.Conditions(); + conditions.setId(3); + try { + mapper.selectDescriptionByConditions2(conditions); + } catch (PersistenceException e) { + assertEquals("There is no getter for property named 'conditions' in 'class org.apache.ibatis.submitted.dynsql.DynSqlMapper$Conditions'", e.getCause().getMessage()); + } + assertEquals(7, mapper.selectDescriptionByConditions2(null).size()); + } + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + DynSqlMapper mapper = sqlSession.getMapper(DynSqlMapper.class); + DynSqlMapper.Conditions conditions = new DynSqlMapper.Conditions(); + conditions.setId(3); + try { + mapper.selectDescriptionByConditions3(conditions); + } catch (PersistenceException e) { + assertEquals("There is no getter for property named 'conditions' in 'class org.apache.ibatis.submitted.dynsql.DynSqlMapper$Conditions'", e.getCause().getMessage()); + } + assertEquals(7, mapper.selectDescriptionByConditions3(null).size()); + } + + } + + /** + * Variations for with https://github.com/mybatis/mybatis-3/issues/1486 + */ + @Test + void testCustomValueObjectWithoutParamAnnotation() throws IOException { + SqlSessionFactory sqlSessionFactory; + try (Reader configReader = Resources.getResourceAsReader("org/apache/ibatis/submitted/dynsql/MapperConfig.xml")) { + sqlSessionFactory = new SqlSessionFactoryBuilder().build(configReader); + // register type handler for the user defined class (= value object) + sqlSessionFactory.getConfiguration().getTypeHandlerRegistry().register(DynSqlMapper.Conditions.class, new TypeHandler() { + @Override + public void setParameter(PreparedStatement ps, int i, DynSqlMapper.Conditions parameter, JdbcType jdbcType) throws SQLException { + if (parameter.getId() != null) { + ps.setInt(i, parameter.getId()); + } else { + ps.setNull(i, JdbcType.INTEGER.TYPE_CODE); + } + } + @Override + public DynSqlMapper.Conditions getResult(ResultSet rs, String columnName) throws SQLException { + return null; + } + @Override + public DynSqlMapper.Conditions getResult(ResultSet rs, int columnIndex) throws SQLException { + return null; + } + @Override + public DynSqlMapper.Conditions getResult(CallableStatement cs, int columnIndex) throws SQLException { + return null; + } + }); + } + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + DynSqlMapper mapper = sqlSession.getMapper(DynSqlMapper.class); + DynSqlMapper.Conditions conditions = new DynSqlMapper.Conditions(); + conditions.setId(3); + List descriptions = mapper.selectDescriptionByConditions(conditions); + assertEquals(1, descriptions.size()); + assertEquals("Pebbles", descriptions.get(0)); + + assertEquals(7, mapper.selectDescriptionByConditions(null).size()); + assertEquals(7, mapper.selectDescriptionByConditions(new DynSqlMapper.Conditions()).size()); + } + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + DynSqlMapper mapper = sqlSession.getMapper(DynSqlMapper.class); + DynSqlMapper.Conditions conditions = new DynSqlMapper.Conditions(); + conditions.setId(3); + List descriptions = mapper.selectDescriptionByConditions2(conditions); + assertEquals(1, descriptions.size()); + assertEquals("Pebbles", descriptions.get(0)); + + assertEquals(7, mapper.selectDescriptionByConditions2(null).size()); + assertEquals(0, mapper.selectDescriptionByConditions2(new DynSqlMapper.Conditions()).size()); + } + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + DynSqlMapper mapper = sqlSession.getMapper(DynSqlMapper.class); + DynSqlMapper.Conditions conditions = new DynSqlMapper.Conditions(); + conditions.setId(3); + List descriptions = mapper.selectDescriptionByConditions3(conditions); + assertEquals(1, descriptions.size()); + assertEquals("Pebbles", descriptions.get(0)); + + assertEquals(7, mapper.selectDescriptionByConditions3(null).size()); + assertEquals(7, mapper.selectDescriptionByConditions3(new DynSqlMapper.Conditions()).size()); + } + } + } From f47b023d41c84854af0b477a94ae2479cee4ce15 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Fri, 14 Jun 2019 07:36:49 +0900 Subject: [PATCH 2/2] Polishing See gh-1486 --- .../org/apache/ibatis/scripting/xmltags/DynamicContext.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java b/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java index 47f897a5205..cc6ea638e5a 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java @@ -95,9 +95,8 @@ public Object get(Object key) { return null; } - if (fallbackParameterObject) { - return parameterMetaObject.hasGetter(strKey) ? - parameterMetaObject.getValue(strKey) : parameterMetaObject.getOriginalObject(); + if (fallbackParameterObject && !parameterMetaObject.hasGetter(strKey)) { + return parameterMetaObject.getOriginalObject(); } else { // issue #61 do not modify the context when reading return parameterMetaObject.getValue(strKey);