首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >mybatis-plus 自定义SQL、一对多、分页查询过滤多租户

mybatis-plus 自定义SQL、一对多、分页查询过滤多租户

原创
作者头像
kinbug [进阶者]
修改于 2021-07-28 10:01:58
修改于 2021-07-28 10:01:58
5.2K00
代码可运行
举报
文章被收录于专栏:IT进修之路IT进修之路
运行总次数:0
代码可运行

前言

        这几天在使用的mybatis-plus的时候,在遇见复杂业务的时候遇见的一些租户过滤问题,面对多表关联查询的时候、自定义sql的时候,或者说一对多的时候,其中一个查询等功能过滤过滤租户的解决方案。

        在一个缓存命中率不高的场景中,分页很多时候不能依赖主数据分页查询再遍历查询的方式来组装数据的时候,就会遇见自定义sql 或者是一对多查询。这个时候如果用mybatis-plus的多租户就会很有问题。

自定义sql分页查询方法:

Mapper.xml
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<select id="getPageUser" resultMap="userResult">
	select * from user ${ew.customSqlSegment}
</select>

这里的SQL很简单,根据自己的业务变动sql。${ew.customSqlSegment} 很多人不了解这个哈,就是:Wrapper<Material> queryWrapper 转化后的sql。还不明白的话,继续看...

Mapper.java
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Mapper
public interface UserMapper extends BaseMapper<User> {
	 
	 List<User> getPageUser(@Param(Constants.WRAPPER) Wrapper<Material> queryWrapper);
}

看清楚哦,这里返回的是一个list集合。${ew.customSqlSegment} 就是指的这的queryWrapper,ew就是Constants.WRAPPER的值。

ServiceImpl.java
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public PageInfo<User> getPageUser(PageParam<UserQueryParam> param, Wrapper<User> queryWrapper) {
	// TODO Auto-generated method stub
	PageHelper.startPage(param.getCurrent(), param.getPageSize());
	List<User> list = baseMapper.getPageUser(queryWrapper);
	PageInfo<User> pageInfo = new PageInfo<User>(list);
	return pageInfo;
}

PageParam 就是组装了,当前页码与页行数,UserQueryParam 是查询条件:用于组装在queryWrapper中。

Service.java、Controller.java我就直接省了....

多租户面临的情况:

mybatis-plus 多住户配置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;

import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;

/**
 * @author miemie
 * @since 2018-08-10
 */
@Configuration
public class MybatisPlusConfig {


    @Bean
	public MybatisPlusInterceptor mybatisPlusInterceptor() {
		MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
		// 分页插件
		interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
		// 租户拦截 PaginationInterceptor
		interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(tenantLineHandler()));
		return interceptor;
	}

	/**
	 * 租戶設置
	 * @return SQL解析过滤
	 */
	private TenantLineHandler tenantLineHandler() {
		return new TenantLineHandler() {
			@Override
			public Expression getTenantId() {
				return new StringValue(getTenantNo());
			}

			// 在数据库中租户关联的字段
			@Override
			public String getTenantIdColumn() {
				return "tenant_no";
			}

			// 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件
			@Override
			public boolean ignoreTable(String tableName) {
				List<String> list = new ArrayList<String>();
				list.add("user");
				list.add("a_region_city");
				return list.contains(tableName);
			}
		};
	}

}

ignoreTable 就是根据表名进行过滤租户,全表所有的sql都不会拼接租户的sql。

那么某个表单独一个sql怎么取消租户过滤呢?

官方方法:就是在Mapper.java的方法上加如下注解

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@InterceptorIgnore(tenantLine = "1")

比如加在Mapper.java的getPageUser方法上:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Mapper
public interface UserMapper extends BaseMapper<User> {
	 @InterceptorIgnore(tenantLine = "1")
	 List<User> getPageUser(@Param(Constants.WRAPPER) Wrapper<Material> queryWrapper);
}

执行分页查询发现报错,页数对不上,原因是分页查询有:select count(*) from user 的语句,这个是分页工具的能力,如何解决呢? 能通过传递租户ID就不自动拼接sql吗? 能让增加、修改、删除需要租户,而查询不需要吗?

统一回答:当然可以

具体思路与方法输入下:

通过mybatis-plus 多住户配置MybatisPlusConfig可看出租户拦截器是TenantLineInnerInterceptor,查看源码发现有如下方法:

  1. processSelect
  2. processSelectBody
  3. processInsert
  4. processUpdate
  5. processDelete

你可以重写对应的方法,就可以实现sql中某一个方法不加租户拼接举例如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {

	/**
	 * 分页插件
	 *
	 * @return
	 */
	@Bean
	public MybatisPlusInterceptor mybatisPlusInterceptor() {
		MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
		// 分页插件
		interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
		// 租户拦截 PaginationInterceptor
		interceptor.addInnerInterceptor(tenantLineInnerInterceptor(tenantLineHandler()));
		return interceptor;
	}

	TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantLineHandler tenantLineHandler) {
		return new TenantLineInnerInterceptor(tenantLineHandler) {
			
			//重写查询,租户拦截问题
			@Override
			protected void processSelect(Select select, int index, String sql, Object obj) {
				if (sql.contains("tenant_no")) {
					return;
				}
				processSelectBody(select.getSelectBody());
				List<WithItem> withItemsList = select.getWithItemsList();
				if (!CollectionUtils.isEmpty(withItemsList)) {
					withItemsList.forEach(this::processSelectBody);
				}
			}
		};
	}

	/**
	 * 租戶設置
	 * @return SQL解析过滤
	 */
	private TenantLineHandler tenantLineHandler() {
		return new TenantLineHandler() {
			@Override
			public Expression getTenantId() {
				return new StringValue(getTenantNo());
			}

			// 在数据库中租户关联的字段
			@Override
			public String getTenantIdColumn() {
				return "tenant_no";
			}

			// 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件
			@Override
			public boolean ignoreTable(String tableName) {
				List<String> list = new ArrayList<String>();
				list.add("user");
				list.add("a_region_city");
				return list.contains(tableName);
			}
		};
	}
}

 现在再去查询发现分页的统计是ok的了,数量也能对的上。

最后说说一对多sql实现

VO代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * @Description: 说明
 * @author: kinbug
 * @date: 2021年07月22日
 */
@Data
public class MaterialVO {
	private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";

	@ApiModelProperty(value = "id")
	private Long id;

	@ApiModelProperty(value = "编号")
	private String no;

	@ApiModelProperty(value = "租户编号")
	private String tenantNo;

	@ApiModelProperty(value = "产品名字")
	private String name;

	@ApiModelProperty(value = "产品类别")
	private String categoryNo;

	@ApiModelProperty(value = "商品单位")
	private String unit;

	@ApiModelProperty(value = "产品编号")
	private String serialNo;

	@ApiModelProperty(value = "产品型号")
	private String model;

	@ApiModelProperty(value = "产品规格")
	private String standard;

	@ApiModelProperty(value = "产品状态(0未启用,1启用)")
	private Boolean enabled;

	@ApiModelProperty(value = "制造商名")
	private String mfrs;

	@ApiModelProperty(value = "删除标记(0未删除,1是删除)")
	private Boolean deleteFlag;

	@ApiModelProperty(value = "创建人")
	private String createUno;

	@ApiModelProperty(value = "添加时间")
	@JsonFormat(pattern = DATE_FORMAT)
	private Date createTime;

	@ApiModelProperty(value = "修改人")
	private String updateUno;

	@ApiModelProperty(value = "修改时间")
	@JsonFormat(pattern = DATE_FORMAT)
	private Date updateTime;
	
	@ApiModelProperty(value = "商品条码价格")
	private List<MaterialExtend> materialExtends;
	
	@ApiModelProperty(value = "商品图片")
	private List<MaterialImage> materialImages;

	public void copyMaterial(Material material) {
		BeanUtils.copyProperties(material, this);
	}
}
Mapper.XML
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!-- start查询一个商品信息 -->
	<resultMap type="com.turtle.eos.domian.vo.material.MaterialVO" id="materialResult">
		<result column="id" property="id" />
  		<result column="no" property="no" />
  		<result column="name" property="name" />
  		<result column="category_no" property="categoryNo" />
  		<result column="unit" property="unit" />
  		<result column="serial_no" property="serialNo" />
  		<result column="model" property="model" />
  		<result column="standard" property="standard" />
  		<result column="enabled" property="enabled" />
  		<result column="mfrs" property="mfrs" />
  		<result column="delete_flag" property="deleteFlag" />
  		<result column="tenant_no" property="tenantNo" />
  		<result column="create_time" property="createTime" />
  		<result column="update_time" property="updateTime" />
  		<result column="update_uno" property="updateUno" />
  		<result column="create_uno" property="createUno" />
  		
        <collection property="materialExtends" ofType="com.turtle.eos.entity.material.MaterialExtend" select="queryMaterialExtends" column="no">
	          
    	</collection>
		<collection property="materialImages" ofType="com.turtle.eos.entity.material.MaterialImage" select="queryMaterialImages" column="no">
	          
    	</collection>
    </resultMap>
	<select id="getMaterial" resultMap="materialResult">
		select * from m_material ${ew.customSqlSegment}
	</select>
	
	<select id="queryMaterialExtends" resultType="com.turtle.eos.entity.material.MaterialExtend">
		select * from m_material_extend where delete_flag = 0 and material_no = #{no}
	</select>
	
	<select id="queryMaterialImages" resultType="com.turtle.eos.entity.material.MaterialImage">
		select * from m_material_image where delete_flag = 0 and material_no = #{no}
	</select>
	<!-- end查询一个商品信息 -->

值得注意的是collection中的column是给下一个queryMaterialExtends查询传递的值。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
初识MCP,尝试从码农的角度来理解
​1. 解决“工具碎片化”痛点​ 传统AI集成需为每个外部工具(数据库、API等)单独开发适配层。例如,连接数据库和邮件系统需分别调用psycopg2和smtplib库,代码冗余率高。MCP通过统一协议标准​(类似USB-C接口)
软件架构师Michael
2025/06/11
3110
Python开发MCP 入门指南
官网:https://gofastmcp.com/getting-started/welcome
码之有理
2025/05/06
1.3K0
从知识图谱到精准决策:基于MCP的招投标货物比对溯源系统实践
从最初对人工智能的懵懂认知,到逐渐踏入Prompt工程的世界,我们一路探索,从私有化部署的实际场景,到对DeepSeek技术的全面解读,再逐步深入到NL2SQL、知识图谱构建、RAG知识库设计,以及ChatBI这些高阶应用。一路走来,我们在AI的领域里一步一个脚印,不断拓展视野和能力边界。如果你是第一次点开这篇文章,或许会觉得今天的内容稍有挑战。但别担心,之前我创作的的每一篇人工智能文章都是精心铺设学习前置的基石。如果希望更深入地理解接下来我们将讨论的「从知识图谱到精准决策:基于MCP的招投标货物比对溯源系统实践」这一主题,不妨先回顾我以往分享过的基础与进阶文章,相信它们会让你的学习过程更加顺畅自然。
fanstuck
2025/05/08
7648
从知识图谱到精准决策:基于MCP的招投标货物比对溯源系统实践
MCP、传统API与函数调用的解析
传统API通过预定义端点暴露功能,客户端需严格遵循接口规范进行交互。其核心特征包括:
蝉羽
2025/06/04
4770
MCP、传统API与函数调用的解析
大语言模型交互协议 MCP SDK Go-MCP 正式开源!
今天,ThinkInAI 团队(前身为 GoCN 团队)自豪地宣布,基于 Go 语言的大模型交互协议(Model Context Protocol)SDK —— Go-MCP 正式开源!
深度学习与Python
2025/04/10
2K0
大语言模型交互协议 MCP SDK Go-MCP 正式开源!
打起来了!MCP VS A2A,谁才是Agent的未来事实标准?
谷歌在MCP协议快速发展之际推出A2A协议,定位为智能体Agent间的协调协议。本文通过具体的案例介绍了MCP和A2A的细节,通过同一案例在MCP与A2A两种模式下的实现差异,认为A2A模式下的 Agent 能够通过与大模型深度交互,交付更具价值的功能特性,从而更有效地吸引开发者群体。此外,A2A架构赋予每个 Agent 自主选择底层大模型的权利,这一开放性设计也将进一步吸引大模型供应商参与生态构建。 与行业普遍认为两种协议具有互补性的共识不同,笔者认为MCP和A2A协同发展仍面临显著挑战。文中还列举了 K8s 与Docker 的历史协同案例作为类比,将技术演进的想象空间留给读者。 限于笔者水平,本文部分观点可能存在错误,恳请大家不吝赐教。
腾讯云开发者
2025/04/30
5190
打起来了!MCP VS A2A,谁才是Agent的未来事实标准?
[MCP学习笔记]MCP服务网格化:Istio控制平面改造
在云原生时代,服务网格(Service Mesh)已经成为微服务架构中不可或缺的组成部分。Istio 作为服务网格领域的佼佼者,为微服务提供了流量管理、安全控制和可观测性等功能。然而,随着业务规模的不断扩大和服务数量的急剧增加,Istio 控制平面在性能、可扩展性和资源隔离等方面逐渐暴露出一些问题。
二一年冬末
2025/05/09
1820
[MCP学习笔记]MCP服务网格化:Istio控制平面改造
基于腾讯云MCP的可视化日报生成系统:3秒完成高效自动化绩效管理
在某跨境电商企业的运营管理中,每日需收集1000+员工的文字/数据型日报,存在三大核心问题:
鼓掌MVP
2025/05/18
1580
成熟工程师 1 天完成调试,AI 工程实践被 MCP 彻底颠覆?
去年 11 月,Anthropic 发布了模型上下文协议 (MCP),这是 AI 应用程序组件与外部系统或工具之间通信的新标准。开发者社区迅速采用了该协议,并部署了超过 1000 个 MCP 服务器。如今,随着 AWS、GitHub 等巨头公司,甚至 Anthropic 的“竞争对手”OpenAI 也正式采用 MCP,MCP 在商业领域也获得了越来越多的关注。
深度学习与Python
2025/06/08
1580
成熟工程师 1 天完成调试,AI 工程实践被 MCP 彻底颠覆?
基于强化学习的智能体自主决策
在现代人工智能(AI)领域,智能体的互操作性是实现系统协同的关键要素。随着多个供应商提供不同的智能体产品,如何在复杂的生态系统中构建互操作性的基础设施变得尤为重要。本文将探讨如何构建一个支持多供应商智能体互操作性的生态体系,重点讨论多供应商环境中的MCP(Multi-Agent Collaborative Platform)架构,解决不同智能体之间的协作与资源共享问题。
一键难忘
2025/07/13
550
从LSP到MCP:基础架构、核心组件和协议未来
本文系统性地介绍了MCP(Model Context Protocol)协议的设计理念、核心架构及技术实现,旨在通过标准化AI大模型与外部系统的交互方式,解决大模型工具调用和实时信息获取的行业痛点。文章通过对比API、LSP等历史协议,深入解析了MCP协议的三大核心组件与创新传输机制,并对协议的未来发展进行展望。
腾讯云开发者
2025/06/26
2190
从LSP到MCP:基础架构、核心组件和协议未来
腾讯云MCP数据智能处理:简化数据探索与分析的全流程指南
在当今数据驱动的商业环境中,企业面临着海量数据处理和分析的挑战。腾讯云MCP(Managed Cloud Platform)提供的数据智能处理解决方案,为数据科学家和分析师提供了强大的工具集,能够显著简化数据探索、分析流程,并增强数据科学工作流的效率。本文将深入探讨如何利用腾讯云MCP的各项功能来优化您的数据科学实践。
摘星.
2025/05/16
2030
腾讯云MCP数据智能处理:简化数据探索与分析的全流程指南
Spring AI 1.0 正式发布!核心内容和智能体详解
在经历了八个里程碑式的版本之后(M1~M8),Spring AI 1.0 正式版本,终于在 2025 年 5 月 20 日正式发布了,这是另一个新高度的里程碑式的版本,标志着 Spring 生态系统正式全面拥抱人工智能技术,并且意味着 Spring AI 将会给企业带来稳定 API 支持。
磊哥
2025/05/22
2.1K0
Spring AI 1.0 正式发布!核心内容和智能体详解
一文详解模型上下文协议(MCP):打通大模型与业务场景的关键
暂且抛开MCP,这23年的时候开始搭建AI Agent智能体,对第三方插件API进行交互的时候,我就开始设想能不能自己做一个通用代码协议框架,以后AI团队统一用这个协议,方便大家code review,更好协作。AI要触及到业务就必然逃不过与业务端接口或是数据进行联通,但是之前都没有标准的交互协议,需要理解各个三方接口和协议是比较费时的事情,但如果有类似像Java工程开发标准,那么我们就很方便开发第三方接口了,不会存在那么多不同开发形态的代码,方便维护。MCP协议出现之后发现大家都在慢慢融入到开源协议框架中,故而再对一些不了解MCP的朋友详细解述这一框架协议,以后必然是以开源协议为主导的代码生态。
fanstuck
2025/03/27
3.1K2
一文详解模型上下文协议(MCP):打通大模型与业务场景的关键
# 一文读懂 MCP!大模型如何用它连接世界,打造更智能的 AI Agent?
最近,MCP(模型上下文协议,Model Context Protocol)在 AI 圈子里火了起来。然而,很多人对它的概念仍然感到困惑,包括我在最初接触时也是如此。
AgenticAI
2025/03/19
2.9K0
# 一文读懂 MCP!大模型如何用它连接世界,打造更智能的 AI Agent?
理解MCP 通信机制
MCP核心遵循客户端-服务器架构,其中主机应用程序(MCP client)可以连接到多个服务器(MCP Server):
windealli
2025/04/21
6690
理解MCP 通信机制
Apache Doris × AI 的5个应用场景(附完整案例)
好比用自然语言就能直接查询Doris数据,并结合AI自动进行决策分析,RAG技术让企业知识库变得超级智能,ChatBI让人人都能成为数据分析师..."
一臻数据
2025/04/09
1K0
Apache Doris × AI 的5个应用场景(附完整案例)
一文读懂 MCP!大模型如何用它连接世界,打造更智能的 AI Agent?
最近,MCP[1](模型上下文协议,Model Context Protocol)在 AI 圈子里火了起来。然而,很多人对它的概念仍然感到困惑,包括我在最初接触时也是如此。
AgenticAI
2025/03/19
1.6K0
一文读懂 MCP!大模型如何用它连接世界,打造更智能的 AI Agent?
在浏览器使用 MCP,纯边缘函数实现 MCP Client & Server
下面是一个在线示例:https://mcp-on-edge.edgeone.site/?from=eo
EdgeOne 小助手
2025/05/09
1K0
工良出品 | 长文讲解 MCP 和案例实战
示例项目地址:https://github.com/whuanle/mcpdemo
痴者工良
2025/04/22
1.9K1
工良出品 | 长文讲解 MCP 和案例实战
推荐阅读
相关推荐
初识MCP,尝试从码农的角度来理解
更多 >
LV.0
百度研发工程师
目录
  • 前言
    • 自定义sql分页查询方法:
      • Mapper.xml
      • Mapper.java
      • ServiceImpl.java
      • Service.java、Controller.java我就直接省了....
    • 多租户面临的情况:
    • 最后说说一对多sql实现
      • Mapper.XML
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档