Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow any variable name on ognl expression when specify single value object as parameter object #1487

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -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());
Expand Down Expand Up @@ -75,11 +76,12 @@ public int getUniqueNumber() {

static class ContextMap extends HashMap<String, Object> {
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
Expand All @@ -89,12 +91,16 @@ public Object get(Object key) {
return super.get(strKey);
}

if (parameterMetaObject != null) {
if (parameterMetaObject == null) {
return null;
}

if (fallbackParameterObject && !parameterMetaObject.hasGetter(strKey)) {
return parameterMetaObject.getOriginalObject();
} else {
// issue #61 do not modify the context when reading
return parameterMetaObject.getValue(strKey);
}

return null;
}
}

Expand Down
@@ -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.
Expand All @@ -17,6 +17,26 @@

import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface DynSqlMapper {
String selectDescription(@Param("p") String p);

List<String> selectDescriptionById(Integer id);
List<String> selectDescriptionByConditions(Conditions conditions);
List<String> selectDescriptionByConditions2(Conditions conditions);
List<String> selectDescriptionByConditions3(Conditions conditions);

class Conditions {
private Integer id;

public void setId(Integer id) {
this.id = id;
}

public Integer getId() {
return id;
}
}

}
35 changes: 33 additions & 2 deletions src/test/java/org/apache/ibatis/submitted/dynsql/DynSqlMapper.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

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.
Expand All @@ -16,7 +16,6 @@
limitations under the License.

-->

<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
Expand All @@ -30,4 +29,36 @@
WHERE id = 3
</if>
</select>
<select id="selectDescriptionById" resultType="string">
SELECT description
FROM ibtest.names
<if test="id != null">
WHERE id = #{id}
</if>
</select>
<!-- Specify a property name as variable name (Valid always) -->
<select id="selectDescriptionByConditions" resultType="string">
SELECT description
FROM ibtest.names
<if test="id != null">
WHERE id = #{id}
</if>
</select>
<!-- Specify a any name(object name) as variable name (Valid if exists type handler) -->
<select id="selectDescriptionByConditions2" resultType="string">
SELECT description
FROM ibtest.names
<if test="conditions != null">
WHERE id = #{conditions}
</if>
</select>
<!-- Specify a any name(object name) and nested property name (Valid if exists type handler) -->
<select id="selectDescriptionByConditions3" resultType="string">
SELECT description
FROM ibtest.names
<if test="conditions != null and conditions.id != null">
WHERE id = #{conditions.id}
</if>
</select>

</mapper>
133 changes: 133 additions & 0 deletions src/test/java/org/apache/ibatis/submitted/dynsql/DynSqlTest.java
Expand Up @@ -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;
Expand Down Expand Up @@ -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<String> 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<String> 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<DynSqlMapper.Conditions>() {
@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<String> 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<String> 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<String> 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());
}
}

}