如何自定义SQL(Mapper)语句

场景描述

在实际业务场景中,存在复杂SQL的情况,具体表现为:

  • 单表单SQL满足不了的情况下
  • 有复杂的Join关系或者子查询
  • 复杂SQL的逻辑通过程序逻辑难以实现或实现代价较大

在此情况下,通过原生的mybatis/mybatis-plus, 自定义Mapper的方式实现业务功能

1、编写所需的Mapper

SQL Mapper写法无限制,与使用原生的mybaits/mybaits-plus用法一样; Mapper(DAO)和SQL可以写在一个文件中,也分开写在两个文件中。

package pro.shushi.pamirs.demo.core.map;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.List;
import java.util.Map;

@Mapper
public interface DemoItemMapper {
    @Select("<script>select sum(item_price) as itemPrice,sum(inventory_quantity) as inventoryQuantity,categoryId from ${demoItemTable}  as core_demo_item ${where}  group by category_id</script>")
    List<Map<String, Object>> groupByCategoryId(@Param("demoItemTable") String pamirsUserTable, @Param("where") String where);
}

2.调用mapper

调用Mapper代码示例

package pro.shushi.pamirs.demo.core.map;

import com.google.api.client.util.Lists;
import org.springframework.stereotype.Component;
import pro.shushi.pamirs.demo.api.model.DemoItem;
import pro.shushi.pamirs.framework.connectors.data.api.datasource.DsHintApi;
import pro.shushi.pamirs.meta.api.core.orm.convert.DataConverter;
import pro.shushi.pamirs.meta.api.session.PamirsSession;
import pro.shushi.pamirs.meta.common.spring.BeanDefinitionUtils;

import java.util.List;
import java.util.Map;

@Component
public class DemoItemDAO {
    public List<DemoItem> customSqlDemoItem(){
        try (DsHintApi dsHint = DsHintApi.model(DemoItem.MODEL_MODEL)) {
            String demoItemTable = PamirsSession.getContext().getModelCache().get(DemoItem.MODEL_MODEL).getTable();

            DemoItemMapper demoItemMapper = BeanDefinitionUtils.getBean(DemoItemMapper.class);
            String where = " where status = 'ACTIVE'";
            List<Map<String, Object>> dataList = demoItemMapper.groupByCategoryId(demoItemTable,where);
            DataConverter persistenceDataConverter = BeanDefinitionUtils.getBean(DataConverter.class);
            return persistenceDataConverter.out(DemoItem.MODEL_MODEL, dataList);
        }
        return Lists.newArrayList();
    }
}

调用Mapper一些说明

  • 启动类需要配置扫描包MapperScan
    @MapperScan(value = "pro.shushi", annotationClass = Mapper.class)
    @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, FreeMarkerAutoConfiguration.class})
    public class DemoApplication {
  • 调用Mapper接口的时候,需要指定数据源;即上述示例代码中的 DsHintApi dsHint = DsHintApi.model(DemoItem.MODEL_MODEL), 实际代码中使用 try-with-resources语法。
  • 从Mapper返回的结果中获取数据
    • 如果SQL Mapper中已定义了resultMap,调用Mapper(DAO)返回的就是Java对象
    • 如果Mapper返回的是Map<String, Object>,则通过 DataConverter.out进行转化,参考上面的示例

其他参考:Oinone连接外部数据源方案:https://doc.oinone.top/backend/4562.html

Oinone社区 作者:数式-海波原创文章,如若转载,请注明出处:https://doc.oinone.top/backend/4759.html

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

Like (0)
数式-海波's avatar数式-海波数式管理员
Previous 2023年11月24日 pm4:51
Next 2023年11月27日 pm4:30

相关推荐

  • Oinone登录扩展:对接SSO(适应于4.7.8及之后的版本)

    适配版本 4.7.8及其之后的版本 概述 在企业内部,对于已有一套完整的登录系统(SSO)的情况下,通常会要求把所有的系统都对接到SSO中;本文主要讲解用Oinone开发的项目对接SSO的具体实现。 对接步骤 1、项目自定义实现UserCookieLogin,可参考示例说明: pro.shushi.pamirs.user.api.login.UserCookieLoginFree 2、对接SSO示例 对接流程说明: 1)【必须】从请求头Header或者Query中获取到token; 2)【必须】去SSO服务端验证token的有效性; 3)【可选】根据token去服务端获取用户信息;如果token可以直接反解析出用户信息,则该步骤忽略; 4)【可选】根据实际情况用户信息是否进行DB的存储; 5)【必须】验证token有效后,生成Session和Cookie(即token换cookie); 注意超时时间需要 <= SSO服务端token失效时间。 package pro.shushi.pamirs.demo.core.sso; import com.alibaba.fastjson.JSON; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.Order; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import pro.shushi.pamirs.demo.core.sso.constant.HttpConstant; import pro.shushi.pamirs.demo.core.sso.constant.SessionUserTypeEnum; import pro.shushi.pamirs.demo.core.sso.model.ApiCommonTransient; import pro.shushi.pamirs.demo.core.sso.model.PermissionInfoResp; import pro.shushi.pamirs.demo.core.sso.utils.AuthenticateUtils; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.api.dto.model.PamirsUserDTO; import pro.shushi.pamirs.meta.api.session.PamirsSession; import pro.shushi.pamirs.meta.common.exception.PamirsException; import pro.shushi.pamirs.meta.common.spring.BeanDefinitionUtils; import pro.shushi.pamirs.resource.api.enmu.UserSignUpType; import pro.shushi.pamirs.user.api.cache.UserCache; import pro.shushi.pamirs.user.api.constants.UserConstant; import pro.shushi.pamirs.user.api.enmu.UserExpEnumerate; import pro.shushi.pamirs.user.api.enmu.UserLoginTypeEnum; import pro.shushi.pamirs.user.api.login.IUserLoginChecker; import pro.shushi.pamirs.user.api.login.UserCookieLogin; import pro.shushi.pamirs.user.api.login.UserCookieLoginSimple; import pro.shushi.pamirs.user.api.model.PamirsUser; import pro.shushi.pamirs.user.api.model.tmodel.PamirsUserTransient; import pro.shushi.pamirs.user.api.service.UserService; import pro.shushi.pamirs.user.api.utils.CookieUtil; import javax.servlet.http.HttpServletResponse; /** * * @author shushi * * 完全自定义login的过程 * 需要实现登陆部分login 以及拦截部分fetchUserIdByReq * 如果fetchUserIdByReq返回值为null的时候 将会被拦截 */ @Slf4j @Order(0) @Component public class DemoUserSSOCookieLogin extends UserCookieLogin<PamirsUser> { //刷新令牌 private static String REFRESH_TOKEN = "refreshToken"; //系统id private static String CLIENT_ID = "client-id"; //访问令牌 private static String AUTHORIZATION = "Authorization"; private IUserLoginChecker checker; @Autowired private UserService userService; @Autowired private RedisTemplate<String, String> redisTemplate; @Override public String type() { return UserLoginTypeEnum.COOKIE.value(); } @Override public PamirsUser resolveAndVerification(PamirsUserTransient user) { if (checker == null) { checker = BeanDefinitionUtils.getBean(IUserLoginChecker.class); } return checker.check4login(user); } /** * 重写登录拦截功能 * 该函数主要作用,通过三方权限校验. * @return */ // 版本升级需要修改 @Override public PamirsUserDTO fetchUserIdByReq() { String sessionId =…

    2024年4月2日
    2.3K00
  • 如何通过传输模型完成页面能力

    介绍 在业务中我们经常能遇到这种场景,我们的数据是通过调用第三方接口获取的,在业务系统中没有对应的存储模型,但是我们又需要展示这些数据,这时候可以利用传输模型不建表的特性完成这个功能。 定义传输模型 package pro.shushi.pamirs.demo.api.tmodel; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.base.TransientModel; @Model.model(DemoCreateOrder.MODEL_MODEL) @Model(displayName = "下单页面模型") public class DemoCreateOrder extends TransientModel { public static final String MODEL_MODEL = "demo.DemoCreateOrder"; @Field.Integer @Field(displayName ="下单人uid") private Long userId; } 定义action,由于传输模型用于表现层和应用层之间的数据交互,本身不会存储,没有默认的数据管理器,只有数据构造器,所以需要手动添加所需的queryOne、create、update等方法 注意:传输模型没有数据管理器能力,所以不提供类似queryPage的方法,后续版本考虑支持中 package pro.shushi.pamirs.demo.core.action; import org.springframework.stereotype.Component; import pro.shushi.pamirs.demo.api.tmodel.DemoCreateOrder; import pro.shushi.pamirs.meta.annotation.Action; import pro.shushi.pamirs.meta.annotation.Function; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.api.dto.condition.Pagination; import pro.shushi.pamirs.meta.api.dto.wrapper.IWrapper; import pro.shushi.pamirs.meta.constant.FunctionConstants; import pro.shushi.pamirs.meta.enmu.FunctionOpenEnum; import pro.shushi.pamirs.meta.enmu.FunctionTypeEnum; import pro.shushi.pamirs.meta.enmu.ViewTypeEnum; import static pro.shushi.pamirs.meta.enmu.FunctionOpenEnum.*; @Component @Model.model(DemoCreateOrder.MODEL_MODEL) public class DemoCreateOrderAction { @Function.Advanced(type = FunctionTypeEnum.QUERY) @Function.fun(FunctionConstants.queryByEntity) @Function(openLevel = {LOCAL, REMOTE, API}) public DemoCreateOrder queryOne(DemoCreateOrder query) { return query; } @Action.Advanced(name = FunctionConstants.create, managed = true) @Action(displayName = "创建", label = "确定", summary = "添加", bindingType = ViewTypeEnum.FORM) @Function(name = FunctionConstants.create) @Function.fun(FunctionConstants.create) public DemoCreateOrder create(DemoCreateOrder data) { return data; } @Action.Advanced(name = FunctionConstants.update, managed = true) @Action(displayName = "确定", summary = "修改", bindingType = ViewTypeEnum.FORM) @Function(name = FunctionConstants.update) @Function.fun(FunctionConstants.update) public DemoCreateOrder update(DemoCreateOrder data) { return data; } }

    2024年5月24日
    1.2K00
  • DsHint(指定数据源)和BatchSizeHint(指定批次数量)

    概述和使用场景 DsHintApi ,强制指定数据源, BatchSizeHintApi ,强制指定查询批量数量 API定义 DsHintApi public static DsHintApi model(String model/**模型编码*/) { // 具体实现 } public DsHintApi(Object dsKey/***数据源名称*/) { // 具体实现 } BatchSizeHintApi public static BatchSizeHintApi use(Integer batchSize) { // 具体实现 } 使用示例 1、【注意】代码中使用 try-with-resources语法; 否则可能会出现数据源错乱 2、DsHintApi使用示例包裹在try里面的所有查询都会强制使用指定的数据源 // 使用方式1: try (DsHintApi dsHintApi = DsHintApi.model(PetItem.MODEL_MODEL)) { List<PetItem> items = demoItemDAO.customSqlDemoItem(); PetShopProxy data2 = data.queryById(); data2.fieldQuery(PetShopProxy::getPetTalents); } // 使用方式2: try (DsHintApi dsHintApi = DsHintApi.use("数据源名称")) { List<PetItem> items = demoItemDAO.customSqlDemoItem(); PetShopProxy data2 = data.queryById(); data2.fieldQuery(PetShopProxy::getPetTalents); } 3、BatchSizeHintApi使用示例包裹在try里面的所有查询都会按照指定的batchSize进行查询 // 查询指定每次查询500跳 try (BatchSizeHintApi batchSizeHintApi = BatchSizeHintApi.use(500)) { PetShopProxy data2 = data.queryById(); data2.fieldQuery(PetShopProxy::getPetTalents); } // 查询指定不分页(batchSize=-1)查询。 请注意,你必须在明确不需要分页查询的情况下使用;如果数据量超大不分页可能会卡死。默认不指定分页数的情况下下平台会进行分页查询 try (BatchSizeHintApi batchSizeHintApi = BatchSizeHintApi.use(-1)) { PetShopProxy data2 = data.queryById(); data2.fieldQuery(PetShopProxy::getPetTalents); }

    2024年5月18日
    1.8K00
  • 国际化-语言和时区设置

    国际化-翻译 1、引入翻译的包 <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-translate</artifactId> </dependency> 2、默认逻辑:在系统的右上角,切换【系统语言后】,用户所选择的语言会保存到对应的用户信息中,后续所有的请求都会拿这个「语言」的值,并将其放入到PamirsSession#Lang中。3、实际项目可以通过自定义Session逻辑,根据实际业务覆盖掉默认方式,并将其设置在PamirsSession中: PamirsSession.setLang(langCode); 构建自定义Session参考:https://shushi.yuque.com/yoxz76/oio3/kg2sgr 构建自定义Session的逻辑中,根据业务逻辑把获取到的langCode设置都PamirsSession 4、目标语言编码说明语言编码必须符合ISO标准,即语言ISO代码。国际化-语言代码表-Language Codes参考下面的链接:https://blog.csdn.net/qq827245563/article/details/131552695 国际化-时区 1、引入时区的包 <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-timezone</artifactId> </dependency> 2、时区设置类似语言(langCode)3、在自定义Session(与设置语言共同的Session自定义)中,根据实际业务覆盖掉默认方式,并将其设置在TimezoneSession中: pro.shushi.pamirs.timezone.session.TimezoneSession#setTimezone(TimeZone timezone) 其他说明 PamirsSession 和 TimezoneSession 都是请求级别的;即每次请求都会自动销毁; 因此在自定义Session中覆盖这两个属性的默认值的时候特别注意一下性能。

    2023年12月4日
    1.3K00
  • 自定义审批方式、自定义审批节点名称

    @Model.model(审批模型.MODEL_MODEL) @Component public class 审批模型Action { @Function @Function.Advanced(category = FunctionCategoryEnum.CUSTOM_DESIGNER, displayName = "测试自定义审批类型") public WorkflowSignTypeEnum signType(String json) { // json为业务数据,可用JsonUtils转换 return WorkflowSignTypeEnum.COUNTERSIGN_ONEAGREE_ONEREJUST; } @Function @Function.Advanced(category = FunctionCategoryEnum.CUSTOM_DESIGNER, displayName = "测试自定义审批名称") public String customApprovalName() { return UUID.randomUUID().toString(); } }

    2023年12月5日
    2.0K00

Leave a Reply

Please Login to Comment