Oinone连接外部数据源方案

场景描述

在实际业务场景中,有是有这样的需求:链接外部数据进行数据的获取;通常的做法:
1、【推荐】通过集成平台的数据连接器,链接外部数据源进行数据操作;
2、项目代码中链接数据源,即通过程序代码操作外部数据源的数据;

本篇文章只介绍通过程序代码操作外部数据源的方式.

整体方案

  • Oinone管理外部数据源,即yml中配置外部数据源;
  • 后端通过Mapper的方式进行数据操作(增/删/查/改);
  • 调用Mapper接口的时候,指定到外部数据源;

详细步骤

1、数据源配置(application.yml), 与正常的数据源配置一样

    out_ds_name(外部数据源别名):
      driverClassName: com.mysql.cj.jdbc.Driver
      type: com.alibaba.druid.pool.DruidDataSource
      # local环境配置调整
      url: jdbc:mysql://ip(host):端口/数据库Schema?useSSL=false&allowPublicKeyRetrieval=true&useServerPrepStmts=true&cachePrepStmts=true&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true
      username: 用户名
      password: 命名
      initialSize: 5
      maxActive: 200
      minIdle: 5
      maxWait: 60000
      timeBetweenEvictionRunsMillis: 60000
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      poolPreparedStatements: true
      asyncInit: true

2、外部数据源其他配置
外部数据源限制创建表结构的执行,可以通过配置指定【不创建DB,不创建数据表】

      persistence:
        global:
          auto-create-database: true
          auto-create-table: true
        ds:
          out_ds_name(外部数据源别名):
            # 不创建DB
            auto-create-database: false
            # 不创建数据表
            auto-create-table: false

3、后端写Mapper

  • SQL Mapper跟使用原生mybaits/mybaits-plus写法一样,无特殊限制;
  • Mapper和SQL写到一起,或者分开两个文件都可以

4、Mapper被Service或者Action调用
1)启动的Application中@MapperScan需要扫描到对应的包。
2)调用是与普通bean一样(即调用方式跟传统的方式样),唯一的区别就是加上DsHintApi,即指定Mapper所使用的数据源。

@Autowired
private ScheduleItemMapper scheduleItemMapper;

public saveData(Object data) {
    ScheduleQuery scheduleQuery = new ScheduleQuery();
    //scheduleQuery.setActionName();
    try (DsHintApi dsHint = DsHintApi.use(“外部数据源名称”)) {
        List<ScheduleItem> scheduleItems = scheduleItemMapper.selectListForSerial(scheduleQuery);
        // 具体业务逻辑
    }
}

其他参考:
如何自定义sql语句:https://doc.oinone.top/backend/4759.html

Oinone社区 作者:望闲原创文章,如若转载,请注明出处:https://doc.oinone.top/backend/4562.html

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

Like (2)
望闲's avatar望闲数式管理员
Previous 2024年5月17日 pm2:23
Next 2024年5月17日 pm6:40

相关推荐

  • 如何加密gql请求内容

    介绍 在一些对安全等级要求比较高的场景,oinone提供了扩展前端加密请求内容,后端解密请求内容的能力,该方案要求前后端的加解密方案统一。 后端 1. 继承平台的RequestController新增一个请求类,在里面处理加密逻辑 package pro.shushi.pamirs.demo.core.controller; import org.apache.commons.lang3.StringUtils; import org.springframework.web.bind.annotation.*; import pro.shushi.pamirs.framework.gateways.graph.java.RequestController; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.api.dto.protocol.PamirsClientRequestParam; import pro.shushi.pamirs.user.api.utils.AES256Utils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @Slf4j public class DemoRequestController extends RequestController { @SuppressWarnings("unused") @RequestMapping( value = "/pamirs/{moduleName:^[a-zA-Z][a-zA-Z0-9_]+[a-zA-Z0-9]$}", method = RequestMethod.POST ) public String pamirsPost(@PathVariable("moduleName") String moduleName, @RequestBody PamirsClientRequestParam gql, HttpServletRequest request, HttpServletResponse response) { decrypt(gql); return super.pamirsPost(moduleName, gql, request, response); } @SuppressWarnings("unused") @RequestMapping( value = "/pamirs/{moduleName:^[a-zA-Z][a-zA-Z0-9_]+[a-zA-Z0-9]$}/batch", method = RequestMethod.POST ) public String pamirsBatch(@PathVariable("moduleName") String moduleName, @RequestBody List<PamirsClientRequestParam> gqls, HttpServletRequest request, HttpServletResponse response) { for (PamirsClientRequestParam gql : gqls) { decrypt(gql); } return super.pamirsBatch(moduleName, gqls, request, response); } private static final String GQL_VAR = "gql"; private void decrypt(PamirsClientRequestParam gql) { Map<String, Object> variables = null != gql.getVariables() ? gql.getVariables() : new HashMap<>(); String encodeStr = (String) variables.get(GQL_VAR); if (StringUtils.isNotBlank(encodeStr)) { variables.put(GQL_VAR, null); // TODO 此处的加密方法可以换为其他算法 String gqlQuery = AES256Utils.decrypt(encodeStr); gql.setQuery(gqlQuery); } } } 2.boot工程的启动类排除掉平台默认的RequestController类 @ComponentScan( excludeFilters = { // 该注解排除平台的RequestController类 @ComponentScan.Filter( type = FilterType.REGEX, pattern = "pro.shushi.pamirs.framework.gateways.graph.java.RequestController" ) }) public class DemoApplication { } 以下为实际项目中的启动类示例 package pro.shushi.pamirs.demo.boot; import org.apache.ibatis.annotations.Mapper; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import…

    2024年6月20日
    1.8K00
  • 如何通过传输模型完成页面能力

    介绍 在业务中我们经常能遇到这种场景,我们的数据是通过调用第三方接口获取的,在业务系统中没有对应的存储模型,但是我们又需要展示这些数据,这时候可以利用传输模型不建表的特性完成这个功能。 定义传输模型 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.3K00
  • 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日
    2.1K00
  • 重写QueryPage时,增加额外的条件

    在需要对QueryPage增加额外的查询条件,比如DemoItem增加只展示创建人为当前用户的数据 @Function.Advanced(type = FunctionTypeEnum.QUERY, displayName = "查询列表") @Function.fun(FunctionConstants.queryPage) @Function(openLevel = {FunctionOpenEnum.LOCAL, FunctionOpenEnum.REMOTE, FunctionOpenEnum.API}) public Pagination<DemoItem> queryPage(Pagination<DemoItem> page, IWrapper<DemoItem> queryWrapper) { LambdaQueryWrapper<DemoItem> qw = ((QueryWrapper<DemoItem>) queryWrapper).lambda(); qw.eq(DemoItem::getCreateUid, PamirsSession.getUserId()); return demoItemService.queryPage(page, qw); }

    2023年11月1日
    1.1K00
  • 如何重写获取首页的方法

    介绍 用户登录成功后或者访问网页不带任何路由参数的时候前端会请求全局的首页的视图动作viewAction配置,然后跳转到该视图动作viewAction 方案 我们可以通过在该方法的后置hook自定义获取首页的逻辑,下面以按角色跳转不同首页的需求示例 package pro.shushi.pamirs.demo.core.hook; import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Component; import pro.shushi.pamirs.auth.api.model.AuthRole; import pro.shushi.pamirs.boot.base.enmu.BaseExpEnumerate; import pro.shushi.pamirs.boot.base.model.ViewAction; import pro.shushi.pamirs.boot.web.loader.PageLoadAction; import pro.shushi.pamirs.demo.api.model.DemoItemCategory; import pro.shushi.pamirs.demo.api.model.DemoItemLabel; import pro.shushi.pamirs.meta.annotation.Hook; import pro.shushi.pamirs.meta.api.CommonApiFactory; import pro.shushi.pamirs.meta.api.core.faas.HookAfter; import pro.shushi.pamirs.meta.api.dto.fun.Function; import pro.shushi.pamirs.meta.api.session.PamirsSession; import pro.shushi.pamirs.meta.common.exception.PamirsException; import pro.shushi.pamirs.user.api.model.PamirsUser; import java.util.List; import java.util.stream.Collectors; @Component public class DemoHomepageHook implements HookAfter { private static final String TEST_ROLE_CODE_01 = "ROLE_1211"; private static final String TEST_ROLE_CODE_02 = "ROLE_1211_1"; @Override @Hook(module = {"base"}, model = {ViewAction.MODEL_MODEL}, fun = {"homepage"}) public Object run(Function function, Object ret) { if (ret == null) { return null; } ViewAction viewAction = getViewActionByCurrentRole(); if (viewAction != null) { ViewAction retNew = CommonApiFactory.getApi(PageLoadAction.class).load(viewAction); ViewAction viewActionRet = (ViewAction) ((Object[]) ret)[0]; viewActionRet.set_d(retNew.get_d()); } return ret; } protected ViewAction getViewActionByCurrentRole() { try { PamirsUser user = new PamirsUser(); user.setId(PamirsSession.getUserId()); user.fieldQuery(PamirsUser::getRoles); List<AuthRole> roles = user.getRoles(); if (CollectionUtils.isNotEmpty(roles)) { List<String> roleCodes = roles.stream().map(AuthRole::getCode).collect(Collectors.toList()); if (roleCodes.contains(TEST_ROLE_CODE_01)) { return new ViewAction().setModel(DemoItemCategory.MODEL_MODEL).setName("DemoMenus_ItemPMenu_DemoItemAndCateMenu_DemoItemCategoryMenu").queryOne(); } else if (roleCodes.contains(TEST_ROLE_CODE_02)) { return new ViewAction().setModel(DemoItemLabel.MODEL_MODEL).setName("DemoMenus_ItemPMenu_DemoItemAndCateMenu_DemoItemLabelMenu").queryOne(); } } } catch (PamirsException exception) { if (PamirsSession.getUserId() == null) { throw PamirsException.construct(BaseExpEnumerate.BASE_USER_NOT_LOGIN_ERROR, exception.getCause()).errThrow(); } else { throw exception; } } return null; } }

    2024年7月6日
    2.9K00

Leave a Reply

Please Login to Comment