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

refactor the MapperProxy through MapperHandler, support for Optional, add @SqlRef for redirect the sql statement, add sql-config-function #1187

Closed
wants to merge 9 commits into from

Conversation

linjisong
Copy link

@linjisong linjisong commented Feb 8, 2018

  1. add the @SqlRef annotation for redirect the sql statement
  2. add the package 'org.apache.ibatis.binding.handler',add the interface MapperHandler for easier extension
  3. Support for Optional return types through OptionalMapperHandler
  4. add the sql-config-function for simplify the sql-mapper config

@linjisong linjisong changed the title merge mapper-handler branch refactor the MapperProxy Feb 9, 2018
@linjisong
Copy link
Author

linjisong commented Feb 9, 2018

1. Using the @ SqlRef annotation to redirect the sql statement

@Repository
public interface SelectDao {

    @SqlRef("select")
    public DictDefine selectOne(@Param("dictId") String dictId);

    @SqlRef("select")
    public List<DictDefine> selectList();

    @SqlRef("select")
    public Page<DictDefine> selectList(@Param("dictId") String dictId, Pageable pageable);

    @SqlRef("select")
    public Cursor<DictDefine> selectCursor();
}
<select id="select" resultMap="DictDefine">
		select * from TB_DICT_DEF
		<where>
			<if test="null != dictId and '' != dictId">
				and DICT_ID = #{dictId, jdbcType=VARCHAR}
			</if>
		</where>
		order by SEQNO
</select>

2. Refactoring MapperProxy using chain of responsibility patterns for easy extension, such as adding tree query、 Optional support, etc.

public class TreeMapperHandler implements MapperHandler {

    @Override
    public boolean supports(MapperContext context) {
        return ITree.class.isAssignableFrom(context.getMethod().getReturnType());
    }

    @Override
    public Object execute(SqlSession sqlSession, Object[] args, MapperContext context) {
        String sqlId = context.getMappedStatement().getId();
        Object param = context.getParamResolver().getNamedParams(args);
        List<ITreeNode> list = sqlSession.<ITreeNode>selectList(sqlId, param);
        if (null == list || list.isEmpty()) {
            return null;
        } else {
            Object first = list.get(0);
            if (!(first instanceof ITreeNode)) {
                throw new IllegalStateException("interface method: [" + context.getMethod() + "], the class of tree node must be implement interface [" + ITreeNode.class + "]");
            }
            return TreeFactory.builder(list);
        }
    }
}

add the handler with Configuration object.

Configuration.getHandlerRegistry().getMapperHandlers().add(0, new TreeMapperHandler());
@Repository
public interface TreeDao {

    public ITree<DictTreeItem> selectTree(@Param("dictId") String dictId);
}
<select id="selectTree" resultMap="DictTreeItem">
		select ITEM_CODE AS CODE,
			   ITEM_TEXT AS TEXT,
			   PARENT_ITEM_CODE AS PARENT_CODE,
			   SEQNO 
		  from TB_DICT_TREE
		<where>
			<if test="null != dictId and '' != dictId">
				and DICT_ID = #{dictId, jdbcType=VARCHAR}
			</if>
		</where>
		order by SEQNO
</select>

3. support for Optional

<select id="select" resultMap="DictDefine">
		select * from TB_DICT_DEF
		<where>
			<if test="null != dictId and '' != dictId">
				and DICT_ID = #{dictId, jdbcType=VARCHAR}
			</if>
		</where>
		order by SEQNO
</select>
@Mapper
public interface OptionalDao {

    @SqlRef("select")
    public Optional<DictDefine> selectOne(@Param("dictId") String dictId);

    @SqlRef("select")
    public Optional<List<DictDefine>> selectList();

    @SqlRef("select")
    public Optional<Cursor<DictDefine>> selectCursor();
}
@UsesJava8
public class OptionalMapperHandler extends SelectMapperHandler {

    @Override
    public boolean supports(MapperHandlerContext context) {
        return super.supports(context)
                && Optional.class.isAssignableFrom(context.getMethod().getReturnType());
    }

    @Override
    public Object execute(SqlSession sqlSession, Object[] args, MapperHandlerContext context) {
        Object result = null;
        Class<?> returnType = getOptionalType(context);
        if (null == returnType) {
            throw new RuntimeException("not supported return type: [" + context.getMethod().getGenericReturnType() + "]");
        }

        String sqlId = context.getMappedStatement().getId();
        Object param = context.getParamResolver().getNamedParams(args);
        RowBounds rowBounds = extractParam(args, RowBounds.class);
        if (context.getConfiguration().getObjectFactory().isCollection(returnType) || returnType.isArray()) {
            result = super.executeMany(sqlSession, sqlId, param, rowBounds, returnType);
        } else if (Map.class.isAssignableFrom(returnType) && context.getMethod().isAnnotationPresent(MapKey.class)) {
            result = super.executeMany(sqlSession, sqlId, param, rowBounds, returnType);
        } else if (Cursor.class.isAssignableFrom(returnType)) {
            result = super.executeCursor(sqlSession, sqlId, param, rowBounds);
        } else {
            result = sqlSession.selectOne(sqlId, param);
        }
        return Optional.ofNullable(result);
    }

    private Class<?> getOptionalType(MapperHandlerContext context) {
        ParameterizedType genericReturnType = (ParameterizedType) context.getMethod().getGenericReturnType();
        Type type = genericReturnType.getActualTypeArguments()[0];
        Class<?> cls = null;
        if (type instanceof ParameterizedType) {
            cls = (Class<?>) ((ParameterizedType) type).getRawType();
        } else if (type instanceof Class) {
            cls = (Class<?>) type;
        }
        return cls;
    }

}

4. sql-config-function

<select id="testSqlConfigFunction" resultMap="DictDefine">
		select DICT_ID, DICT_NAME, SEQNO, $decode{#{paramName}, '1', 'A', '2', 'B','C'} AS DECODE_TEST from TB_DICT_DEF
		<where>
			<if test="null != dictName and '' != dictName">
				and DICT_NAME $like{#{dictName, jdbcType=VARCHAR}}
			</if>
		</where>
		order by SEQNO
</select>

<!-- not use sql-config-function -->
<select id="notUseSqlConfigFunction" resultType="com.pab.brcp.we4a.config.dict.bean.DictDefine">
		select DICT_ID, DICT_NAME, SEQNO, 
		<choose>
			<when test="'oracle' == databaseId">
				DECODE(#{paramName}, '1', 'A', '2', 'B','C') AS DECODE_TEST 
			</when>
			<otherwise>
				CASE #{paramName} WHEN '1' THEN 'A' WHEN '2' THEN 'B' ELSE 'C' END AS DECODE_TEST 
			</otherwise>
		</choose>
		from TB_DICT_DEF
		<where>
			<if test="null != dictName and '' != dictName">
				<choose>
					<when test="'mysql' == databaseId">
						and DICT_NAME LIKE concat('%',#{dictName, jdbcType=VARCHAR}, '%')
					</when>
					<otherwise>
						and DICT_NAME LIKE '%' || #{dictName, jdbcType=VARCHAR} || '%'
					</otherwise>
				</choose>
			</if>
		</where>
		order by SEQNO
</select>

H2:

select DICT_ID, DICT_NAME, SEQNO, CASE ? WHEN '1' THEN 'A' WHEN '2' THEN 'B' ELSE 'C' END AS DECODE_TEST 
from TB_DICT_DEF 
WHERE DICT_NAME LIKE '%'||?||'%' 
order by SEQNO

MySQL:

select DICT_ID, DICT_NAME, SEQNO, CASE ? WHEN '1' THEN 'A' WHEN '2' THEN 'B' ELSE 'C' END AS DECODE_TEST 
from TB_DICT_DEF 
WHERE DICT_NAME LIKE CONCAT('%',?,'%') 
order by SEQNO 

Oracle:

select DICT_ID, DICT_NAME, SEQNO, DECODE(?,'1','A','2','B','C') AS DECODE_TEST 
from TB_DICT_DEF
WHERE DICT_NAME LIKE '%'||?||'%' 
order by SEQNO 

@linjisong linjisong changed the title refactor the MapperProxy refactor the MapperProxy, add @SqlRef for redirect the sql statement, add sql-config-function Feb 9, 2018
@linjisong linjisong changed the title refactor the MapperProxy, add @SqlRef for redirect the sql statement, add sql-config-function refactor the MapperProxy through OptionalMapperHandler, support for Optional, add @SqlRef for redirect the sql statement, add sql-config-function Feb 13, 2018
@linjisong linjisong changed the title refactor the MapperProxy through OptionalMapperHandler, support for Optional, add @SqlRef for redirect the sql statement, add sql-config-function refactor the MapperProxy through MapperHandler, support for Optional, add @SqlRef for redirect the sql statement, add sql-config-function Feb 13, 2018
@harawata
Copy link
Member

Hi @linjisong ,

I appreciate your effort, but its impact is huge and some part of it is not acceptable.
In general, you should not mix multiple enhancements in one PR.
It's harder to review and has less chance of being merged.

  1. add the @SqlRef annotation for redirect the sql statement

For simple use cases, declaring default methods allows you to reuse the XML statement.
For other rare cases, reusing SQL fragments with <include /> could be an acceptable solution.

  1. add the package 'org.apache.ibatis.binding.handler',add the interface MapperHandler for easier extension

I couldn't understand this one.
Is this the base of other enhancements, maybe?

  1. Support for Optional return types through OptionalMapperHandler

Optional support is added in 3.5.0. See #799 .

  1. add the sql-config-function for simplify the sql-mapper config

We have no plan to support this at the moment.


I'm sorry, but there is no chance that we can merge this PR as it is.
When you have an enhancement idea that requires a big change, I suggest you to create a feature request issue first.
Then we can discuss various approaches before you spend a lot of time and energy (they are precious!).

@harawata harawata closed this Apr 27, 2019
@linjisong
Copy link
Author

Hi @linjisong ,

I appreciate your effort, but its impact is huge and some part of it is not acceptable.
In general, you should not mix multiple enhancements in one PR.
It's harder to review and has less chance of being merged.

  1. add the @SqlRef annotation for redirect the sql statement

For simple use cases, declaring default methods allows you to reuse the XML statement.
For other rare cases, reusing SQL fragments with <include /> could be an acceptable solution.

  1. add the package 'org.apache.ibatis.binding.handler',add the interface MapperHandler for easier extension

I couldn't understand this one.
Is this the base of other enhancements, maybe?

  1. Support for Optional return types through OptionalMapperHandler

Optional support is added in 3.5.0. See #799 .

  1. add the sql-config-function for simplify the sql-mapper config

We have no plan to support this at the moment.

I'm sorry, but there is no chance that we can merge this PR as it is.
When you have an enhancement idea that requires a big change, I suggest you to create a feature request issue first.
Then we can discuss various approaches before you spend a lot of time and energy (they are precious!).

Okay, I split these features into multiple submissions, hoping to merge them into the new version, but Iundefinedm going to use a new github account: andyslin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants