mybatis 用注解实现动态sql与连表查询(provider)

前言

以前大家在用mybatis的时候比较喜欢用mapper(xml),确实写sql比较方便。但是开发过程中,在xml与代码之间跳来跳去实在太繁琐了,而且添加或者删除字段的成本也比较大。

后面mybatis plus出现了,单表操作确实比较方便,但是遇到多表联查的时候,就比较难受了。

下面看看mybatis官方提供的注解实现方式。

注解

先写一个简单的查询,似乎比较轻松

@Mapper
public interface UserDao {

    @Select("select id, name, age, address from tb_user_info")
    List<UserInfo> listUserInfo();
}

那么,如何实现动态sql呢,仅仅依靠@Select注解是无法实现的,还需要借助mybatis提供的另一个注解@SelectProvider

@Mapper
public interface UserDao {

    @SelectProvider(type = UserInfoProvider.class, method = "listUserInfoByDto")
    List<UserInfo> listUserInfoByDto(@Param("userInfoDto") UserInfoDto userInfoDto);
   
}

创建一个辅助类, 方法名必须和注解中的method属性相对应, 字节码对象需要赋值给注解中的type属性,返回值就是要执行的sql。

public class UserInfoProvider {

    public String listUserInfoByDto(UserInfoDto userInfoDto) {
        String sql = "select id, name, age, address from tb_user_info";
        if(userInfoDto.getAddress() != null) {
            sql += " where address = #{userInfoDto.address}";
        }

        if(userInfoDto.getName() != null) {
            if(userInfoDto.getAddress() != null) {
                sql += " and name = #{userInfoDto.name}";
            } else {
                sql += " where name = #{userInfoDto.name}";
            }
        }
        return sql;
    }

}

SQL辅助类

虽然实现了功能,但是sql拼装太原始,而且一不小心就会出错。mybatis官方提供了一个工具类,我们可以借助他的工具类重新实现这个功能。

    public String listUserInfoByDtoPro(UserInfoDto userInfoDto) {
        return new SQL(){
            {
                SELECT("id, name, age, address");
                FROM("tb_user_info");
                if(userInfoDto.getAddress() != null) {
                    WHERE("address = #{userInfoDto.address}");
                }

                if(userInfoDto.getName() != null) {
                    WHERE("name = #{userInfoDto.name}");
                }
            }
        }.toString();
    }

巧妙地通过了双大括号的写法来初始化SQL工具类,并且屏蔽了sql拼接的过程。(双大括号写法,也是实现匿名类的一种方式,可以参考我的另一篇博客https://blog.csdn.net/qq_37855749/article/details/115913722

多表联查

@Mapper
public interface UserDao {

    @SelectProvider(type = UserInfoProvider.class, method = "listUserWithRole")
    List<UserInfo> listUserWithRole(@Param("userInfoDto") UserInfoDto userInfoDto);

}
public class UserInfoProvider {

    public String listUserWithRole(UserInfoDto userInfoDto) {
        return new SQL(){
            {
                SELECT("tui.id, tui.name, tui.age, tui.address, tr.role_name roleName");
                FROM("tb_user_info tui");
                LEFT_OUTER_JOIN("tb_user_role tur on tui.id = tur.user_id");
                LEFT_OUTER_JOIN("tb_role tr on tur.role_id = tr.id");
                //也可以用下面这种写法
                //LEFT_OUTER_JOIN("tb_user_role tur on tui.id = tur.user_id", "tb_role tr on tur.role_id = tr.id");

                if(userInfoDto.getAddress() != null) {
                    WHERE("address = #{userInfoDto.address}");
                }

                if(userInfoDto.getName() != null) {
                    WHERE("name = #{userInfoDto.name}");
                }
            }
        }.toString();
    }

}

Provider

回过头来看注解,mybatis提供了四种Provider

    @SelectProvider

    @InsertProvider

    @UpdateProvider

    @DeleteProvider

但是我们在编码的过程中发现,其实主要是看UserInfoProvider 这个类中method的返回值,理论上和注解名没有太多关系,那为什么还要区分四个注解呢?

其实是和sql语句执行的返回值有关

    //如果是SelectProvider也可以,但是返回值为null
    @UpdateProvider(type = UserInfoProvider.class, method = "updateUser")
    Integer updateUser(@Param("userInfo") UserInfo userInfo);

更新、删除、修改的返回值都是数值型(数据库中涉及的数据条数),而查询类sql执行的返回值可能是java对象,或者集合之类的,与其他类型不兼容。

比如上述的语句明显是个更新,我如果强行换成DeleteProvider,也能得到正确的结果,因为返回值都是数值类型的。

    @DeleteProvider(type = listUserWithRole.class, method = "updateUser")
    Integer updateUser(@Param("userInfo") UserInfo userInfo);

但肯定是不推荐的,语义化的注解让开发人员一看就知道当前的sql是什么类型的,直观且具体。

总结

Provider需要的是原始的sql,我们一般会定义辅助类提供所需要的sql。

不一定要用官方的SQL拼装工具类,比如我们公司有人就是自己造了个轮子。

但我觉得SQL工具类还是很好用的,并且看了看里面的源码,明天再写一篇简单的源码分析。(已写:https://blog.csdn.net/qq_37855749/article/details/115932800

另外要注意,Provider工具类(例子中的UserInfoProvider)可以有很多类,不一定要放在一个文件里。但是方法名不能相同,不要用重载,mybatis是根据方法名定位的。

资源

如果需要测试的源码:

https://gitee.com/DayCloud/dayrain-demo/tree/master/mybatis-provider

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值