零 XML 多表联查
mul-table-query
本文项目链接:超级好用的多数据源,多表联查工具, 专注 java 原生语法,零硬编码,零 xml
基于 mybatis-plus的多数据源配置,多表联查工具
效果:不改写任何的 mapper 和 mapper.xml,轻松实现 crud
多数据源配置
多数据源使用了 mybatis、mybatis-plus 以及 druid,mybatis相关的配置在 IMybatisPlusConfig.java 中,只有数据源和 druid 的配置需要单独配置。
为了支持多数据源,使用了单独的数据源配置,前缀为
system.jdbc.datasource
, 见 MultiDatasourceProperties.javaspring:datasource:# 使用alibaba的druid作为数据库连接池type: com.alibaba.druid.pool.DruidDataSourcedruid:# 连接池初始化连接数量initial-size: 5# 最小空闲连接数量min-idle: 5# 最大连接数量max-active: 20# 最大等待时间max-wait: 60000time-between-eviction-runs-millis: 60000# 单个连接在池中最小生存的时间,单位是毫秒min-evictable-idle-time-millis: 300000# 单个连接在池中最大生存的时间,单位是毫秒max-evictable-idle-time-millis: 900000# 以系统资源换稳定,连接空闲时检查有效性test-while-idle: true# 以系统资源换稳定,申请连接时检查有效性test-on-borrow: false# 以系统资源换稳定,回收连接时检查有效性test-on-return: false# 缓存statement,用本机内存换效率,但是通常可以关闭pool-prepared-statements: false# max-pool-prepared-statement-per-connection-size: 20# 监控过滤器web-stat-filter:enabled: trueexclusions:- "*.js"- "*.gif"- "*.jpg"- "*.png"- "*.css"- "*.ico"- "/druid/*"# druid 监控页面stat-view-servlet:enabled: trueurl-pattern: /druid/*reset-enable: falselogin-username: rootlogin-password: rootsystem:security:gateway-auth: truegateway-open: jdbc:datasource:filters: stat,wallcustomConfig: icu.helltab.fpi.pure.common.web.config.CustomJdbcConfigconnections:mysql:validationQuery: SELECT 1scanPackages: icu.helltab.fpi.pure.module.system.web.mapperdriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://172.16.40.153:3306/fpi-pure?allowMultiQueries=true&createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=trueusername: {pldd}password: {password}
使用数据源和设置事务管理,默认数据源只需要继承 DefaultBaseService.java ,事务管理默认也是添加好的
@Servicepublic class UserService extends DefaultBaseService<UserMapper, UserInfo> {}List<UserInfo> objects = userService.exList(sql -> {sql.select(UserInfo::getUsername).from(UserInfo.class);}, UserInfo.class);
自定义数据源配置修改钩子
在配置中指定钩子处理类
system:jdbc:datasource:# 配置钩子处理类customConfig: icu.helltab.fpi.pure.common.web.config.CustomJdbcConfig
编写钩子处理类, 下面这个例子中,我们修改了数据配置中的元数据注入策略
继承 MultiDatasourceProperties.java ,这里主要调用 register 将钩子注册到数据源初始化的生命周期里面去
通过
MybatisConfiguration configuration = factory.getConfiguration();
获取到配置,可以做自定义配置
package icu.helltab.fpi.pure.common.web.config;import cn.hutool.core.date.LocalDateTimeUtil;import com.baomidou.mybatisplus.core.MybatisConfiguration;import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;import icu.helltab.fpi.pure.common.web.config.security.HttpContextHolder;import icu.helltab.itool.multablequery.config.db.handler.MyMetaObjectHandler;import icu.helltab.itool.multablequery.config.db.multi.MultiDatasourceProperties;import org.apache.ibatis.reflection.MetaObject;import org.springframework.context.annotation.Configuration;import org.springframework.stereotype.Component;import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;/*** @author Helltab* @mail helltab@163.com* @date 2023/4/18 13:45* @desc 这是数据源自定义配置的钩子, 在这里可以更改 mybatis 的 MybatisSqlSessionFactoryBean 配置* @see*/public class CustomJdbcConfig extends MultiDatasourceProperties {static {// 注册数据源对应的回调函数, 可以修改 MybatisSqlSessionFactoryBean 的属性register("mysql", factory->{MybatisConfiguration configuration = factory.getConfiguration();GlobalConfigUtils.getGlobalConfig(configuration).setMetaObjectHandler(new CusMetaObjectHandler());});}public static class CusMetaObjectHandler extends MyMetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {this.setFieldValByName("createBy", HttpContextHolder.getUserNo(), metaObject);this.setFieldValByName("updateBy", HttpContextHolder.getUserNo(), metaObject);super.insertFill(metaObject);}@Overridepublic void updateFill(MetaObject metaObject) {this.setFieldValByName("updateBy", HttpContextHolder.getUserNo(), metaObject);super.updateFill(metaObject);}}}
新增数据源:
在配置文件中添加数据源,参见 ScanConfig.java
jdbc:datasource:filters: stat,wallcustomConfig: icu.helltab.fpi.pure.common.web.config.CustomJdbcConfigconnections:other_db: # 这里添加新数据源validationQuery: SELECT 1scanPackages: icu.helltab.fpi.pure.module.system.web.mapperdriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://172.16.40.153:3306/fpi-pure?allowMultiQueries=true&createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=trueusername: {pldd}password: {password}
添加数据源操作基类,参考 DefaultBaseService.java , 该数据源的操作都需要集成这个类
package icu.helltab.itool.multablequery.config.db.multi;import javax.annotation.Resource;import icu.helltab.itool.multablequery.config.db.CusBaseService;import org.springframework.transaction.annotation.Transactional;import com.baomidou.mybatisplus.core.mapper.BaseMapper;/*** 多数据源示例* todo* 所有的该数据源的 service 都需要继承本 Service*/@Transactional(transactionManager = "这里需要替换: ${数据源名}_TM", rollbackFor = Throwable.class)public class DemoBaseServiceDontUseMe<M extends BaseMapper<T>, T> extends CusBaseService<M, T> {/*** todo*/@Resource(name = "这里需要替换:${数据源名}_RUNNER")MySqlRunner mySqlRunner;protected MySqlRunner getMySqlRunner() {return mySqlRunner;}}
多表联查工具的使用
Startup
配置完数据源之后,即可使用多表联查了,当然也可以直接使用 MySqlRunner 中的方法,为了统一,推荐继承相关数据源的 Service,使用提供的查询能力。
继承相关数据源 Service
@Servicepublic class UserService extends DefaultBaseService<UserMapper, UserInfo> {}List<UserInfo> objects = userService.exList(sql -> {sql.select(UserInfo::getUsername).from(UserInfo.class);}, UserInfo.class);
直接使用 MysqlRunner
@Resource(name = DSConfig01.CONF.SQL_RUNNER)MySqlRunner mySqlRunner;List<Map<String, Object>> maps = mySqlRunner.selectLambda(sql -> {sql.select(UserInfo::getUsername).from(UserInfo.class);});
API 说明
特别说明:因为 lambda 表达式的原理是推断表名,因此无法优雅的自定义某个表的别名:这里采取一个策略:别名自动生成,但是如果一个表在环境中出现两次以上,则需要指定其编号,如第一次出现的表是 0,第二次出现是 1,默认为 0;
select
// 单个字段查询
1. select(UserInfo::getUsername);
2. select(UserInfo::getUsername, q.Alias(1));
// 多个字段查询
1. select(UserInfo::getUsername, UserInfo::getPasswrod);//类型检测预警
2. select(UserInfo::getUsername).select(UserInfo::getPassword);
3. select(UserInfo::getUsrname, q.Alias(1)).select(UserInfo::getPasswrod,q.Alias(2));
//子查询
1. sql.select(inner->{inner.selectCount(UserInfo::getId, q.Alias(1)).from(UserInfo.class).eq(UserInfo::getUsername, "张三");}, UserInfo::getCount);
// 原始查询
1. sql.selectRaw("Ada username", "1 password");
from
// 多表 inner join
sql.from(UserInfo.class, UserInfo.class, UserInfo.class, UserInfo.class);
// 子查询
sql.from(inner->{sql.select(UserInfo::getUsername, 0).from(UserInfo.class);
});
join
// left, 第二个参数使用 lambda 来做条件设置
sql.leftJoin(RoleInfo.class, j -> {j.eq(RoleInfo::getId, UserInfo::getRoleId);})
// right full 类似
where
条件判断第一个参数前面可以添加一个 nullJudge 的布尔值,默认为 true
true: 如果值为空,则不添加判断条件;
false: 如果值不为空,则添加判断条件,如 eq 会变为 is null, neq 会变为 is not null
// eq
sql.eq(UserInfo::getUsername, "张三");
sql.neq(UserInfo::getUsername, "张三");
// 小于等于
sql.le(UserInfo::getAge, 20);
// 小于
sql.lt(UserInfo::getAge, 20);
// 大于等于
sql.ge(UserInfo::getAge, 20);
// 大于
sql.gt(UserInfo::getAge, 20);
sql.in(UserInfo::getAge, 20, 18);
sql.notIn(UserInfo::getAge, 20, 18);
sql.notIn(UserInfo::getAge, inner->inner.selectRaw("1"));
sql.exists(UserInfo::getAge, inner->inner.selectRaw("1"));
sql.like(UserInfo::getUsername, "尚");
sql.notLike(UserInfo::getAge, "尚");
group
sql.group(UserInfo::getId).group(UserInfo::getAge).group(UserInfo::getUsername);// having
sql.having(UserInfo::getUsername, "='张三'");
sort
// true 正序, false 倒序
sql.order(UserInfo::getAge, true);
sql.order(UserInfo::getAge, 0, true);
function
// select count(a.id) from user_info a;
sql.selectCount(UserInfo::getId).from(UserInfo.class);
sql.selectCount(UserInfo::getId, 0).from(UserInfo.class);// select sum(a.id) from user_info a;
sql.selectSum(UserInfo::getId).from(UserInfo.class);
sql.selectSum(UserInfo::getId, 0).from(UserInfo.class);// 使用 concat({}, {}) 表达式来申明函数原型
// 使用 .bind(UserInfo::getAge).bind(UserInfo::getUsername) 来绑定参数
sql.eq(false, sql.fun("concat({}, {})").bind(UserInfo::getAge).bind(UserInfo::getUsername), "23")
CusBaseService 的能力
CusBaseService.java
新增
save; saveBatch;
修改
updateById; updateByIdBatch;
新增或修改
saveOrUpdate; saveOrUpdateBatch;
删除
remove; removeById; removeByIds;
查询
特别说明,分页返回结果为 HttpPagedInfo.java
包含 count 和 list 两个值
// 查询单个
getOne(sql->sql.select(UserInfo::getUsername));
// 查询数量getCount(sql->sql.eq(UserInfo::getUsername, "张三"));
// 查询是否存在has(sql->sql.eq(UserInfo::getUsername, "张三"));
// 查询列表list(sql->sql.eq(UserInfo::getUsername, "张三"));
// 查询分页, 默认接口传参 params: pageNum: 当前页数, pageSize: 分页大小(最大为 100)page(sql->sql.eq(UserInfo::getUsername, "张三"));// 多表联查能力, 需要指定 from 和返回值接收对象exOne(sql->sql.select(UserInfo::getUsername).from(UserInfo.class), UserInfoVo.class);
// 列表exList(sql->sql.select(UserInfo::getUsername).from(UserInfo.class), UserInfoVo.class);
// 分页exPage(sql->sql.select(UserInfo::getUsername).from(UserInfo.class), UserInfoVo.class);
零 XML 多表联查相关推荐
- 【mybatis】mybatis多表联查,存在一对多关系的,实体中使用List作为字段接收查询结果的写法...
实体如下: IntegralGoods 积分商品 IntegralGoodsImg 积分商品图片 ShelfLog 积分商品自动上架记录 IntegralGoods :IntegralGoodsIm ...
- SSM多表联查,原来如此方便,快捷!!!
SSM多表联查(注解/配置文件) 第一步:实体类 public class Contract {private Integer cid;private Integer lid;private Inte ...
- 使用mybatis进行四表联查
文章目录 一.问题背景 二.实际问题 三.问题解决 四.sql语句与XML映射文件 五.测试 一.问题背景 先数据库有用户表user.角色表role.菜单表menu.功能表funs和角色菜单关系表ro ...
- 健康管理系统第六天(移动端开发之体检预约_经典五表联查_调用阿里云提供的短信服务进行短信验证码发送)
一.移动端开发 1.移动端开发方式 随着移动互联网的兴起和手机的普及,目前移动端应用变得愈发重要,成为了各个商家的必争之地.例如,我们可以使用手机购物.支付.打车.玩游戏.订酒店.购票等, 以前只能通 ...
- MySQL单表查询与多表联查
1. 创建表 数据表的每行称为一条记录(record):每一列称为一个字段(field)[列之间以英文逗号隔开]. 简单语法:在当前数据库中创建一张表CREATE TABLE 表名(列名 列数据类型, ...
- 关于Mybatis-plus多表联查自定义sql分页查询
问题描述: 使用mybatis-plus进行开发过程中,单表得增删改查等都可以利用封装好的方法,而一些场景设计多表联合查询,且需要自定义字段的,就需要进行自定义sql 使用方法: 1.service中 ...
- MyBatis如何实现多表联查
目录 一.什么是级联 二.实现多表联查的方式 方式一:通过xml配置文件 1.一对一级联步骤 2.一对多联步骤 方式二:使用映射器注解方式 1.一对一映射 2.一对多映射 三.级联的缺陷 一.什么是级 ...
- Mybatis多表联查简简单单
Mybatis多表联查 1.一对一关系`association` 2.一对多关系`collection` 3.多对多关系 Mybatis中实现了对数据库中的数据进行封装,那么进行多表查询时就会遇到查询 ...
- 数据库多表查询 myBatis四表联查
查询目标 user表 role表 角色和菜单的关系 menu表 funs表(功能) 表和表的关系 1.user对role 是多对一role对user是一对多即一个user对应一个role 一个role ...
最新文章
- 信息资源管理的标准与法规
- OpenBSD 6.0 将移除 Linux 子系统以改进安全
- Nginx出现这么几个500怎么解决?
- Django,Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传
- angularjs与PHP,我应该混合AngularJS与PHP框架吗?
- SAP CDS view的文本格式的源代码是如何被ABAP后台解析的
- 记一次转不过弯的递归
- vb.net编写函数应该在哪里_编写代码时清晰至上
- 探讨微软团队开发利器VSTS安装及部署篇
- JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(三):两个Viewmodel搞定增删改查
- Directed Minimum Spanning Tree: Chu-Liu/Edmonds Algorithm
- [转]HTTP消息格式
- 最小割最大流算法matlab,matlab练习程序(最大流/最小割)
- 计算机求百钱买百鸡问题采用,5.5 百钱买百鸡问题
- QFD质量机能展开,了解一下呀!
- 青春使命网页制作html,青春的使命初中作文
- 交易原则Jesse Livermore 杰西·利弗莫尔
- USB摄像头测试网址
- swap()函数实现变量值的交换
- 计算机基础三: 二进制减法实现
热门文章
- Linux定时任务与开机自启动脚本
- mysql常用汉字库_MYSQL 常用总结【基础】
- cocos creator 3D | 拇指投篮 | 3D项目入门实战
- [原创] 在MFC中大家都习惯用CStdioFile来处理文本文件,可是为什么CStdioFile不叫CTextFile?
- 关闭浏览器后退出登录_升级后的相互宝要不要退出?相互宝怎么关闭?
- 云服务商将占据 80% CDN 市场份额,传统CDN或将终结
- 软件测试面试要注意的细节以及处理(自我介绍篇)
- 树莓派3B+如何完成对产品的升级改造
- Scrapy第十五篇:后起之秀-Playwright
- 人工智能会话代理在医疗保健中的有效性:系统综述