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 ...
随机推荐
- 移动物体监控系统-sprint2摄像头子系统开发
一.摄像头使能驱动 1.1 摄像头软件系统构架 摄像头采集系统按照上图,硬件(摄像头)->摄像头驱动 ->V4L2接口规范 ->图像采集(应用).V4L2将不同类型的摄像头设备按照统 ...
- JS 执行机制笔记
js同步和异步同步 前一个任务结束以后再执行下面一个任务,程序的执行顺序与任务的排列顺序是一致的 同步任务都在主线程上执行,形成一个执行线 异步 前一个任务没结束之前程序还可以执行别的任务 j ...
- javascript Math对象 、Date对象笔记
Math对象 Math 是一个内置对象, 它具有数学常数和函数的属性和方法.不是一个函数对象. Math数学对象不是构造函数使用的时候不需要new来调用,可以直接使用里面的属性和方法 ...
- 剑指offer之字符串是否为数值
1. 题目 这是<剑指offer>上的一道题,刚开始觉得这是一道挺简单的题目,后来发现自己太年轻了,考虑的因素太少了,思考了而是分钟还是无从下手,看了作者的思路深深被他折服了,题目如下: ...
- 【API进阶之路】高考要考口语?我用多模态评测API做了一场10w+刷屏活动
摘要:闲着没事用多模态评测API做了一个测评英语口语的互动小游戏,居然成了一场10万人参与的刷屏级活动. 上一期故事说到,我成为了公司技术委员会副主席,上任后的第一件事是建立了一个云容器化的研发资料库 ...
- C# 使用代理实现线程间调用
实现功能: 后台线程改变窗体控件(flowLayoutPanel1)的状态. 利用 this.flowLayoutPanel1.InvokeRequired == false,可以知道是主线程调用的自 ...
- CSS 学习第一天
css的三种引入方式: 1.内嵌:直接在标签中添加style属性 格式:<标签名 style="样式1:样式值1:样式2:样式值2"> 2.内联:在head标签中添加& ...
- C#LeetCode刷题之#674-最长连续递增序列( Longest Continuous Increasing Subsequence)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3734 访问. 给定一个未经排序的整数数组,找到最长且连续的的递增 ...
- 漏洞重温之XSS(上)
漏洞简介 跨站脚本攻击(XSS)是指恶意攻击者往Web页面里插入恶意Script代码,当用户浏览页面之时,嵌入web网页中的script代码会被执行,从而达到恶意攻击用户的目的. XSS漏洞通常是通过 ...
- vue cli 中关于vue.config.js中chainWebpack的配置
Vue CLI 的官方文档上写:调整webpack配置最简单的方式就是在vue.config.js中的configureWebpack选项提供一个对象. Vue CLI 内部的 webpack 配置 ...