你是否还在为mybatis的多表关联查询而写xml烦恼,是否还在为动态组装查询条件烦恼,是否还在为此没有合适的解决方案烦恼?

mybatis-extension插件,解决开发过程中需要多表关联时需手写xml的烦恼,同样支持通过传入sql返回结果集。
纯mybatis原生支持,轻量级无侵入,可用于辅助mybatis-plus、tk.mybatis或者mybatis-generator使用。

1.运行依赖:
* mybatis>=3.5.2
* jdk>=1.8

2.特性:
2.1. 支持多表自定义join关联查询
2.2. 支持自定义AND/OR混合条件,排序,分页等
2.3. 支持GROUPBY/HAVING聚合查询
2.4. 支持自定义sql查询
2.5. 所有的列名选择功能支持lambda写法和字符串输入两种方式
2.6. 自动判断表名、列名与实体类名、字段名对应,多表关联时自动给表名起别名
2.7. 内置多种mybatis generator常用插件,例如批量新增、分页等

3.性能:
比mybatis-generator性能提高约30%,甚至比navicat中直接执行查询还快,与其他插件的对比待测试。

4.不足:
不支持单表多次重复关联
不支持复杂的三层嵌套AND/OR
不支持多表UNION

5.最佳实践:
轻量级(jar包小于100KB)无其他依赖,可辅助其他mybatis插件使用,为解决JOIN关联为生,并持续提供各种特性

6.使用说明(springboot示例,代码在本文下方下载)

6.1pom.xml中引入mybatis-extension的依赖(目前未放到maven中央仓库,可参考下方本地引入或者放入离线仓库)

<dependency>
<groupId>priv.rexsheng</groupId>
<artifactId>mybatis-extension</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/templates/mybatis-extension-1.0.0.jar</systemPath>
</dependency>

6.2 在启动类或者配置类上加入扫描mapper包

@MapperScan(basePackages = {"priv.rexsheng.mybatis.mapper"})

6.3配置mybatis拦截器

import priv.rexsheng.mybatis.interceptor.ResultTypeInterceptor;

@Configuration
public class InterceptorConfig {
@Bean
public ResultTypeInterceptor resultTypeInterceptor() {
return new ResultTypeInterceptor();
}
}

6.4正常使用mapper接口查询即可

import priv.rexsheng.mybatis.extension.TableQueryBuilder;
import priv.rexsheng.mybatis.mapper.DynamicMapper;
import priv.rexsheng.mybatis.test.dto.UserRoleQueryDto;
import priv.rexsheng.mybatis.test.entity.TUser;
import priv.rexsheng.mybatis.test.entity.UserRole; @SpringBootTest
public class MapperTest {
@Autowired
private DynamicMapper dao; /**
* 单表简单查询
*/
@Test
public void simpleSelect() {
//定义要查询的表的构建器
TableQueryBuilder<TUser> userQuery=TableQueryBuilder.from(TUser.class);
//定义要查询的字段
userQuery.select(TUser::getUserId,TUser::getUserName).and().like(TUser::getUserName, "%王二小%");
//执行查询
List<TUser> userList=dao.selectByBuilder(userQuery.build());
log.info("用户列表:{}",userList);
}
}

7. 各种查询使用说明

7.1单表查询

    @Test
public void simpleUserList() {
TableQueryBuilder<TUser> userQuery=TableQueryBuilder.from(TUser.class);
//只查询user_id与user_name两个字段,
userQuery.select(TUser::getUserId,TUser::getUserName)
.where().gt(TUser::getUserId, 3)
.or().isNotNull(TUser::getUpdateUser).isNotNull(TUser::getCreateTime);
//按照创建时间倒序取前10条
userQuery.orderByDesc(TUser::getCreateTime).take(10);
List<TUser> userList=dao.selectByBuilder(userQuery.build());
logger.info("用户列表:{}",userList);
}
-- ::32.272 DEBUG  --- [           main] p.r.m.m.D.selectByBuilder_TUser          : ==>  Preparing: SELECT user_id AS userId, user_name AS userName FROM t_user WHERE (user_id > ?) AND (update_user IS NOT NULL OR create_time IS NOT NULL) ORDER BY create_time DESC LIMIT ?
-- ::32.293 DEBUG --- [ main] p.r.m.m.D.selectByBuilder_TUser : ==> Parameters: (Integer), (Integer)
-- ::32.315 DEBUG --- [ main] p.r.m.m.D.selectByBuilder_TUser : <== Total:

7.2 单表分组聚合查询

    @Test
public void simpleUserGroup() throws ParseException {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
Date minDate=sdf.parse("2000-10-01"); TableQueryBuilder<TUser> userQuery=TableQueryBuilder.from(TUser.class);
//select create_user,max(create_time)
userQuery.select(TUser::getCreateUser).selectMax(TUser::getCreateTime, "createTime")
.where().gt(TUser::getUserId, 3);
//group by create_user having count(*)>1 and min(create_time)>='2020-10-01 00:00:00'
userQuery.groupBy(TUser::getCreateUser).havingCount(a->a.gt("*", 1)).havingMin(a->a.gte(TUser::getCreateTime,minDate));
List<TUser> userList=dao.selectByBuilder(userQuery.build());
logger.info("用户列表:{}",userList);
}
-- ::27.178 DEBUG  --- [           main] p.r.m.m.D.selectByBuilder_TUser          : ==>  Preparing: SELECT create_user AS createUser, MAX(create_time) AS createTime FROM t_user WHERE (user_id > ?) GROUP BY create_user HAVING (COUNT(*) > ? AND MIN(create_time) >= ?)
-- ::27.204 DEBUG --- [ main] p.r.m.m.D.selectByBuilder_TUser : ==> Parameters: (Integer), (Integer), -- ::00.0(Timestamp)
-- ::27.242 DEBUG --- [ main] p.r.m.m.D.selectByBuilder_TUser : <== Total:
-- ::27.250 INFO --- [ main] priv.rexsheng.mybatis.test.JoinTest : 用户列表:[TUser [userId=null, userName=null, createTime=Mon Aug :: CST , createUser=, updateTime=null, updateUser=null], TUser [userId=null, userName=null, createTime=Mon Aug :: CST , createUser=, updateTime=null, updateUser=null], TUser [userId=null, userName=null, createTime=Mon Aug :: CST , createUser=, updateTime=null, updateUser=null], TUser [userId=null, userName=null, createTime=Mon Aug :: CST , createUser=, updateTime=null, updateUser=null], TUser [userId=null, userName=null, createTime=Mon Aug :: CST , createUser=, updateTime=null, updateUser=null], TUser [userId=null, userName=null, createTime=Mon Aug :: CST , createUser=, updateTime=null, updateUser=null], TUser [userId=null, userName=null, createTime=Mon Aug :: CST , createUser=, updateTime=null, updateUser=null], TUser [userId=null, userName=null, createTime=Mon Aug :: CST , createUser=, updateTime=null, updateUser=null], TUser [userId=null, userName=null, createTime=Mon Aug :: CST , createUser=, updateTime=null, updateUser=null], TUser [userId=null, userName=null, createTime=Mon Aug :: CST , createUser=, updateTime=null, updateUser=null]]

7.3单表分页查询并转换返回类型

    @Test
public void simpleUserPageWithConvert() throws ParseException {
TableQueryBuilder<TUser> userQuery=TableQueryBuilder.from(TUser.class);
//查询第4页数据,并将查询的结果转换为实体类UserInfoDto
userQuery.select(TUser::getUserId,TUser::getCreateTime)
.selectAs(TUser::getUserName, "trueName")
.page(4, 10);
List<UserInfoDto> userPagedList=dao.selectByBuilder(userQuery.build(UserInfoDto.class));
logger.info("用户列表:{}",userPagedList);
}
-- ::13.542 DEBUG  --- [           main] p.r.m.m.D.selectByBuilder_UserInfoDto    : ==>  Preparing: SELECT user_id AS userId, create_time AS createTime, user_name AS trueName FROM t_user LIMIT ? OFFSET ?
-- ::13.565 DEBUG --- [ main] p.r.m.m.D.selectByBuilder_UserInfoDto : ==> Parameters: (Integer), (Integer)
-- ::13.587 DEBUG --- [ main] p.r.m.m.D.selectByBuilder_UserInfoDto : <== Total:
-- ::13.596 INFO --- [ main] priv.rexsheng.mybatis.test.JoinTest : 用户列表:[UserInfoDto [userId=, trueName=用户31, createTime=--31T15::], UserInfoDto [userId=, trueName=用户32, createTime=--31T15::], UserInfoDto [userId=, trueName=用户33, createTime=--31T15::], UserInfoDto [userId=, trueName=用户34, createTime=--31T15::], UserInfoDto [userId=, trueName=用户35, createTime=--31T15::], UserInfoDto [userId=, trueName=用户36, createTime=--31T15::], UserInfoDto [userId=, trueName=用户37, createTime=--31T15::], UserInfoDto [userId=, trueName=用户38, createTime=--31T15::], UserInfoDto [userId=, trueName=用户39, createTime=--31T15::], UserInfoDto [userId=, trueName=用户40, createTime=--31T15::]]
public class UserInfoDto {

    private Integer userId;

    private String trueName;

    private LocalDateTime createTime;
}

7.4多个表的关联查询

@Test
public void joinAndGroup() {
long startTime=System.currentTimeMillis();
TableQueryBuilder<TUser> userQuery=TableQueryBuilder.from(TUser.class);
userQuery.selectAs(TUser::getCreateUser,"userId").selectCount("*","count")
.groupBy(TUser::getCreateUser).havingCount(a->a.gt("*", 0)).havingMin(a->a.gte(TUser::getUserId, 1));
userQuery.where().gt(TUser::getUserId, 3).or().isNotNull(TUser::getCreateTime).isNotNull(TUser::getCreateUser); TableQueryBuilder<TRole> roleQuery=TableQueryBuilder.from(TRole.class);
roleQuery.selectMax(TRole::getRoleId, "roleId")
.where().isNull(TRole::getUpdateTime).and().isNotNull(TRole::getCreateUser);
roleQuery.groupBy(TRole::getRoleId).havingMax(a->a.gt(TRole::getRoleId, -1)); TableQueryBuilder<UserRole> userRoleQuery=TableQueryBuilder.from(UserRole.class);
userRoleQuery.and().isNotNull("user_id").isNotNull("role_id");
userRoleQuery.leftJoin(roleQuery).on(UserRole::getRoleId, TRole::getRoleId); userQuery.orderByCount("*").leftJoin(userRoleQuery).on(TUser::getUserId, UserRole::getUserId); List<UserCountDto> userList=dao.selectByBuilder(userQuery.build(UserCountDto.class));
long endTime=System.currentTimeMillis();
logger.info("复杂聚合:{}ms,data:{}",endTime-startTime,userList);
TestCase.assertNotNull(userList);
TestCase.assertTrue(userList.size()>0);
TestCase.assertNotNull(userList.get(0));
}
-- ::52.626 DEBUG  --- [           main] p.r.m.m.D.selectByBuilder_UserCountDto   : ==>  Preparing: SELECT a.create_user AS userId, COUNT(*) AS count, MAX(c.role_id) AS roleId FROM t_user AS a LEFT OUTER JOIN t_user_role AS b on a.user_id=b.user_id LEFT OUTER JOIN t_role AS c on b.role_id=c.role_id WHERE (a.user_id > ?) AND (a.create_time IS NOT NULL OR a.create_user IS NOT NULL) AND (b.user_id IS NOT NULL AND b.role_id IS NOT NULL) AND (c.update_time IS NULL) AND (c.create_user IS NOT NULL) GROUP BY a.create_user, c.role_id HAVING (COUNT(*) > ? AND MIN(a.user_id) >= ?) AND (MAX(c.role_id) > ?) ORDER BY COUNT(*)
-- ::52.627 DEBUG --- [ main] p.r.m.m.D.selectByBuilder_UserCountDto : ==> Parameters: (Integer), (Integer), (Integer), -(Integer)
-- ::52.630 DEBUG --- [ main] p.r.m.m.D.selectByBuilder_UserCountDto : <== Total:
-- ::52.631 INFO --- [ main] priv.rexsheng.mybatis.test.JoinTest : 复杂聚合:6ms,data:[UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=], UserCountDto [userId=, count=, roleId=]]

7.5 sql查询

@Test
public void testCountSql() {
List<Long> countList = dao.selectBySql("Select Count(*) from t_user", Long.class); logger.info("countList:{}", countList);
TestCase.assertNotNull(countList);
TestCase.assertTrue(countList.get(0)>0);
logger.info("countList.0:{}", countList.get(0));
} @Test
public void testSelectSql() {
List<TUser> userList = dao.selectBySql("Select user_id as userId,user_name as userName,create_time as createTime from t_user where user_id>10 limit 3", TUser.class); logger.info("userList:{}", userList);
TestCase.assertNotNull(userList);
TestCase.assertNotNull(userList.get(0).getCreateTime());
logger.info("userList.0:{}", userList.get(0));
}
-- ::33.860 DEBUG  --- [           main] p.r.m.m.DynamicMapper.selectBySql_TUser  : ==>  Preparing: Select user_id as userId,user_name as userName,create_time as createTime from t_user where user_id> limit
-- ::33.884 DEBUG --- [ main] p.r.m.m.DynamicMapper.selectBySql_TUser : ==> Parameters:
-- ::33.912 DEBUG --- [ main] p.r.m.m.DynamicMapper.selectBySql_TUser : <== Total:
-- ::33.921 INFO --- [ main] priv.rexsheng.mybatis.test.SqlTest : userList:[TUser [userId=, userName=用户11, createTime=Mon Aug :: CST , createUser=null, updateTime=null, updateUser=null], TUser [userId=, userName=用户12, createTime=Mon Aug :: CST , createUser=null, updateTime=null, updateUser=null], TUser [userId=, userName=用户13, createTime=Mon Aug :: CST , createUser=null, updateTime=null, updateUser=null]]
-- ::33.924 INFO --- [ main] priv.rexsheng.mybatis.test.SqlTest : userList.:TUser [userId=, userName=用户11, createTime=Mon Aug :: CST , createUser=null, updateTime=null, updateUser=null]
-- ::33.930 DEBUG --- [ main] p.r.m.m.DynamicMapper.selectBySql_Long : ==> Preparing: Select Count(*) from t_user
-- ::33.930 DEBUG --- [ main] p.r.m.m.DynamicMapper.selectBySql_Long : ==> Parameters:
-- ::33.935 DEBUG --- [ main] p.r.m.m.DynamicMapper.selectBySql_Long : <== Total:
-- ::33.936 INFO --- [ main] priv.rexsheng.mybatis.test.SqlTest : countList:[]
-- ::33.936 INFO --- [ main] priv.rexsheng.mybatis.test.SqlTest : countList.:

7.6 实体类结构

public class TUser {
private Integer userId; private String userName; private Date createTime; private Integer createUser; private Date updateTime; @ColumnName("update_user")
private Integer updateUser;
} public class TRole {
private Integer roleId; private String roleName; private String roleRemark; private LocalDateTime createTime; private Integer createUser; private Date updateTime; private Integer updateUser;
} @TableName("t_user_role")
public class UserRole {
private Integer Id; private Integer userId; private Integer roleId;
} public class UserInfoDto { private Integer userId; private String trueName; private LocalDateTime createTime;
} public class UserCountDto { private Integer userId; private Long count; private Integer roleId;
}

mybatis-extension-test.rar

Mybatis通用Join的实现(最终版)的更多相关文章

  1. Mybatis Generator通用Join的实现

    通常,我们使用Mybatis实现join表关联的时候,一般都是通过在xml或注解里写自定义sql实现. 本文通过Mybatis Generator的插件功能新增一个JoinPlugin插件,只要在配置 ...

  2. 最终版的Web(Python实现)

    天啦,要考试了,要期末考试了,今天把最终版的Python搭建Web代码先写这里记下了.详细的过程先不写了. 这次是在前面的基础上重写 HTTPServer 与 BaseHTTPRequestHandl ...

  3. 理解JavaScript设计模式与开发应用中发布-订阅模式的最终版代码

    最近拜读了曾探所著的<JavaScript设计模式与开发应用>一书,在读到发布-订阅模式一章时,作者不仅给出了基本模式的通用版本的发布-订阅模式的代码,最后还做出了扩展,给该模式增加了离线 ...

  4. Angular 2 最终版正式发布

    9月15日,Angular 2 的最终版正式发布了. 作为 Angular 1 的全平台继任者 -- Angular 2 的最终版,意味着什么? 意味着稳定性已经得到了大范围用例的验证: 意味着已经针 ...

  5. RHEL 6.0服务器安装Oracle 11G R2 最终版

    RHEL6安装Oracle 11g R2最终版 结合网上教程 服务器实战所得 1.使用DVD做yum源新建dvd挂载目录[root@fxq-dp ~]# mkdir /media/iso进入到DVD挂 ...

  6. Spring Boot MyBatis 通用Mapper插件集成

    Mybatis在使用过程中需要三个东西,每张表对应一个XXMapper.java接口文件,每张表对应一个XXMapper.xml文件,每张表对应一个Entity的Java文件.   其中XXMappe ...

  7. MyBatis通用Mapper开发

    通常情况下,MyBatis 的增删改查操作需要自己在相应xml中写相关语句, 但是运用相关工具,其实可以很方便的自动生成单表的所有增删改查(通用的多表联合查询还是需要自己写). 也可以根据具体环境,设 ...

  8. jQuery 3.0最终版发布,十大新特性眼前一亮

    jQuery 3.0在日前发布了最终的全新版本.从2014年10月,jQuery团队对这个主要大版本进行维护开始,web开发者社区便一直在期待着这一刻的到来,终于在2016年6月他们迎来了这一个最终板 ...

  9. 值得收藏的Mybatis通用Mapper使用大全。

    引言 由于小编的记性不太好,每次在写代码的时候总是把通用mapper的方法记错,所以今天把通用mapper的常用方法做一下总结,方便以后直接查看.好了,不废话啦. 引包 <!-- 通用Mappe ...

随机推荐

  1. Linux的VMWare中Centos7查看文件内容命令 (more-less-head-tail)

    一.More分页查看文件 more 命令类似 cat ,不过会以一页一页的形式显示,更方便使用者逐页阅读, 而最基本的指令就是按空白键(space)就往下一页显示, 按 b 键就会往回(back)一页 ...

  2. 虹软人脸识别—版本升级接口修改说明(C#)

    自虹软发布了免费.离线版本人脸识别SDK,在Github上陆续分享了各个版本对应的 C# Demo.因为公司项目一直在使用虹软的SDK,因此每当虹软官网有新版本发布,我都会进行了解,并结合Demo进行 ...

  3. Pytorch_第八篇_深度学习 (DeepLearning) 基础 [4]---欠拟合、过拟合与正则化

    深度学习 (DeepLearning) 基础 [4]---欠拟合.过拟合与正则化 Introduce 在上一篇"深度学习 (DeepLearning) 基础 [3]---梯度下降法" ...

  4. Java基础高级篇 NIO

    nio模型与io模型的对比 netty 是什么 怎么使用

  5. MyBatis深入理解参数

    目录 一.快速创建mapper文件 二.parameterType 三.MyBatis 传递参数 1. 一个简单参数(掌握) 一.快速创建mapper文件 由于每个接口都要创建一个对应的mapper文 ...

  6. Jdk1.8下的HashMap源码分析

    目录结构 一.面试常见问题 二.基本常量属性 三.构造方法 四.节点结构        4.1 Node类        4.2.TreeNode 五.put方法        5.1 key的has ...

  7. 防止多次引入js文件导致的重复注册点击事件

    前端代码中的js文件如果是动态引入的或者是某个事件操作进行注册的,那么重复的引入js文件或者多次触发注册事件会导致事件多次进行注册,造成不必要的麻烦,所以需要在每次注册之前将先前的事件进行取消,下面以 ...

  8. 生成kafka内部请求与响应的接口文档

    生成kafka内部请求与响应的接口文档 /** */ package com.code260.ss.kafka10demo; import java.io.File; import java.io.I ...

  9. Golang gRPC学习(04): Deadlines超时限制

    为什么要使用Deadlines 当我们使用gRPC时,gRPC库关系的是连接,序列化,反序列化和超时执行.Deadlines 允许gRPC客户端设置自己等待多长时间来完成rpc操作,直到出现这个错误 ...

  10. 救救孩子吧,到现在还搞不懂TCP的三次握手四次挥手

    本文在个人技术博客同步发布,详情可用力戳 亦可扫描屏幕右侧二维码关注个人公众号,公众号内有个人联系方式,等你来撩...   前几天发了一个朋友圈,发现暗恋已久的女生给我点了个赞,于是我当晚辗转反侧.彻 ...