Mybatis通用Join的实现(最终版)
你是否还在为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通用Join的实现(最终版)的更多相关文章
- Mybatis Generator通用Join的实现
通常,我们使用Mybatis实现join表关联的时候,一般都是通过在xml或注解里写自定义sql实现. 本文通过Mybatis Generator的插件功能新增一个JoinPlugin插件,只要在配置 ...
- 最终版的Web(Python实现)
天啦,要考试了,要期末考试了,今天把最终版的Python搭建Web代码先写这里记下了.详细的过程先不写了. 这次是在前面的基础上重写 HTTPServer 与 BaseHTTPRequestHandl ...
- 理解JavaScript设计模式与开发应用中发布-订阅模式的最终版代码
最近拜读了曾探所著的<JavaScript设计模式与开发应用>一书,在读到发布-订阅模式一章时,作者不仅给出了基本模式的通用版本的发布-订阅模式的代码,最后还做出了扩展,给该模式增加了离线 ...
- Angular 2 最终版正式发布
9月15日,Angular 2 的最终版正式发布了. 作为 Angular 1 的全平台继任者 -- Angular 2 的最终版,意味着什么? 意味着稳定性已经得到了大范围用例的验证: 意味着已经针 ...
- RHEL 6.0服务器安装Oracle 11G R2 最终版
RHEL6安装Oracle 11g R2最终版 结合网上教程 服务器实战所得 1.使用DVD做yum源新建dvd挂载目录[root@fxq-dp ~]# mkdir /media/iso进入到DVD挂 ...
- Spring Boot MyBatis 通用Mapper插件集成
Mybatis在使用过程中需要三个东西,每张表对应一个XXMapper.java接口文件,每张表对应一个XXMapper.xml文件,每张表对应一个Entity的Java文件. 其中XXMappe ...
- MyBatis通用Mapper开发
通常情况下,MyBatis 的增删改查操作需要自己在相应xml中写相关语句, 但是运用相关工具,其实可以很方便的自动生成单表的所有增删改查(通用的多表联合查询还是需要自己写). 也可以根据具体环境,设 ...
- jQuery 3.0最终版发布,十大新特性眼前一亮
jQuery 3.0在日前发布了最终的全新版本.从2014年10月,jQuery团队对这个主要大版本进行维护开始,web开发者社区便一直在期待着这一刻的到来,终于在2016年6月他们迎来了这一个最终板 ...
- 值得收藏的Mybatis通用Mapper使用大全。
引言 由于小编的记性不太好,每次在写代码的时候总是把通用mapper的方法记错,所以今天把通用mapper的常用方法做一下总结,方便以后直接查看.好了,不废话啦. 引包 <!-- 通用Mappe ...
随机推荐
- 【BZOJ3307】雨天的尾巴 题解(树链剖分+树上差分)
题目链接 题目大意:给定一颗含有$n$个结点的树,每次选择两个结点$x$和$y$,对从$x$到$y$的路径上发放一带$z$类型的物品.问完成所有操作后每个结点发放最多的时哪种物品. 普通的树链剖分貌似 ...
- C++STL算法
1.不变序列算法 不会修改算法所作用的容器或对象 适用于顺序容器和关联容器,时间复杂度为O(n). 2.变值算法 会修改源区间或目标区间元素的值,值被修改的那个区间,不可属于关联容器. 3.删除算法 ...
- Flink的流处理API(二)
一.Environment 1,getExecutionEnvironment getExecutionEnvironment会根据查询运行的方式决定返回什么样的运行环境,是最常用的一种创建执行环境的 ...
- 团队项目-记账App
一.团队成员介绍 队长: 向瑜 博客园地址: https://www.cnblogs.com/xiangyu721/ 沟通能力较强,善于总结,能够正确分配团队任务.其次,有耐心学习新事物,发现新问题 ...
- java_流程控制语句、权限修饰符
判断语句 if语句第一种格式: if if(关系表达式){ 语句体; } if语句第二种格式: if…else if(关系表达式) { 语句体1; } else { 语句体2; } if语句第三种格式 ...
- JavaScript 模块封装
JavaScript 模块封装 前言介绍 在最早的时候JavaScript这门语言其实是并没有模块这一概念,但是随着时间的推移与技术的发展将一些复用性较强的代码封装成模块变成了必要的趋势. 在这篇文章 ...
- C#LeetCode刷题之#55-跳跃游戏(Jump Game)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3674 访问. 给定一个非负整数数组,你最初位于数组的第一个位置. ...
- C#LeetCode刷题之#1-两数之和(Two Sum)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3762 访问. 给定一个整数数组和一个目标值,找出数组中和为目标值 ...
- 02 Arduino-基于串口的学习
1串口通讯的基本理论知识,想必大家都熟悉,这里就不过多的介绍,这里主要花时间来介绍串口的应用 2参考内容如下所示: 3串口通讯所涉及到的函数分析 A if (Serial) 如果串口已经准备好了 ...
- Linux内核之 进程管理
正如上一篇我们提到过,进程是Linux系统中仅次于文件的基本抽象概念.正在运行的进程不仅仅是二进制代码,而是数据.资源.状态和虚拟的计算机组成.我们今天主要介绍进程的概念,组成,运行状态和生命周期等. ...