使用Mapper方式进行联表查询

有些业务场景需要查询两张表的数据,这时候就需要用到联表查询。下面将介绍两种方式进行联表查询。

场景:A模型页面,查询条件中包含B模型字段

模型A

@Model.model(YesOne.MODEL_MODEL)
@Model(displayName = "YesOne", summary = "YesOne")
public class YesOne extends IdModel {

    public static final String MODEL_MODEL = "top.YesOne";

    @Field.Integer
    @Field(displayName = "YesId")
    private Long yesId;

    @Field.String
    @Field(displayName = "名字")
    private String name;

    @Field.String
    @Field(displayName = "科目名字")
    private String professionalName;

    @Field(displayName = "关联YesTwo")
    @Field.many2one
    @Field.Relation(relationFields = {"yesId"},referenceFields = {"id"})
    private YesTwo yesTwo;

}

模型B

@Model.model(YesTwo.MODEL_MODEL)
@Model(displayName = "YesTwo", summary = "YesTwo")
public class YesTwo extends IdModel {

    public static final String MODEL_MODEL = "top.YesTwo";

    @Field.Integer
    @Field(displayName = "科目id")
    private Long professionalId;

    @Field.String
    @Field(displayName = "科目名字")
    private String professionalName;

}

1. 使用in的方式查询

通过B模型的查询条件查询出符合条件的所有数据ID,再根据这个ID去A模型里面查询出所需的数据。

    @Function.Advanced(displayName = "查询列表", type = FunctionTypeEnum.QUERY, category = FunctionCategoryEnum.QUERY_PAGE, managed = true)
    @Function(openLevel = {FunctionOpenEnum.LOCAL, FunctionOpenEnum.REMOTE, FunctionOpenEnum.API})
    public Pagination<YesOne> queryPage(Pagination<YesOne> page, IWrapper<YesOne> queryWrapper) {

        String professionalName = (String) queryWrapper.getQueryData().get("professionalName");
        if (StringUtils.isNotBlank(professionalName)) {
            List<Long> yesTwoId = new YesTwo().queryList(Pops.<YesTwo>lambdaQuery()
                            .from(YesTwo.MODEL_MODEL)
                            .eq(YesTwo::getProfessionalName, professionalName))
                    .stream().map(YesTwo::getId)
                    .collect(Collectors.toList());
            LambdaQueryWrapper<YesOne> wq = Pops.<YesOne>lambdaQuery().from(YesOne.MODEL_MODEL);
            if (CollectionUtils.isNotEmpty(yesTwoId)) {
                wq.in(YesOne::getYesId, yesTwoId);
            }
            return new YesOne().queryPage(page, wq);
        }
        return new YesOne().queryPage(page, queryWrapper);
    }

2. 使用mapper的方式查询

利用sql的方式去直接查询出结果。使用联表查询的方式查询

    @Autowired
    private YesOneQueryMapper yesOneQueryMapper;

    @Function.Advanced(displayName = "查询列表", type = FunctionTypeEnum.QUERY, category = FunctionCategoryEnum.QUERY_PAGE, managed = true)
    @Function(openLevel = {FunctionOpenEnum.LOCAL, FunctionOpenEnum.REMOTE, FunctionOpenEnum.API})
    public Pagination<YesOne> queryPage(Pagination<YesOne> page, IWrapper<YesOne> queryWrapper) {

        try (DsHintApi dsHint = DsHintApi.model(YesOne.MODEL_MODEL)) {
            String professionalName = (String) queryWrapper.getQueryData().get("professionalName");
            String yesOneTable = PamirsSession.getContext().getModelCache().get(YesOne.MODEL_MODEL).getTable();
            String yesTwoTable = PamirsSession.getContext().getModelCache().get(YesTwo.MODEL_MODEL).getTable();
            StringBuffer where = new StringBuffer().append("a.is_deleted = 0").append(CharacterConstants.SEPARATOR_BLANK)
                    .append(SqlConstants.AND).append(CharacterConstants.SEPARATOR_BLANK)
                    .append("b.is_deleted=0").append(CharacterConstants.SEPARATOR_BLANK);
            if (StringUtils.isNotBlank(professionalName)) {
                where.append(SqlConstants.AND).append(CharacterConstants.SEPARATOR_BLANK).append("b.").
                        append(PStringUtils.fieldName2Column(LambdaUtil.fetchFieldName(YesOne::getProfessionalName))).append(CharacterConstants.SEPARATOR_BLANK).
                        append(SqlConstants.EQ).append(professionalName);
            }
            StringBuffer limit = new StringBuffer().append(page.getStart() + " , " + page.getSize());
            List<YesOne> yesOnes = yesOneQueryMapper.unionTableQuery(yesOneTable, yesTwoTable, where.toString(), limit.toString());
            Long total = yesOneQueryMapper.queryTotal(yesOneTable, yesTwoTable, where.toString());
            page.setTotalElements(total);
            page.setContent(yesOnes);
        }
        return page;
    }

接口

package pro.shushi.pamirs.top.core.service;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import pro.shushi.pamirs.top.api.model.YesOne;

import java.util.List;

@Mapper
public interface YesOneQueryMapper {

    @Select("<script>"
            + "SELECT "
            + "a.id , "
            + "a.name, "
            + "b.professional_name as professionalName "
            + "FROM ${yesOne} a "
            + "INNER JOIN ${yesTwo} b ON a.yes_id = b.id "
            + "<if test='whereConditions != null'>"
            + "where (${whereConditions}) "
            + "</if>"
            + "ORDER BY a.id ASC "
            + "<if test='limitConditions != null'>"
            + "LIMIT ${limitConditions} "
            + "</if>"
            + "</script>")
    List<YesOne> unionTableQuery(@Param("yesOne") String yesOne, @Param("yesTwo") String yesTwo, @Param("whereConditions") String whereConditions, @Param("limitConditions") String limitConditions);

    @Select("<script>"
            + "SELECT count(a.id )"
            + "FROM ${yesOne} a "
            + "INNER JOIN ${yesTwo} b ON a.yes_id = b.id "
            + "<if test='whereConditions != null'>"
            + "where (${whereConditions}) "
            + "</if>"
            + "</script>")
    Long queryTotal(@Param("yesOne") String yesOne, @Param("yesTwo") String yesTwo, @Param("whereConditions") String whereConditions);

}

Oinone社区 作者:yexiu原创文章,如若转载,请注明出处:https://doc.oinone.top/kai-fa-shi-jian/17950.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

Like (0)
yexiu's avataryexiu数式员工
Previous 2024年9月27日 am11:20
Next 2024年9月29日 pm8:56

相关推荐

  • Oinone项目引入Nacos作为配置中心

    Oinone项目引入nacos作为配置中心 Oinone项目配置默认读取的项目中yml文件(application-xxx.yml), 实际项目中有可能要求项目的配置放到Nacos配置中心中; Oinone默认引入的nacos-client-1.4.1,低于1.4.1的版本不支持认证配置;1.4.1的客户端版本支持Nacos服务端1.x的和2.x的版本; 一、项目中增加依赖 项目主pom引入依赖(最新版平台已默认引入), Nacos版本要求1.4.1以上,低版本不支持认证配置 <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <version>2021.1</version> </dependency> <dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> <version>1.4.1</version> </dependency> 项目的boot工程的pom引入依赖(最新版平台已默认引入) <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> </dependency> 二、项目工程bootstrap配置 bootstrap.yml文件的配置修改为: spring: application: name: hr-simple # 替换为实际服务名 profiles: active: wx # 指定 profile cloud: config: enabled: false nacos: discovery: enabled: false config: server-addr: 127.0.0.1:8848 enabled: true # namespace: # 如果使用 public 命名空间,建议省略此行 # namespace: your-custom-namespace-id # 如果使用自定义命名空间,填写其 ID group: DEFAULT_GROUP # prefix: # 通常省略,使用默认的 spring.application.name file-extension: yaml # 推荐使用 yaml,而不是 yml (虽然通常兼容) timeout: 5000 #【可选】修改为和nacos一致即可(如果服务端未开启可以不用配置) # username: wangxian # password: wangxian 三、Naocs服务端配置 在Nacos服务端的对应的namespace(5a8b3710-a9a2-4f7c-932f-50f326cb1ccf)下增加配置,把原本配置在代码中的(application-xxx.yml)配置到Nacos中

    2024年2月28日
    2.4K00
  • 【后端】项目开发后端知识要点地图

    大类 明细 文档链接 平台基础 如何开发Action,理解前后端协议 如何开发Action,理解前后端协议 CDN配置及文件操作相关 OSS(CDN)配置和文件系统的一些操作 MINIO无公网访问地址下OSS的配置 MINIO无公网访问地址下OSS的配置 分库分表与自定义分表规则 分库分表与自定义分表规则 Oinone引入搜索引擎(增强模型) Oinone引入搜索引擎(增强模型) 引入搜索/增强模型Channel)常见问题解决办法 引入搜索(增强模型Channel)常见问题解决办法 框架之MessageHub(信息提示) 框架之MessageHub(信息提示) DsHint和BatchSizeHint的使用 DsHint(指定数据源)和BatchSizeHint(查询批次数量) Oinone连接外部数据源方案 Oinone连接外部数据源方案 如何自定义SQL(Mapper)语句 如何自定义SQL(Mapper)语句 IWrapper、QueryWrapper和LambdaQueryWrapper使用 IWrapper、QueryWrapper和LambdaQueryWrapper使用 如何在代码中使用自增ID、手动方式获取CODE 如何在代码中使用自增ID、手动方式获取CODE 函数之触发与定时配置和示例 函数之触发与定时配置和示例 函数之异步执行 函数之异步执行 查询时自定义排序字段和排序规则 查询时自定义排序字段和排序规则 非存储字段搜索 非存储字段搜索,适应灵活的搜索场景 枚举/二进制枚举/多值枚举 如何使用位运算的数据字典 全局首页及应用首页配置方法(homepage) 全局首页及应用首页配置方法(homepage) 缓存连接由Jedis切换为Lettuce 缓存连接由Jedis切换为Lettuce GraphQL请求:后端接口实现逻辑解析 GraphQL请求:后端接口实现逻辑解析 Nacos支持 Nacos作为注册中心 Oinone项目引入Nacos作为注册中心 Nacos作为配置中心 Oinone项目引入Nacos作为配置中心 Nacos做为注册中心调用SpringCloud服务 Nacos做为注册中心调用SpringCloud服务 分布式相关 如何构建分布式项目 Oinone如何支持构建分布式项目 构建分布式项目一些要点(dubbo日志关闭等) Oinone构建分布式项目一些注意点 信创支持 后端部署使用达梦数据库 【达梦】后端使用达梦数据库 后端部署使用PostgreSQL数据库 【PostgreSQL】后端使用PostgreSQL数据库 后端部署使用OpenGauss数据库 【OpenGauss】后端使用OpenGauss数据库 后端部署使用MSSQL数据库(SQLServer) 【MSSQL】后端部署使用MSSQL数据库(SQLServer) 东方通Web和Tomcat部署Oinone项目 东方通Web和Tomcat部署Oinone项目 常见扩展 如何增加用户中心的菜单 如何增加用户中心的菜单 导入导出 如何批量导入 如何批量导入 如何支持多Excel多个Sheet导入功能 如何支持多Excel多个Sheet导入功能 如何自定义Excel导入功能 如何自定义Excel导入功能 如何自定义Excel导出功能 如何自定义Excel导出功能 如何自定义表达式 如何自定义表达式 登录扩展 对接外部SSO Oinone登录扩展:对接SSO(4.7.8及之后的版本) 自定义占位符 自定义RSQL占位符及在权限中使用 自定义RSQL占位符(placeholder)及在权限中使用 自定义数据权限拦截处理 自定义数据权限拦截处理 设计器公共 后端无代码设计器Jar包启动方法 后端无代码设计器Jar包启动方法 界面设计器 页面跳转时增加跳转参数 页面跳转时增加跳转参数 界面设计器的导入导出 界面设计器的导入导出 流程设计器 项目中工作流引入和流程触发 项目中工作流引入和流程触发 流程扩展自定义函数示例代码汇总 工作流-流程扩展自定义函数示例代码汇总 工作流-流程代办等页面自定义 工作流-流程代办等页面自定义 审核撤回/回退/拒绝钩子使用 工作流审核撤回/回退/拒绝钩子使用 流程设计器的导入导出 流程设计器的导入导出 如何添加工作流运行时依赖 如何添加工作流运行时依赖 数据可视化 项目中图表设计器引入 数据可视化-项目中数据可视化的实现引入 自定义图表模版 数据可视化中图表的低无一体 图表设计器数据获取示例 数据可视化-数据可视化数据获取示例 如何添加数据可视化运行时依赖 如何添加数据可视化运行时依赖 图表设计器的设计数据导入导出 图表设计器的设计数据导入导出

    2024年5月21日
    3.1K00
  • 如何使用GQL工具正确发起请求

    简介 本文将讲解一下如何正确发起GQL请求和GQL工具使用过程中的常见问题。 参数介绍 请求url和请求方法在浏览器的请求标头里面可以查到,保持一致就可以向服务正常发送请求,但还缺少请求体确定请求哪个接口。 每个请求都包括两部分内容:1. Query 2. Variables右键Query和Variables复制值直接拷贝到工具中就可以正常请求了。 注意:如果使用admin账号登录,请求的时候可以不携带Variables参数,因为admin没有权限控制,如果使用其他用户,就必须携带Variables参数,否则会被权限拦截。

    2024年10月25日
    2.3K00
  • 函数之触发与定时配置和示例

    异步任务总体介绍 函数的触发和定时在很多场景中会用到,也是一个oinone的基础能力。比如我们的流程产品中在定义流程触发时就会让用户选择模型触发还是时间触发,就是用到了函数的触发与定时能力。 触发任务TriggerTaskAction 触发任务的创建,使用sql-record模块监听mysql的binlog事件,通过rocketmq发送变更数据消息,收到MQ消息后,创建TriggerAutoTask。 触发任务的执行,使用TBSchedule拉取触发任务后,执行相应函数。 项目中引入依赖 1、项目的API工程引入依赖pamirs-core-trigger模块 <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-api</artifactId> </dependency> 2、DemoModule在模块依赖定义中增加@Module(dependencies={TriggerModule.MODULE_MODULE}) @Component @Module( name = DemoModule.MODULE_NAME, displayName = "oinoneDemo工程", version = "1.0.0", dependencies = {ModuleConstants.MODULE_BASE, CommonModule.MODULE_MODULE, UserModule.MODULE_MODULE, TriggerModule.MODULE_MODULE} ) @Module.module(DemoModule.MODULE_MODULE) @Module.Advanced(selfBuilt = true, application = true) @UxHomepage(PetShopProxy.MODEL_MODEL) public class DemoModule implements PamirsModule { ……其他代码 } 3、项目的boot工程引入依赖 <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-core</artifactId> </dependency> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-bridge-tbschedule</artifactId> </dependency> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-sql-record-core</artifactId> </dependency> yml文件修改(applcation-xxx.yml) a. 修改pamris.event.enabled和pamris.event.schedule.enabled为trueb. pamirs_boot_modules增加启动模块:trigger、sql_record pamirs: record: sql: #改成自己路径 store: /opt/pamirs/logs … event: enabled: true schedule: enabled: true rocket-mq: namesrv-addr: 127.0.0.1:9876 boot: init: true sync: true modules: – base -…… – trigger – sql_record -…… 新建触发任务 新建PetTalentTrigger类,当PetTalent模型的数据记录被新建时触发系统做一些事情 package pro.shushi.pamirs.demo.core.trigger; import pro.shushi.pamirs.demo.api.model.PetTalent; import pro.shushi.pamirs.meta.annotation.Fun; import pro.shushi.pamirs.meta.annotation.Function; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.trigger.annotation.Trigger; import pro.shushi.pamirs.trigger.enmu.TriggerConditionEnum; @Fun(PetTalent.MODEL_MODEL) @Slf4j public class PetTalentTrigger { @Function @Trigger(displayName = “PetTalent创建时触发”,name = “PetTalent#Trigger#onCreate”,condition = TriggerConditionEnum.ON_CREATE) public PetTalent onCreate(PetTalent data){ log.info(data.getName() + “,被创建”); //可以增加逻辑 return data; } } 定时任务 定时任务是一种非常常见的模式,这里就不介绍概念了,直接进入示例环节 新建PetTalentAutoTask实现ScheduleAction getInterfaceName()需要跟taskAction.setExecuteNamespace定义保持一致,都是函数的命名空间 taskAction.setExecuteFun("execute");跟执行函数名“execute”一致 TaskType需配置为CYCLE_SCHEDULE_NO_TRANSACTION_TASK,把定时任务的schedule线程分开,要不然有一个时间长的任务会导致普通异步或触发任务全部延时。 package pro.shushi.pamirs.demo.core.task; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import pro.shushi.pamirs.core.common.enmu.TimeUnitEnum; import pro.shushi.pamirs.demo.api.model.PetTalent; import pro.shushi.pamirs.meta.annotation.Fun; import pro.shushi.pamirs.meta.annotation.Function; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.domain.fun.FunctionDefinition; import pro.shushi.pamirs.middleware.schedule.api.ScheduleAction; import pro.shushi.pamirs.middleware.schedule.common.Result; import pro.shushi.pamirs.middleware.schedule.domain.ScheduleItem; import pro.shushi.pamirs.middleware.schedule.eunmeration.TaskType; import pro.shushi.pamirs.trigger.enmu.TriggerTimeAnchorEnum; import pro.shushi.pamirs.trigger.model.ScheduleTaskAction; import pro.shushi.pamirs.trigger.service.ScheduleTaskActionService; @Slf4j @Component @Fun(PetTalent.MODEL_MODEL) public class PetTalentAutoTask implements…

    2024年5月25日
    1.8K00
  • GQL GET 请求生成图片文件流 — 技术实现方案

    GQL GET 请求生成图片文件流 — 技术实现方案 基于 Oinone/Pamirs GQL GET 文件流机制,参考 FileAction#downloadFormData(查询型)与 ExcelExportTaskAction#createExportTask(同步写流),实现「按业务 ID 动态生成图片并输出文件流」的通用指南。 1. 背景与目标 1.1 需求 在业务场景中,需要根据业务 ID(如订单 ID、模板 ID、单据 ID)在后端动态渲染图片(条码、标签、证书、预览图等),并将结果以文件流形式返回给前端。 1.2 约束 复用 Oinone 既有鉴权、会话、模块路由体系,不新增独立 REST 下载接口。 与 Excel 同步导出保持一致的调用方式:通过 GET 访问 /pamirs/{module}?query=…&variables=…。 前端仅两种消费方式: 页面预览:<img :src="imageUrl" /> 触发下载:window.open(imageUrl, '_blank') 2. 参考实现 2.1 查询型文件流(图片查看应对齐此模式) 图片查看属于只读查询,应使用 query + @Function(type = QUERY),与 CDN 文件下载一致: // @oinone/kunlun-vue-ui-common/…/UploadService.ts const gql = `query{resourceFileFormQuery{downloadFormData(resourceFileForm:{downloadUrl:"${url}"}){filename}}}`; window.open(UrlHelper.appendBasePath(`/pamirs/file?query=${encodeURIComponent(gql)}`), '_blank'); // pamirs-core/pamirs-file2/…/FileAction.java @Function.Advanced(displayName = "前端下载数据", type = FunctionTypeEnum.QUERY) public ResourceFileForm downloadFormData(ResourceFileForm resourceFileForm) { // 直接写 HttpServletResponse 输出流 response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment; filename=" + …); IOUtils.copy(inputStream, response.getOutputStream()); return new ResourceFileForm(); } 图片查看用 GQL.query,不用 GQL.mutation。 2.2 同步写流机制(共用) 无论 query 还是 mutation,文件流输出的底层机制相同:在 Function/Action 执行过程中直接写 HttpServletResponse。 2.2.1 HTTP 网关:RequestController GET /pamirs/{moduleName}?query={gql}&variables={json} POST /pamirs/{moduleName} body: { query, variables } GET 与 POST 走同一执行链路: // pamirs-framework/pamirs-gateways-graph-java/…/RequestController.java @RequestMapping(value = "/pamirs/{moduleName}", method = RequestMethod.GET) public DeferredResult<String> pamirsGet( @PathVariable("moduleName") String moduleName, @RequestParam("query") String gql, @RequestParam(value = "variables", required = false) String variables, …) { PamirsClientRequestParam gqlRequest = new PamirsClientRequestParam(); gqlRequest.setQuery(gql); if (!StringUtils.isBlank(variables)) { gqlRequest.setVariables(JsonUtils.parseMap(variables)); } return pamirsPost(moduleName, gqlRequest, request, response); } variables.path 在 RequestHelper.preparePamirsRequestParam 中经 SessionPrepareApi.prepare 注入会话上下文,与前端 getSessionPath() 对应。 2.2.2 Excel 导出对照:ExcelFileServiceImpl#doExportSync Excel 同步导出使用 mutation(createExportTask),因为会创建导出任务记录;图片查看无此副作用,故用 query。 同步导出的关键:劫持 FileClient.upload,将字节直接写入当前…

    2026年7月2日
    34200

Leave a Reply

Please Login to Comment