-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Conversation
linjisong
commented
Feb 8, 2018
•
edited
Loading
edited
- add the @SqlRef annotation for redirect the sql statement
- add the package 'org.apache.ibatis.binding.handler',add the interface MapperHandler for easier extension
- Support for Optional return types through OptionalMapperHandler
- add the sql-config-function for simplify the sql-mapper config
2.添加org.apache.ibatis.binding.handler包,重构MapperProxy
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.
@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 |
Hi @linjisong , I appreciate your effort, but its impact is huge and some part of it is not acceptable.
For simple use cases, declaring default methods allows you to reuse the XML statement.
I couldn't understand this one.
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. |
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 |