华为云云耀云服务器L实例评测|基于canal缓存自动更新流程 SpringBoot项目应用案例和源码

在这里插入图片描述

前言

最近华为云云耀云服务器L实例上新,也搞了一台来玩,期间遇到各种问题,在解决问题的过程中学到不少和运维相关的知识。

在之前的博客中,介绍过canal的安装和配置,参考博客

  • 拉取创建canal镜像配置相关参数 & 搭建canal连接MySQL数据库 & spring项目应用canal初步

本篇博客给出了canal项目应用的案例,详细介绍基于canal实现数据库和缓存同步的流程,并给出了核心diamante的源码。

在这里插入图片描述

其他相关的华为云云耀云服务器L实例评测文章列表如下:

  • 初始化配置SSH连接 & 安装MySQL的docker镜像 & 安装redis以及主从搭建 & 7.2版本redis.conf配置文件

  • 安装Java8环境 & 配置环境变量 & spring项目部署 &【!】存在问题未解决

  • 部署spring项目端口开放问题的解决 & 服务器项目环境搭建MySQL,Redis,Minio…指南

  • 由于自己原因导致MySQL数据库被攻击 & MySQL的binlog日志文件的理解

  • 认识redis未授权访问漏洞 & 漏洞的部分复现 & 设置连接密码 & redis其他命令学习

  • 拉取创建canal镜像配置相关参数 & 搭建canal连接MySQL数据库 & spring项目应用canal初步

  • Docker版的Minio安装 & Springboot项目中的使用 & 结合vue进行图片的存取

  • 在Redis的Docker容器中安装BloomFilter & 在Spring中使用Redis插件版的布隆过滤器

在这里插入图片描述

  • Elasticsearch的Docker版本的安装和参数设置 & 端口开放和浏览器访问

  • Elasticsearch的可视化Kibana工具安装 & IK分词器的安装和使用

  • Elasticsearch的springboot整合 & Kibana进行全查询和模糊查询

文章目录

  • 前言
  • 引出
  • 基于canal缓存同步更新
    • 整体的流程
  • 相关代码和流程
    • 1.canal通道的配置
    • 2.前端查询的业务代码
    • 3.数据库数据更新
    • 4.缓存更新前端展示
  • 核心代码源码
    • 1.配置yml和配置类
    • 2.canal自动更新代码
    • 3.查询的业务层service代码
    • 4.主启动类
    • 5.前端vue代码
  • 总结

引出


1.介绍基于canal实现数据库和缓存同步的流程;
2.给出了核心diamante的源码;

基于canal缓存同步更新

整体的流程

启动spring项目时,同步启动缓存自动更新,如果数据库的相关表格数据发生变化,canal通过就会监听到,然后更新缓存redis中的相应数据

在这里插入图片描述

哪些数据从缓存中取?——不经常更新的数据:比如公司的部门,仓库等;

在项目启动时,启动了canal,canal用来监听数据库的变化;

业务逻辑:前端请求相关数据–> 问Redis要

(1)如果redis里面有,则返回给前端;

(2)如果redis里面没有,则从数据库查询,并且存到redis里面,返回给前端;

(3)如果数据库发生更新,canal监听到修改,同步更新到Redis里面,保证缓存和数据库一致;

在这里插入图片描述

相关代码和流程

1.canal通道的配置

在这里插入图片描述

缓存自动更新,读取配置文件中的ip和端口号

在这里插入图片描述

是否开启缓存自动更新,从配置文件中读取配置;

在这里插入图片描述

2.前端查询的业务代码

在这里插入图片描述

在数据库数据没有更新时,获取缓存中的数据

在这里插入图片描述

3.数据库数据更新

如果数据库的数据更新,canal监听到,发现是缓存对应的表,并且是对应的字段发生变化,则进行缓存的自动更新

在这里插入图片描述

缓存自动同步更新

在这里插入图片描述

存到Redis里面的最新的数据

在这里插入图片描述

4.缓存更新前端展示

缓存更新后,前端再次查询,获得最新的数据

在这里插入图片描述

核心代码源码

1.配置yml和配置类

配置yml文件

server:port: 10050## 是否启用安全框架 true为开启,false为关闭
security:isOpen: true## 是否开启canal管道,true为开启,false为关闭
canal:isOpen: true# canal的相关配置
canalConfig:host: 124.70.138.34port: 11111

在这里插入图片描述

Redis存Java对象的配置类

package com.tianju.fresh.config.redis;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration
public class RedisSerializeConfig {@Beanpublic RedisTemplate redisTemplateInit(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(redisConnectionFactory);//设置序列化Key的实例化对象redisTemplate.setKeySerializer(new StringRedisSerializer());//设置序列化Value的实例化对象redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());/**** 设置Hash类型存储时,对象序列化报错解决*/redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());return redisTemplate;}
}

2.canal自动更新代码

canal自动更新的代码,用canal管道监听MySQL数据变化,自动更新redis缓存

package com.tianju.fresh.config.redis;import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;import com.alibaba.otter.canal.client.CanalConnectors;
import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.common.utils.AddressUtils;
import com.alibaba.otter.canal.protocol.Message;
import com.alibaba.otter.canal.protocol.CanalEntry.Column;
import com.alibaba.otter.canal.protocol.CanalEntry.Entry;
import com.alibaba.otter.canal.protocol.CanalEntry.EntryType;
import com.alibaba.otter.canal.protocol.CanalEntry.EventType;
import com.alibaba.otter.canal.protocol.CanalEntry.RowChange;
import com.alibaba.otter.canal.protocol.CanalEntry.RowData;
import com.baomidou.mybatisplus.annotation.TableField;
import com.tianju.fresh.entity.common.GoodsTypeVo;
import com.tianju.fresh.entity.common.WarehouseVo;
import com.tianju.fresh.entity.goods.GoodsType;
import com.tianju.fresh.mapper.goods.GoodsTypeMapper;
import com.tianju.fresh.mapper.warehouse.StorehouseMapper;
import com.tianju.fresh.service.common.CommonStaticMethod;
import com.tianju.fresh.util.Constance;
import com.tianju.fresh.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;/*** 用canal管道监听MySQL数据变化,自动更新redis缓存*/
@Slf4j
@Component
public class AutoUpdateRedis {@Value("${canalConfig.host}")private String host;@Value("${canalConfig.port}")private Integer port;@Autowiredprivate RedisTemplate<String,Object> redisTemplate;@Autowiredprivate GoodsTypeMapper typeMapper;@Autowiredprivate StorehouseMapper storehouseMapper;@Autowiredprivate RedisUtil redisUtil;public void run() {// 创建链接final InetSocketAddress HOST = new InetSocketAddress(host,port);
//        final InetSocketAddress HOST = new InetSocketAddress("192.168.111.130",11111);CanalConnector connector = CanalConnectors.newSingleConnector(HOST, "example", "", "");int batchSize = 1000;int emptyCount = 0;try {connector.connect();connector.subscribe(".*\\..*");connector.rollback();int totalEmptyCount = 120;
//            while (emptyCount < totalEmptyCount) {while (true) {Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据long batchId = message.getId();int size = message.getEntries().size();if (batchId == -1 || size == 0) {emptyCount++;if(emptyCount % 100==0 || emptyCount==1){System.out.println("empty count : " + emptyCount);}try {Thread.sleep(1000);} catch (InterruptedException e) {}} else {emptyCount = 0;// System.out.printf("message[batchId=%s,size=%s] \n", batchId, size);printEntry(message.getEntries());}connector.ack(batchId); // 提交确认// connector.rollback(batchId); // 处理失败, 回滚数据}//            System.out.println("empty too many times, exit");} finally {connector.disconnect();}}private void printEntry(List<Entry> entrys) {for (Entry entry : entrys) {if (entry.getEntryType() == EntryType.TRANSACTIONBEGIN || entry.getEntryType() == EntryType.TRANSACTIONEND) {continue;}RowChange rowChage = null;try {rowChage = RowChange.parseFrom(entry.getStoreValue());} catch (Exception e) {throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(),e);}EventType eventType = rowChage.getEventType();System.out.println(String.format("================&gt; binlog[%s:%s] , name[%s,%s] , eventType : %s",entry.getHeader().getLogfileName(), entry.getHeader().getLogfileOffset(),entry.getHeader().getSchemaName(), entry.getHeader().getTableName(),eventType));String tableName = entry.getHeader().getTableName();if (Constance.LISTEN_TAB_NAMES.contains(tableName)){for (RowData rowData : rowChage.getRowDatasList()) {if (eventType == EventType.DELETE){// 删除之前log.debug("-------删除之前before");changeBefore(rowData.getBeforeColumnsList());log.debug("-------删除之后after");// 传修改的表名,和常量中的map对比,从而对应的redis里的key
//                        changeAfter(rowData.getAfterColumnsList(),tableName);}else if (eventType == EventType.INSERT){// 插入之前log.debug("-------插入之前before");changeBefore(rowData.getBeforeColumnsList());log.debug("-------插入之后after");// 传修改的表名,和常量中的map对比,从而对应的redis里的key
//                        changeAfter(rowData.getAfterColumnsList(),tableName);}else {// 修改之前log.debug("-------修改之前before");changeBefore(rowData.getBeforeColumnsList());log.debug("-------修改之后after");// 传修改的表名,和常量中的map对比,从而对应的redis里的keychangeAfter(rowData.getAfterColumnsList(),tableName);}}}}}/*** 数据库更新之前* @param columns*/private  void changeBefore(List<Column> columns) {for (Column column : columns) {System.out.println(column.getName() + " : " + column.getValue() + "    update=" + column.getUpdated());}}private  void changeAfter(List<Column> columns, String tableName) {for (Column column : columns) {System.out.println(column.getName() + " : " + column.getValue() + "    update=" + column.getUpdated());// 如果是商品类别表变化if ("name".equals(column.getName()) && Constance.GOODS_TYPE_TAB_NAME.equals(tableName)){ // 默认是名称那一列变化才更新缓存// 先删除key,再设置好新的keyMap tabNameToRedisKey = Constance.getTabNameToRedisKey();String redisKey = (String) tabNameToRedisKey.get(tableName);redisTemplate.delete(redisKey);// TODO:设置新的keyList<GoodsTypeVo> list = CommonStaticMethod.getGoodsTypeVos(typeMapper);redisUtil.saveObjectToRedis(Constance.GOODS_TYPE_REDIS_KEY, list);break;}// 如果是仓库那张表变化 storehouseif ("name".equals(column.getName()) && Constance.STOREHOUSE_TAB_NAME.equals(tableName)){ // 默认是名称那一列变化才更新缓存// 先删除key,再设置好新的keyMap tabNameToRedisKey = Constance.getTabNameToRedisKey();String redisKey = (String) tabNameToRedisKey.get(tableName);redisTemplate.delete(redisKey);// 设置新的key,存到redis里面List<WarehouseVo> list = CommonStaticMethod.getStorehouseVos(storehouseMapper);redisUtil.saveObjectToRedis(Constance.STOREHOUSE_REDIS_KEY,list);break;}}}
}

在这里插入图片描述

redis的工具类

package com.tianju.fresh.util;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;@Component
public class RedisUtil {@Autowiredprivate RedisTemplate<String,Object> redisTemplate;public void  saveObjectToRedis(String key,Object json){redisTemplate.opsForValue().set(key,json);}/*** 从redis里面获取json对象,如果没有,返回null* @param key* @return*/public Object getJsonFromRedis(String key){return redisTemplate.opsForValue().get(key);}}

监听数据库表,列名的常量类

package com.tianju.fresh.util;import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 专门放各种常量*/
public interface Constance {// 设置哪些数据库表需要监听,比如单位unit,仓库warehouse,商品类型等List<String> LISTEN_TAB_NAMES = Arrays.asList("goods_type","unit_commodity","warehouse_center","warehouse_tab");String GOODS_TYPE_TAB_NAME = "goods_type";String GOODS_TYPE_REDIS_KEY = "goodsTypeVo";String UNIT_TAB_NAME = "unit_commodity";String UNIT_REDIS_KEY = "unitVo";String WAREHOUSE_TAB_NAME = "warehouse_center";String WAREHOUSE_REDIS_KEY = "warehouseCenterVo";String STOREHOUSE_TAB_NAME = "warehouse_tab";String STOREHOUSE_REDIS_KEY = "storehouseVo";static Map getTabNameToRedisKey(){Map<String,String> map = new HashMap<>();map.put(GOODS_TYPE_TAB_NAME,"goodsTypeVo");map.put("unit_commodity","unitVo");map.put("warehouse_center","warehouseCenterVo");map.put("warehouse_tab","storehouseVo");return map;}
}

3.查询的业务层service代码

在这里插入图片描述

    @Overridepublic List<WarehouseVo> findStorehouse() {
//        List<Storehouse> storehouses = storehouseMapper.selectList(null);
//        List<WarehouseVo> list = new ArrayList<>(storehouses.size());
//        storehouses.forEach(s->{
//            list.add(new WarehouseVo(s.getId()+"", s.getName()));
//        });// 是否有小仓库的redis的keyBoolean hasKey = redisTemplate.hasKey(Constance.STOREHOUSE_REDIS_KEY);if (hasKey){ // 如果有,走缓存List<WarehouseVo> list = (List<WarehouseVo>) redisTemplate.opsForValue().get(Constance.STOREHOUSE_REDIS_KEY);log.debug("get storehouseVo from redis: "+list);return list;}// 如果没有从数据库查询,存到redis里面List<WarehouseVo> list = CommonStaticMethod.getStorehouseVos(storehouseMapper);log.debug("get storehouseVo from mysql: "+list);redisUtil.saveObjectToRedis(Constance.STOREHOUSE_REDIS_KEY, list);return list;}

4.主启动类

主启动类实现implements CommandLineRunner方法,启动canal通道,进行监听数据库的变化,实现缓存同步更新

package com.woniu.fresh;import com.woniu.fresh.config.redis.AutoUpdateRedis;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableScheduling;@SpringBootApplication
@Slf4j
@EnableAspectJAutoProxy // 让动态代理生效
@EnableScheduling // 让定时任务生效
public class FreshApp implements CommandLineRunner {public static void main(String[] args) {SpringApplication.run(FreshApp.class);}@Autowiredprivate AutoUpdateRedis autoUpdateRedis;@Value("${canal.isOpen}")private Boolean isCanal;@Overridepublic void run(String... args) throws Exception {if (isCanal){log.debug(">>>>>启动缓存自动更新");autoUpdateRedis.run();}}
}

5.前端vue代码

<template><div><el-row><el-col :span="24"><el-form :inline="true" label-width="80px"><el-form-item label="领料单号"><el-input v-model="findGoodsParams.shoppingNo"></el-input></el-form-item><el-form-item label="领料数量≥"><el-input v-model="findGoodsParams.nums"></el-input></el-form-item><el-form-item label="原料名称"><el-input v-model="findGoodsParams.rawName"></el-input></el-form-item><el-form-item label="领料仓库"><el-select v-model="findGoodsParams.warehouseId" placeholder="请选择"><el-option v-for="item in commondata.storehouse" :key="item.value" :label="item.label":value="item.value"></el-option></el-select></el-form-item><el-form-item label="状态"><el-select v-model="findGoodsParams.status" placeholder="请选择"><el-option v-for="item in commondata.status" :key="item.value" :label="item.label":value="item.value"></el-option></el-select></el-form-item><el-form-item><el-button type="primary" @click="findGoods">查询</el-button><el-button type="success" @click="reFindGoods">重置</el-button></el-form-item></el-form></el-col></el-row><el-row><el-col :span="24"><el-button type="success" @click="addGoodsBtn">新增</el-button><el-button type="warning" icon="el-icon-edit" @click="batchDelete">批量审批</el-button><el-button type="primary" icon="el-icon-magic-stick" @click="batchPickDoing">批量领取中</el-button><el-button type="primary" icon="el-icon-shopping-cart-full" @click="batchPickDown">批量已领完</el-button></el-col></el-row><el-row><el-col :span="24"><el-table :data="tableData" style="width: 100%" @selection-change="handleSelectionChange"><el-table-column type="selection" width="55"></el-table-column><el-table-column prop="pickNo" label="领料单号" width="240"></el-table-column><el-table-column prop="name" label="原材料名" width="100"></el-table-column><el-table-column prop="nums" label="领料数量" width="80"></el-table-column><el-table-column prop="unit" label="单位" width="80"></el-table-column><el-table-column prop="warehouse" label="领料仓库" width="180"></el-table-column><el-table-column prop="emp" label="仓管员" width="150"></el-table-column><el-table-column prop="status" label="状态" width="80"><template slot-scope="scope"><span v-if="scope.row.status == '0'" style="color: red;">未审批</span><span v-if="scope.row.status == '1'" style="color: rgb(9, 209, 109);">已审批</span><span v-if="scope.row.status == '2'" style="color: rgb(44, 39, 205);">已领取</span><span v-if="scope.row.status == '3'" style="color: rgb(173, 16, 157);">已领完</span><!-- {{ scope.row.status == 1 ? '已上架' : '下架' }} --></template></el-table-column><el-table-column label="操作"><template slot-scope="scope"><el-button type="primary" icon="el-icon-edit" circle@click="loadBtn(scope.row.id)">审批</el-button></template></el-table-column></el-table></el-col></el-row><el-row><el-col :span="24"><div class="block"><el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange":current-page.sync="currentPage" :page-sizes="[3, 5, 10]" :page-size="3"layout="total,sizes, prev, pager, next" :total=total></el-pagination></div></el-col></el-row><!-- 新增原材料入库弹窗 ******* --><el-dialog title="添加原材料单" :visible.sync="b"><el-form><el-row><el-col :span="12"><el-form-item label="原料名称" clearable><el-select v-model="shoppingNoId" placeholder="请选择"style="width: 355px;margin-right:px;margin-left:px" @change="selectBuyNo"><el-option v-for="item in shoppingIdToNo" :key="item.label" :label="item.label":value="item.label"></el-option></el-select></el-form-item><el-form-item label="数量" :label-width="formLabelWidth"><el-input v-model="goods.nums" autocomplete="off"></el-input></el-form-item><el-form-item label="单位" clearable><el-select v-model="goods.unit" placeholder="请选择商品单位"style="width: 355px;margin-right:px;margin-left:px"><el-option v-for="item in commondata.unit" :key="item.value" :label="item.label":value="item.value"></el-option></el-select></el-form-item><el-form-item label="中心仓库" clearable><el-select v-model="goods.warehouse" placeholder="请选择" disabledstyle="width: 355px;margin-right:px;margin-left:px"><el-option v-for="item in commondata.warehouse" :key="item.value" :label="item.label":value="item.value"></el-option></el-select></el-form-item><el-form-item label="领料仓库" clearable><el-select v-model="goods.storehouse" placeholder="请选择"style="width: 355px;margin-right:px;margin-left:px"><el-option v-for="item in commondata.storehouse" :key="item.value" :label="item.label":value="item.value"></el-option></el-select></el-form-item></el-col></el-row></el-form><div slot="footer" class="dialog-footer"><el-button @click="b = false">取 消</el-button><el-button type="primary" @click="addGoods()">确 定</el-button></div></el-dialog></div>
</template><script>
export default {data() {return {findGoodsParams: {"pickNo": "", "name": "", "nums": "", "storehouseId": "", "status": ""},// 批量删除的iddeleteIds: [],tableData: [{"id": 1,"pickNo": "PICK2023090818003927","name": "富士苹果","nums": "350.00","unit": "千克","warehouse": "南京江宁生鲜1号仓库","storehouseId": 2,"emp": "领料操作员1李四","status": "0"}],// 分页相关的参数total: 10,currentPage: 1,pageSize: 3,options: [{ value: '0', label: '未审批' },{ value: '1', label: '审批通过' },{ value: '2', label: '已领取' },{ value: '3', label: '已领完' },],commondata: {"storehouse": [{"value": 1, "label": "南京中心仓库南京总统府"},],"status": [{ "value": 0, "label": "未审批" },{ "value": 1, "label": "审批通过" }]},// 新增商品弹窗控制变量b: false,// 新增的入库信息 goods shoppingNoIdgoods: {"name":"富士苹果","nums":"200.56","unit":"1",// 中心仓库"warehouse":"1", // 目标仓库"storehouse":"2"},formLabelWidth: '100px',// 新增原材料入库清单addRawTab:[{"name": {"unit": "","warehouse": {"1": 1000.20}}},],// 和上面进行比对shoppingNoId:"",// 选择采购清单的下拉框shoppingIdToNo: [{"label": "BUY2023091317093927"},],}},methods: {handleSizeChange(val) {console.log(`每页 ${val} 条`);this.pageSize = valthis.findGoods()},handleCurrentChange(val) {console.log(`当前页: ${val}`);this.pageNum = valthis.findGoods()},findGoods() {let params = {}params.pageNum = this.currentPageparams.pageSize = this.pageSizeparams.param = this.findGoodsParamsconsole.log(params)this.$axios.post("/api/warehouse/findPagePickRaw", params).then(response => {let resp = response.dataconsole.log(resp)if (resp.resultCode.code == 20000) {this.tableData = resp.results.listthis.total = resp.results.totalthis.currentPage = resp.results.pageNumthis.pageSize = resp.results.pageSize}})},reFindGoods() {let params = {}this.findGoodsParams = {"pickNo": "", "name": "", "nums": "", "storehouseId": "", "status": ""},params.pageNum = this.currentPageparams.pageSize = this.pageSizeparams.param = this.findGoodsParamsconsole.log(params)this.$axios.post("/api/warehouse/findPagePickRaw", params).then(response => {let resp = response.dataconsole.log(resp)if (resp.resultCode.code == 20000) {this.tableData = resp.results.listthis.total = resp.results.totalthis.currentPage = resp.results.pageNumthis.pageSize = resp.results.pageSize}})},handleSelectionChange(val) {this.deleteIds = []console.log(val);val.forEach(e => this.deleteIds.push(e.id))},// 批量修改领取中batchPickDoing() {console.log(this.deleteIds)this.$axios.put("/api/warehouse/batchDoingPickMaterial", this.deleteIds).then(response => {let resp = response.dataconsole.log(resp)if (resp.resultCode.code == 20000) {this.findGoods()} else {alert(resp.results)}})},// 批量修改已领完batchPickDown() {console.log(this.deleteIds)this.$axios.put("/api/warehouse/batchDownPickMaterial", this.deleteIds).then(response => {let resp = response.dataconsole.log(resp)if (resp.resultCode.code == 20000) {this.findGoods()} else {alert(resp.results)}})},// 批量审批通过batchDelete() {console.log(this.deleteIds)this.$axios.put("/api/warehouse/batchPassPickMaterial", this.deleteIds).then(response => {let resp = response.dataconsole.log(resp)if (resp.resultCode.code == 20000) {this.findGoods()} else {alert(resp.results)}})},// 逐一审批通过loadBtn(val) {console.log(val)const deleteIds = []deleteIds.push(val)console.log(deleteIds)this.$axios.put("/api/warehouse/batchPassPickMaterial", deleteIds).then(response => {let resp = response.dataconsole.log(resp)if (resp.resultCode.code == 20000) {this.findGoods()} else {alert(resp.results)}})},// 获取公共数据getCommonData() {this.$axios.get("/api/common/pickCommon").then(response => {let resp = response.dataif (resp.resultCode.code == 20000) {this.commondata = resp.resultsconsole.log("#############")console.log(this.commondata)}})},addGoodsBtn() {this.b = truethis.goods = {} this.shoppingIdToNo = []this.$axios.get('/api/warehouse/findRawNames').then(res => {if (res.data.resultCode.code == 20000) {this.addRawTab = res.data.resultsconsole.log(this.addRawTab)this.addRawTab.forEach(r=>{this.shoppingIdToNo.push({"label": Object.keys(r)[0]})})console.log(this.shoppingIdToNo)}})},// 弹出的新增窗口的添加addGoods() {console.log("#############")console.log(this.goods)this.$axios.post('/api/warehouse/addPickRaw', this.goods).then(res => {console.log("&&&&&")console.log(res.data)if (res.data.resultCode.code == 20000) {alert('添加成功')this.findGoods()}else{alert('添加失败')}}),this.b = false},// 绑定下拉框的选择事件selectBuyNo(){console.log("change")const goodsTemp = this.addRawTab.filter(r=> Object.keys(r)[0] == this.shoppingNoId)[0]console.log(goodsTemp)const nameTmp = Object.keys(goodsTemp)[0]const nextTmp = Object.values(goodsTemp)[0].warehouseconst keyTmp = Object.keys(nextTmp)[0]console.log(nextTmp)this.goods={name:nameTmp,nums:nextTmp[keyTmp],unit:Object.values(goodsTemp)[0].unit+"",warehouse:Object.keys(nextTmp)[0]}console.log(this.goods)},},created() {this.findGoods()this.getCommonData()}}</script>

总结

1.介绍基于canal实现数据库和缓存同步的流程;
2.给出了核心diamante的源码;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/149205.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

UG\NX CAM二次开发 加工模块获取 UF _ask_application_module

文章作者:代工 来源网站:NX CAM二次开发专栏 简介: UG\NX CAM二次开发 加工模块获取 UF _ask_application_module 代码: void MyClass::do_it() { // TODO: add your code here // 获取NX当前所在的模块 int module_id = 0; // UF_ask_application_module(&…

JMeter性能测试

性能测试前言 老师开局一句话&#xff1a;性能测试和你会不会JMeter一点关系没有…… 作者坚持技多不压身的原则&#xff0c;还是多学一点JMeter吧&#xff0c;看老师到底要怎么讲下去&#xff0c;什么并发量、吞吐量啥的…… 性能测试的核心思想&#xff1a;在于创造大量并发去…

深度学习基础之参数量(3)

一般的CNN网络的参数量估计代码 class ResidualBlock(nn.Module):def __init__(self, in_planes, planes, norm_fngroup, stride1):super(ResidualBlock, self).__init__()print(in_planes, planes, norm_fn, stride)self.conv1 nn.Conv2d(in_planes, planes, kernel_size3, …

MySQL——使用mysqldump备份与恢复数据

目录 1.mysqldump简介 2.mysqldump备份数据 2.1 备份所有数据库 2.2 备份一个/多个数据库 2.3 备份指定库中的指定表 3.mysqldump恢复数据 3.1 恢复数据库 3.2 恢复数据表 1.mysqldump简介 mysqldump命令可以将数据库中指定或所有的库、表导出为SQL脚本。表的结构和表中…

互联网Java工程师面试题·Elasticsearch 篇·第一弹

目录 1、elasticsearch 了解多少&#xff0c;说说你们公司 es 的集群架构&#xff0c;索引数据大小&#xff0c;分片有多少&#xff0c;以及一些调优手段 。 1.1 设计阶段调优 1.2 写入调优 1.3 查询调优 1.4 其他调优 2、elasticsearch 的倒排索引是什么 3、elastic…

使用Pytorch从零实现Vision Transformer

在这篇文章中,我们将基于Pytorch框架从头实现Vision Transformer模型,并附录完整代码。 Vision Transformer(ViT)是一种基于Transformer架构的深度学习模型,用于处理计算机视觉任务。它将图像分割成小的图像块(patches),然后使用Transformer编码器来处理这些图像块。V…

【单片机】16-LCD1602和12864和LCD9648显示器

1.LCD显示器相关背景 1.LCD简介 &#xff08;1&#xff09;显示器&#xff0c;常见显示器&#xff1a;电视&#xff0c;电脑 &#xff08;2&#xff09;LCD&#xff08;Liquid Crystal Display&#xff09;&#xff0c;液晶显示器&#xff0c;原理介绍 &#xff08;3&#xff…

十天学完基础数据结构-第九天(堆(Heap))

堆的基本概念 堆是一种特殊的树形数据结构&#xff0c;通常用于实现优先级队列。堆具有以下两个主要特点&#xff1a; 父节点的值始终大于或等于其子节点的值&#xff08;最大堆&#xff09;&#xff0c;或者父节点的值始终小于或等于其子节点的值&#xff08;最小堆&#xff…

【2023年11月第四版教材】第18章《项目绩效域》(合集篇)

第18章《项目绩效域》&#xff08;合集篇&#xff09; 1 章节内容2 干系人绩效域2.1 绩效要点2.2 执行效果检查2.3 与其他绩效域的相互作用 3 团队绩效域3.1 绩效要点3.2 与其他绩效域的相互作用3.3 执行效果检查3.4 开发方法和生命周期绩效域 4 绩效要点4.1 与其他绩效域的相互…

2023/10/4 QT实现TCP服务器客户端搭建

服务器端&#xff1a; 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> #include <QTcpSocket> #include <QList> #include <QMessageBox> #include <QDebug>QT_BEGIN_NAMESPACE namespace Ui { cla…

十天学完基础数据结构-第八天(哈希表(Hash Table))

哈希表的基本概念 哈希表是一种数据结构&#xff0c;用于存储键值对。它的核心思想是将键通过哈希函数转化为索引&#xff0c;然后将值存储在该索引位置的数据结构中。 哈希函数的作用 哈希函数是哈希表的关键部分。它将输入&#xff08;键&#xff09;映射到哈希表的索引位…

Ubuntu使用cmake和vscode开发自己的项目,引用自己的头文件和openCV

创建文件夹 mkdir my_proj 继续创建include 和 src文件夹&#xff0c;形成如下的目录结构 用vscode打开项目 创建add.h #ifndef ADD_H #define ADD_Hint add(int numA, int numB);#endif add.cpp #include "add.h"int add(int numA, int numB) {return numA nu…

实战型开发2/3--架构设计

这里谈及在代码设计阶段以及重构阶段要考虑的架构方面问题&#xff0c;可以说是开发过程中的中层阶段&#xff1b; 主要是将 < the art of unix programming>< clean architecture>< the pragmatic programmer>< design patterns> 等几本书结合实践做…

[NSSRound#1 Basic]sql_by_sql - 二次注入+布尔盲注||sqlmap

进入注册界面后   假设sql&#xff1a;update user set password ‘’ where username ‘’ and password ‘’     此时如果我们注册的用户名是admin’–、admin’#、admin’–的话   update user set password ‘123’ where username ‘admin’#’ and passwor…

[架构之路-231]:计算机硬件与体系结构 - 性能评估汇总,性能优化加速比

目录 一、计算机体系结构 二、计算机性能评估 2.1 分类方法1 2.2 分类方法2 三、常见的专项性能测试工具 3.1 浮点运算性能&#xff08;FLOPS&#xff09; 3.2 综合理论性能法 3.3 历史基准测试&#xff08;跑分软件&#xff09;&#xff1a;通过运行典型的综合性的程序…

毕设-原创医疗预约挂号平台分享

医疗预约挂号平台 不是尚医通项目&#xff0c;先看项目质量&#xff08;有源码论文&#xff09; 项目链接&#xff1a;医疗预约挂号平台git地址 演示视频&#xff1a;医疗预约挂号平台 功能结构图 登录注册模块&#xff1a;该模块具体分为登录和注册两个功能&#xff0c;这些…

想要精通算法和SQL的成长之路 - 最长连续序列

想要精通算法和SQL的成长之路 - 最长连续序列 前言一. 最长连续序列1.1 并查集数据结构创建1.2 find 查找1.3 union 合并操作1.4 最终代码 前言 想要精通算法和SQL的成长之路 - 系列导航 并查集的运用 一. 最长连续序列 原题链接 这个题目&#xff0c;如何使用并查集是一个小难…

R语言教程课后习题答案(持续更新中~~)

R语言教程网址如下 https://www.math.pku.edu.cn/teachers/lidf/docs/Rbook/html/_Rbook/index.html 目录 source()函数可以运行保存在一个文本文件中的源程序 R向量下标和子集 数值型向量及其运算 日期功能 R因子类型 source()函数可以运行保存在一个文本文件中的源程序…

【C语言】动态通讯录(超详细)

通讯录是一个可以很好锻炼我们对结构体的使用&#xff0c;加深对结构体的理解&#xff0c;在为以后学习数据结构打下结实的基础 这里我们想设计一个有添加联系人&#xff0c;删除联系人&#xff0c;查找联系人&#xff0c;修改联系人&#xff0c;展示联系人&#xff0c;排序这几…

快速了解Spring Cache

SpringCache是一个框架&#xff0c;实现了基于注解的缓存功能&#xff0c;只需要简单的加一个注解&#xff0c;就可以实现缓存功能。 SpringCache提供了一层抽象&#xff0c;底层可以切换不同的缓存实现。例如&#xff1a; EHChche Redis Caffeine 常用注解&#xff1a; Enabl…