Oinone协同开发源码分析

前提

源码分析版本是 5.1.x版本

什么是协同开发模式

协同开发模式解决的是不同开发,在开发同一个模型时,不会相互影响,也不会影响到测试环境
详见:Oinone协同开发使用手册

协同开发原理

在协同模式下,本地开发的元数据,配置pamirs.data.distribution.session.ownSign参数后,元数据前缀加ownSign值,然后只存在redis缓存,不落库。其它环境无法直接访问到该数据。
测试环境,或其它环境访问,需要在url上加ownSign等于设置的,则读redis数据时,除了加载通用数据,也会合并ownSign前缀的redis数据,显示出来

注意事项

  1. 协同开发仅支持界面设计器,其他设计器均不支持
  2. 不支持权限配置
  3. 不支持工作流触发

版本支持

完整支持5.1.0及以上

功能详解

启动时操作

  1. 做元数据保护检查
  2. 配置ownSign,则key拼接为 ownSign + ':' + key
  3. 清除掉ownSign的redis缓存数据;非ownSign不用清理
  4. 计算差量数据
  5. 有差量数据,放入ownSign标识数据,并清理本地标识
  6. dubbo注册服务,group拼接group + ownSign 后进行注册

读取时操作

读本地

  1. 组装key: ownSign + ':' + key
  2. 本地缓存有数据,更新缓存本地数据,返回
  3. 本地没有数据,读redis,并插入本地缓存

读远程

dubbo注册消费者,group拼接group + ownSign 后进行泛化调用

元数据保护检查

开启数据保护模式,在启动参数里加
-PmetaProtected=pamirs

会在启动时,往redis里写入数据

private static final String META_PROTECTED_KEY = "pamirs:check:meta-protected";
private void writeMetaProtected(String metaProtected) {
    stringRedisTemplate.opsForValue().set(META_PROTECTED_KEY, metaProtected);
}

如果同时又设置 pamirs.data.distribution.session.ownSign
则会报错 在使用元数据保护模式下,不允许设置 [pamirs.distribution.session.ownSign]

处理逻辑如下

  1. 看redis是否启用保护标识的值
  2. 获取pamirs.distribution.session.ownSign配置
  3. 没有启动参数 且redis没有值,则retrun
  4. 如果有启动参数且配置了ownSign,报错 在使用元数据保护模式下,不允许设置 [pamirs.distribution.session.ownSign]
  5. 如果有启动参数且 redis没有值或启动参数设置 -P metaForceProtected,则写入redis
  6. 如果有启动参数, 且启动参数跟redis值不同,则报错[公共环境开启了元数据保护模式,本地开发环境需配置[pamirs.distribution.session.ownSign]]
  7. 如果没有启动参数且redis有值,但没有配置ownSign 报错[公共环境开启了元数据保护模式,本地开发环境需配置[pamirs.distribution.session.ownSign]]

核心代码如下
MetadataProtectedChecker

public void process(AppLifecycleCommand command, Set<String> runModules,
                        List<ModuleDefinition> installModules, List<ModuleDefinition> upgradeModules, List<ModuleDefinition> reloadModules) {
        String currentMetaProtected = stringRedisTemplate.opsForValue().get(META_PROTECTED_KEY);
        String metaProtected = getMetaProtected();
        boolean hasCurrentMetaProtected = StringUtils.isNotBlank(currentMetaProtected);
        boolean hasMetaProtected = StringUtils.isNotBlank(metaProtected);
        if (!hasCurrentMetaProtected && !hasMetaProtected) {
            return;
        }
        if (hasMetaProtected) {
            if (Spider.getDefaultExtension(SessionFillOwnSignApi.class).handleOwnSign()) {
            // 如果有启动参数且配置了ownSign
                throw new UnsupportedOperationException("在使用元数据保护模式下,不允许设置 [pamirs.distribution.session.ownSign]");
            }
            if (!hasCurrentMetaProtected || isForceProtected()) {
                writeMetaProtected(metaProtected);
            } else if (!metaProtected.equals(currentMetaProtected)) {
            // 如果有启动参数, 且启动参数跟redis值不同
                throw unsupportedLocalOperation();
            }
        } else {
            if (Spider.getDefaultExtension(SessionFillOwnSignApi.class).handleOwnSign()) {
                return;
            }
            // 没有启动参数且redis有值,但没有配置ownSign 报错
            throw unsupportedLocalOperation();
        }
    }

取ownSign方式

  1. 看header是否有ownSign这个标识
  2. header没有,则从配置里取,并放到header里
    ownSign的获取核心代码
    CdDistributionSessionFillOwnSignApi

    @Override
    public String getCdOwnSign() {
        String cdOwnSign = null;
        // 看header是否有ownSign这个标识
        Map<String, String> headers = PamirsSession.getRequestVariables().getHeaders();
        if (MapUtils.isNotEmpty(headers)) {
            cdOwnSign = headers.get(OWN_SIGN);
        }
        // 页面没有ownSign标识,则从配置里取,并放到header里
        if (StringUtils.isBlank(cdOwnSign)) {
            cdOwnSign = getConfigCdOwnSign();
            if (headers != null) {
                headers.put(OWN_SIGN, cdOwnSign);
            }
        }
        return cdOwnSign;
    }

清理redis缓存

ownSign的rediskey组装形式,会在原有key前加入ownSign值 + ":"
ownSign + ':' + key
CdDistributionSessionCacheApi

    SEPARATOR_COLON = ":";
    default String keyOwnSign(String key) {
        String cdOwnSign = SessionFillOwnSignApiHolder.get().getCdOwnSign();
        if (StringUtils.isNotBlank(cdOwnSign)) {
            key = cdOwnSign + SEPARATOR_COLON + key;
        }
        return key;
    }
  1. 如果配置ownSign设置,则进行清理工作
  2. 组装Key: ownSign + ':' + key
  3. 执行redis删除
    核心代码 CdModelL2Cache

    public void clear() {
    
        if (handleOwnSign()) {
            SessionRedisUtils.deletePattern(keyOwnSign(super.getModelDefPattern()));
            SessionRedisUtils.deletePattern(keyOwnSign(super.getModelNamePattern()));
        }
    }

数据写入缓存

以模型为例
插入缓存入口,updateMetaData

public <T extends MetaBaseModel> void updateMetaData(String model, List<T> dataList) {
        if (CollectionUtils.isEmpty(dataList)) {
            return;
        }

        // 初始化. Init要放在allMetaRefresh的判断前面
        this.init();
        if (allMetaRefresh) {
            return;
        }

        switch (model) {
            case ModuleDefinition.MODEL_MODEL:
            case UeModule.MODEL_MODEL:
                asyncExecution(model, dataList.size(), MetaUpgradeCheckApi.UPDATE_OPERATOR, () -> fillSessionModule((List<ModuleDefinition>) dataList));
                break;
            ....
        }
    }

每个model轮询处理
    private boolean fillSessionModel(List<ModelDefinition> dataList, Boolean needFunctions) {
        ....
        return reliableExecution(() -> {
            for (ModelDefinition modelDefinition : dataList) {
                doOneModel(modelDefinition, needFunctions);
            }
        }, "模型元数据[变更]");
    }

    private void doOneModel(ModelDefinition modelDefinition, Boolean needFunctions) {
        // 构造模型配置
        ModelConfig modelConfig = SessionsHelper.fetchModelConfig(modelDefinition);
        String modelModel = modelConfig.getModel();
        String modelUniqueKey = modelModel + ":" + Optional.ofNullable(modelDefinition.getModelFields()).map(List::size).orElse(0);
        try {
            if (ObjectRepeatHelper.isNotRepeat(DistributionDataSupplier.getModelRepeats(), modelUniqueKey)) {
            // 放入缓存
                modelCache.put(modelModel, modelConfig);
            }
        } catch (Exception e) {
            ....
        }

        // 全量情况下metaData.getStandAloneFunctionList()的Function不全;需要把模型的Function也进行缓存
        if (needFunctions) {
            fillSessionModelFunction(modelConfig.getFunctionList());
        }
    }

放入缓存
CdModelL2Cache

    private void cdPut(String key, ModelConfig value) {
        String modelDefCacheKey = cdModelDefCacheKey(key);
        String modelNameCacheKey = cdModelNameCacheKey(value.getName());
        byte[] bytes = MetadataSerializationHelper.serialize(value.getModelDefinition());
        SessionRedisUtils.set(modelDefCacheKey, bytes);
        SessionRedisUtils.set(modelNameCacheKey, bytes);
        //从模型设计器保存后,需要清除掉本地空的标识。后面再get的时候会从redis中获取
DistributionMetaDataLocalCache.removeModelDef(modelDefCacheKey);
        DistributionMetaDataLocalCache.removeModelDef(modelNameCacheKey);
    }

其它处理ownSign数据对象有:

CdFunctionByNameL2Cache
CdFunctionL2Cache
CdActionL2Cache
CdViewL2Cache
....

写入dubbo服务

写入dubbo链路看文章Oinone远程调用链路源码分析

  1. 在group加后缀 group = group + ownSign
  2. 调用dubbo泛化调用

核心代码DefaultRemoteRegistry

    // 泛化调用
    public void registryService(Function function) {
        String interfaceName = RegistryUtils.getRegistryInterface(function);
        String group = getGroup(function, true);
        String version = getVersion(function);
        Integer timeout = getTimeout(function);
        Integer retries = function.getRetries();
        if (log.isDebugEnabled()) {
            log.debug("register service. interfaceName: {}, group: {}, version: {}, timeout: {}, retries: {}", interfaceName, group, version, timeout, retries);
        }
        remoteRegistryComponent.registryGenericService(interfaceName, group, version, timeout, retries);
}

// group加前缀
private String getGroup(Function function, boolean usingOwnSign) {
        String group = function.getGroup();
...
        if (usingOwnSign) {
            String ownSign = Spider.getDefaultExtension(SessionFillOwnSignApi.class).getCdOwnSign();
            if (StringUtils.isNotBlank(ownSign)) {
                group = group + ownSign;
            }
        }
        return group;
    }

读缓存

  1. 如果配置ownSign参数,则走ownSign处理逻辑
  2. 组装key: cdOwnSign + SEPARATOR_COLON + key
  3. 本地有缓存,更新本地缓存,并返回
  4. 本地没有缓存,读取redis数据,后放入本地缓存,返回

CdModelL2Cache

public ModelConfig get(String key) {
        ModelConfig modelConfig;
        if (handleOwnSign()) {
            modelConfig = cdGet(key);
            if (modelConfig == null) {
                modelConfig = super.get(key);
            }
        } else {
            modelConfig = super.get(key);
        }
        return modelConfig;
    }

private ModelConfig cdGet(String key) {
        if (DistributionBaseKeyer.MODEL_MODEL_BASE_KEYS.contains(key)) {
            return CommonApiFactory.getApi(DistributionL1CacheProxy.class).getModelL1Cache().get(key);
        }
        String modelDefCacheKey = cdModelDefCacheKey(key);
        ModelDefinition modelDef = DistributionMetaDataLocalCache.getModelDef(modelDefCacheKey);
        // 本地有数据,返回,没有则放入本地缓存,再返回
        if (modelDef != null || DistributionMetaDataLocalCache.keyModelDef(modelDefCacheKey)) {
            return super.covertModelConfig(modelDef);
        }
        // 读取redis数据
        byte[] bytes = SessionRedisUtils.get(modelDefCacheKey);
        modelDef = KryoSerializer.deserialize(bytes);
        if (modelDef != null) {
            // 放入本地缓存DistributionMetaDataLocalCache.setModelDef(modelDefCacheKey, modelDef);
        }
        return super.covertModelConfig(modelDef);
    }

读dubbo服务

读取dubbo链路看文章Oinone远程调用链路源码分析
RemoteComputer.invoke()

  1. 在group加后缀 group = group + ownSign
  2. 调用dubbo泛化调用

核心代码DefaultRemoteRegistry

    public GenericService registryConsumer(Function function) {
        String interfaceName = RegistryUtils.getRegistryInterface(function);
        // 
        String group = getGroup(function, true);
        String version = getVersion(function);
        Integer timeout = getTimeout(function);
        Integer retries = function.getRetries();
        if (log.isDebugEnabled()) {
            log.debug("register consumer. interfaceName: {}, group: {}, version: {}, timeout: {}, retries: {}", interfaceName, group, version, timeout, retries);
        }
        return remoteRegistryComponent.registryGenericConsumer(interfaceName, group, version, timeout, retries);
    }

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

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

Like (0)
oinone's avataroinone
Previous 2024年9月11日 am9:57
Next 2024年9月12日 pm6:12

相关推荐

  • 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.apache.commons.lang3.StringUtils; 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.SSOConstant; 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.utils.AuthenticateUtils; import pro.shushi.pamirs.demo.core.sso.model.PermissionInfoResp; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.api.dto.model.PamirsUserDTO; import pro.shushi.pamirs.meta.api.dto.protocol.PamirsRequestVariables; 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.meta.common.util.UUIDUtil; 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.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.concurrent.TimeUnit; /** * * @author wangxian * * 完全自定义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 createSessionId(HttpServletRequest request, PamirsUser idModel) { return UUIDUtil.getUUIDNumberString(); } @Override public String type() { return UserLoginTypeEnum.COOKIE.value(); } @Override public PamirsUser resolveAndVerification(PamirsUserTransient user) { if (checker == null) { checker =…

    2023年11月24日
    1.8K00
  • 如何自定义Excel导出功能

    介绍 在平台提供的默认导出功能无法满足业务需求的时候,我们可以自定义导出功能,以满足业务中个性化的需求。 功能示例 继承平台的导出任务模型,加上需要在导出的弹窗视图需要展示的字段 package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.file.api.model.ExcelExportTask; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; @Model.model(DemoItemExportTask.MODEL_MODEL) @Model(displayName = "商品-Excel导出任务") public class DemoItemExportTask extends ExcelExportTask { public static final String MODEL_MODEL = "demo.DemoItemExportTask"; // 自定义显示的字段 @Field.String @Field(displayName = "发布人") private String publishUserName; } 编写自定义导出弹窗视图的数据初始化方法和导出提交的action package pro.shushi.pamirs.demo.core.action; import org.springframework.stereotype.Component; import pro.shushi.pamirs.demo.api.model.DemoItemExportTask; import pro.shushi.pamirs.file.api.action.ExcelExportTaskAction; import pro.shushi.pamirs.file.api.model.ExcelWorkbookDefinition; import pro.shushi.pamirs.file.api.service.ExcelFileService; 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.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.enmu.ActionContextTypeEnum; import pro.shushi.pamirs.meta.enmu.FunctionOpenEnum; import pro.shushi.pamirs.meta.enmu.FunctionTypeEnum; import pro.shushi.pamirs.meta.enmu.ViewTypeEnum; @Slf4j @Component @Model.model(DemoItemExportTask.MODEL_MODEL) public class DemoItemExcelExportTaskAction extends ExcelExportTaskAction { public DemoItemExcelExportTaskAction(ExcelFileService excelFileService) { super(excelFileService); } @Action(displayName = "导出", contextType = ActionContextTypeEnum.CONTEXT_FREE, bindingType = {ViewTypeEnum.TABLE}) public DemoItemExportTask createExportTask(DemoItemExportTask data) { if (data.getWorkbookDefinitionId() != null) { ExcelWorkbookDefinition workbookDefinition = new ExcelWorkbookDefinition(); workbookDefinition.setId(data.getWorkbookDefinitionId()); data.setWorkbookDefinition(workbookDefinition); } super.createExportTask(data); return data; } /** * @param data * @return */ @Function(openLevel = FunctionOpenEnum.API) @Function.Advanced(type = FunctionTypeEnum.QUERY) public DemoItemExportTask construct(DemoItemExportTask data) { data.construct(); return data; } } 编写导出的数据处理逻辑,此处可以拿到导出弹窗内自定义的字段提交的值,然后根据这些值处理自定义逻辑 package pro.shushi.pamirs.demo.core.excel.extPoint; import org.springframework.stereotype.Component; import pro.shushi.pamirs.demo.api.model.DemoItem; import pro.shushi.pamirs.demo.api.model.DemoItemExportTask; import pro.shushi.pamirs.demo.api.model.DemoItemImportTask; import pro.shushi.pamirs.file.api.context.ExcelDefinitionContext; import pro.shushi.pamirs.file.api.enmu.ExcelTemplateTypeEnum; import pro.shushi.pamirs.file.api.extpoint.ExcelExportFetchDataExtPoint; import pro.shushi.pamirs.file.api.extpoint.impl.ExcelExportSameQueryPageTemplate; import pro.shushi.pamirs.file.api.model.ExcelExportTask; import pro.shushi.pamirs.file.api.model.ExcelWorkbookDefinition; import pro.shushi.pamirs.file.api.util.ExcelHelper; import pro.shushi.pamirs.file.api.util.ExcelTemplateInit; import pro.shushi.pamirs.meta.annotation.ExtPoint; import java.util.Collections; import java.util.List; @Component public class DemoItemExportExtPoint extends ExcelExportSameQueryPageTemplate implements ExcelTemplateInit , ExcelExportFetchDataExtPoint…

    2024年1月3日
    1.4K00
  • 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

    2024年5月17日
    2.1K00
  • IWrapper、QueryWrapper和LambdaQueryWrapper使用

    条件更新updateByWrapper 通常我们在更新的时候new一个对象出来在去更新,减少更新的字段 Integer update = new DemoUser().updateByWrapper(new DemoUser().setFirstLogin(Boolean.FALSE), Pops.<DemoUser>lambdaUpdate().from(DemoUser.MODEL_MODEL).eq(IdModel::getId, userId) 使用基础模型的updateById方法更新指定字段的方法: new 一下update对象出来,更新这个对象。 WorkflowUserTask userTaskUp = new WorkflowUserTask(); userTaskUp.setId(userTask.getId()); userTaskUp.setNodeContext(json); userTaskUp.updateById(); 条件删除updateByWrapper public List<T> delete(List<T> data) { List<Long> petTypeIdList = new ArrayList(); for(T item:data){ petTypeIdList.add(item.getId()); } Models.data().deleteByWrapper(Pops.<PetType>lambdaQuery().from(PetType.MODEL_MODEL).in(PetType::getId,petTypeIdList)); return data; } 构造条件查询数据 示例1: LambdaQueryWrapper拼接查询条件 private void queryPetShops() { LambdaQueryWrapper<PetShop> query = Pops.<PetShop>lambdaQuery(); query.from(PetShop.MODEL_MODEL); query.setSortable(Boolean.FALSE); query.orderBy(true, true, PetShop::getId); List<PetShop> petShops2 = new PetShop().queryList(query); System.out.printf(petShops2.size() + ""); } 示例2: IWrapper拼接查询条件 private void queryPetShops() { IWrapper<PetShop> wrapper = Pops.<PetShop>lambdaQuery() .from(PetShop.MODEL_MODEL).eq(PetShop::getId,1L); List<PetShop> petShops4 = new PetShop().queryList(wrapper); System.out.printf(petShops4.size() + ""); } 示例3: QueryWrapper拼接查询条件 private void queryPetShops() { //使用Lambda获取字段名,防止后面改字段名漏改 String nameField = LambdaUtil.fetchFieldName(PetTalent::getName); //使用Lambda获取Clumon名,防止后面改字段名漏改 String nameColumn = PStringUtils.fieldName2Column(nameField); QueryWrapper<PetShop> wrapper2 = new QueryWrapper<PetShop>().from(PetShop.MODEL_MODEL) .eq(nameColumn, "test"); List<PetShop> petShops5 = new PetShop().queryList(wrapper2); System.out.printf(petShops5.size() + ""); } IWrapper转为LambdaQueryWrapper @Function.Advanced(type= FunctionTypeEnum.QUERY) @Function.fun(FunctionConstants.queryPage) @Function(openLevel = {FunctionOpenEnum.API}) public Pagination<PetShopProxy> queryPage(Pagination<PetShopProxy> page, IWrapper<PetShopProxy> queryWrapper) { LambdaQueryWrapper<PetShopProxy> wrapper = ((QueryWrapper<PetShopProxy>) queryWrapper).lambda(); // 非存储字段从QueryData中获取 Map<String, Object> queryData = queryWrapper.getQueryData(); if (null != queryData && !queryData.isEmpty()) { String codes = (String) queryData.get("codes"); if (org.apache.commons.lang3.StringUtils.isNotBlank(codes)) { wrapper.in(PetShopProxy::getCode, codes.split(",")); } } return new PetShopProxy().queryPage(page, wrapper); }

    2024年5月25日
    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日
    2.6K00

Leave a Reply

Please Login to Comment