MyBatis 返回动态结果类型插件

MyBatis 返回动态结果类型插件

说明

虽然写了这么一个插件,但是个人建议尽可能不去这么用,如果这个插件真正能方便你,使用起来也没任何问题。

关于插件的一些个人修改建议,在插件的注释中有说明。

插件用途:可以在 MyBatis 参数中带上要返回的类型Class,插件就会改变返回值类型为你指定的类型。

用法

说的可能不清楚,看个简单的用法。

MyBatis 中定义如下方法:

Object selectById(@Param("id")Long id, @Param("resultType")Class resultType);

//或

Object selectById(@Param("id")Long id, @Param("resultType")String resultType);

支持直接的Class或者String类型的全限定类名,必须指定参数的keyresultType,通过拦截器参数可以修改这个值,参数的顺序无所谓。

用法:

City city = (City) mapper.selectById(1L, City.class);

//或

City city = (City) mapper.selectById(1L, "tk.mybatis.model.City");

更变态直观的例子就是SQL也是动态传入${sql}这样方式的,不同SQL配不同的结果类型更能说明问题。你可以结合这篇 MyBatis 执行动态 SQL 博客使用。

如果看到这里觉得有用,你就可以继续往下看实现原理。

源码地址

http://git.oschina.net/free/Mybatis_Utils/tree/master/ResultType

虽然在git上,但我并不会长期维护这个插件。

实现原理

前面写过一篇 MyBatis Excutor 拦截器的巧妙用法,这个插件算是一个“修改MappedStatement 时解决并发的问题”可以参考复制的例子。

从上往下开始说实现。

拦截器签名:

@Intercepts(@Signature(
        type = Executor.class,
        method = "query",
        args = {
            MappedStatement.class, 
            Object.class, 
            RowBounds.class, 
            ResultHandler.class
        }
    )
)
public class ResultTypeInterceptor implements Interceptor {

拦截的Executor 的查询方法,拦截Executor的好处是自由度更高,但是难度也比其他的高,如果能够避免并发问题,实现起来会更容易。

拦截器处理代码

@Override
public Object intercept(Invocation invocation) throws Throwable {
    final Object[] args = invocation.getArgs();
    MappedStatement ms = (MappedStatement) args[0];
    Object parameterObject = args[1];
    //获取参数中设置的返回值类型
    Class resultType = getResultType(parameterObject);
    if(resultType == null){
        return invocation.proceed();
    }
    //复制ms,重设类型
    args[0] = newMappedStatement(ms, resultType);
    return invocation.proceed();
}

首先通过invocation.getArgs()可以得到当前执行方法的参数,这个参数列表和签名中定义的args中的类型是一致的。

其中第一个是(MappedStatement) args[0],第二个是参数对象parameterObject

首先要做的就是从参数对象中获得通过参数传递进来的resultType,使用 getResultType 方法获取。对参数不了解的人可能不清楚这里的参数可能是什么样,如果你想了解,可以看 深入了解MyBatis参数, 或者只是参考这个方法了解如何在这里处理参数。

得到返回值类型后,就该根据新的类型去创建一个resultMap,然后用这个新的类型根据现有的MappedStatement去创建一个新的MappedStatement,之所以创建一个新的而不是直接修改MappedStatement就是为了避免并发问题。

使用 newMappedStatement 创建新的MappedStatement对象,这个方法很重要,如果你要对MappedStatement做其他的修改,参考这个方法就可以。

通过下面代码就可以替换原有的ms :

args[0] = newMappedStatement(ms, resultType);

因为后续方式是使用参数(args)中的这个 ms,而不是 MyBatis Configuration中全局的ms,所以这里就避免了修改全局ms引起并发的问题。

虽然少数人能看到这里,但是我仍然建议,只学这段代码中的用法,尽可能不要去使用这个插件实现返回动态结果类型。直接使用会很不直观。但是呢,如果你是在开发一些通用框架方法,可以尝试在内部这么使用。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

isea533

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值