springboot mybatis-plus 对接 sqlserver 数据库 批处理的问题 批量更新添加数据 方案三

news/2024/5/24 6:13:32/

问题:
在对接 sqlserver数据库的时候 主子表 保存的时候
子表批量保存 使用的 mybatis-plus提供的 saveOrUpdateBatch saveBatch 这两个方法
但是 报错
报错内容为 :
com.microsoft.sqlserver.jdbc.SQLServerException: 必须执行该语句才能获得结果。

框架版本
sprin boot 2.0 +
mybatis-plus 3.3.1
mybatis-plus 代码生成器 mybatis-plus-generator: 3.3.2

SQL Server JDBC: mssql-jdbc 版本 :8.4.1.jre8

经过排查后
猜测 应该是 mssql-jdbc 和 mybatis-plus 不兼容把

mssql-jdbc 和 mybatis-plus 都调整的了版本 还是不行

最后我的结论 应该是 mybatis-plus在处理 sqlserver 批处理的时候
没有考虑这种情况
最后也没找到合适的解决方案

以前写过一篇博客 解决这个问题

https://blog.csdn.net/Drug_/article/details/129336556

我当时采用的是 博客中方案一 的处理方法
异常捕获一下
当时测试是没有问题的
但是 经过使用 发现还是有问题 会丢数据

以前的博客中也提供了方式二 就是写 xml文件 原生sql 执行

经过测试 方式二 是非常好用的

但是 我接触java的时候 就开始使用 mybatis-plus

习惯了调方法 ,就很烦写xml

而且我在学习 以前java web框架 发现以前的web框架 都是在编写xml
我有点感叹 这一代的java 程序员好幸福 , 基本不用写 xml 文件
我也有点怀念php框架的 操作数据库的方法

所以为了 批量处理数据 不写大量的 xml文件

既然 框架没办法解决这个问题

经过 两天的摸索 又看了底层的实现方式
于是我就 在 mybatis-plus 依赖的基础上 封装了一个 自定义的 saveOrUpdateBatch 方法

遇到批量处理数据 就调用自己的

以下是封装的代码
我们使用 mybatis-plus 自动生成文件
他默认 继承 ServiceImpl 和 BaseMapper 这两个类

我就 又封装了两个文件
CommonService 和 CommonMapper
分别 继承ServiceImpl 和 BaseMapper

然后 让各个表 的Mapper和Service 都来继承 CommonService 和 CommonMapper

CommonMapper 代码

package com.erp.yt.common.init.appService;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;import java.util.List;
import java.util.Map;/*** User: Json* <p>* Date: 2023/3/30**/
public interface CommonMapper<T> extends BaseMapper<T> {@Insert({"<script>" +"INSERT INTO ${table} (${tableFieldList}) VALUES " +"<foreach collection=\"tableFieldValueList\" item=\"item\" separator=\",\">" +" ${item} " +"</foreach>" +" ;" +"</script>"})int insertAll(@Param("table") String table,@Param("tableFieldList") String tableFieldList,@Param("tableFieldValueList") List<Object> tableFieldValueList);//目前不会 返回 主键id//针对insert有效,当有关联表操作的时候,可以先插入主表,然后根据主表返回的主键id去落库详情表//如果需要// 加入 第四个参数 传入一个对象 然后 keyProperty="对象的主键id",// 则会返回id//@Options(useGeneratedKeys=true,keyProperty="EtMaintainsub.id",keyColumn="id")//useGeneratedKeys 是否返回生成的主键//keyProperty 传入对象中的对象名//keyColumn 数据库中的字段名@Update({"<script>" +"<foreach collection=\"fieldValueList\" item=\"item\" separator=\";\">" +" UPDATE ${table} SET ${item.fieldValue} WHERE ${item.where}" +"</foreach>" +"</script>"})int updateAll(@Param("table") String table, @Param("fieldValueList") List<Map<String,Object>> fieldValueList);}

CommonService 代码

package com.erp.yt.common.init.appService;import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.erp.yt.common.init.utils.DateUtil;
import com.erp.yt.common.init.utils.RequestUtils;
import com.erp.yt.common.init.utils.WhlUtil;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;/*** User: Json* <p>* T  实体类* Rq 可以传实体类 也可以传 请求类  这个泛型 在这 个代码里没有用 也可以删掉* 方法没有抽取封装 可自行优化使用* Date: 2022/6/30**/public class CommonService<M extends CommonMapper<T>, T, Rq> extends ServiceImpl<M, T> {//没有控制 批量操作条数  调用mybatisplus框架方法的话 框架默认是 1000条@Transactional(rollbackFor = Exception.class)public boolean saveOrUpdateBatchZdy(Collection<T> entityList) {TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);String keyProperty = tableInfo.getKeyProperty();//新增字段List<String> insertTableFieldList=new ArrayList<>();//新增字段值List<Object> insertTableFieldValueList=new ArrayList<>();//更新数据List<Map<String,Object>> updateFieldValueList=new ArrayList<>();for (T entity : entityList) {//  System.out.println(entity);Object idVal = ReflectionKit.getFieldValue(entity, keyProperty);if (com.baomidou.mybatisplus.core.toolkit.StringUtils.checkValNull(idVal) || Objects.isNull(getById((Serializable) idVal))) {//  System.out.println("新增");List<Object> tableFieldValue=new ArrayList<>();for (TableFieldInfo fieldInfo :tableInfo.getFieldList()){Object field = ReflectionKit.getFieldValue(entity, fieldInfo.getProperty());
//                    System.out.println("sql字段:"+fieldInfo.getColumn());
//                    System.out.println("实体字段类型:"+fieldInfo.getPropertyType());
//                    System.out.println("实体类字段:"+fieldInfo.getProperty());
//                    System.out.println("实体类字段值:"+field);if(!insertTableFieldList.contains(fieldInfo.getColumn())){insertTableFieldList.add(fieldInfo.getColumn());}//如果前端传个空字符串"" 应该会被执行 没有判断 "" 空字符串的情况,// 如果需要判断 // 可根据实体类上的 mybatisplus 上的 注解// 多个判断 应该就可以了if(ObjectUtils.isEmpty(field)){//自动填充 根据  mybatisplus 的注解 判断 填充就好if(FieldFill.INSERT_UPDATE.equals(fieldInfo.getFieldFill())){// 目前自动填充 只有 更新人 更新时间 创建人 和创建时间// 所以只用判断数据类型就好  不用针对某个字段进行判断if(fieldInfo.getPropertyType().equals(LocalDateTime.class)){Date strDate= DateUtil.localDateToDateTime(LocalDateTime.now());tableFieldValue.add("'"+DateUtil.format(strDate,"yyyy-MM-dd HH:mm:ss")+"'");}if(fieldInfo.getPropertyType().equals(String.class)){tableFieldValue.add("'"+RequestUtils.getUsername()+"'");}}else if(FieldFill.INSERT.equals(fieldInfo.getFieldFill())){if(fieldInfo.getPropertyType().equals(LocalDateTime.class)){Date strDate= DateUtil.localDateToDateTime(LocalDateTime.now());tableFieldValue.add("'"+DateUtil.format(strDate,"yyyy-MM-dd HH:mm:ss")+"'");}if(fieldInfo.getPropertyType().equals(String.class)){tableFieldValue.add("'"+RequestUtils.getUsername()+"'");}}else{tableFieldValue.add(null);}}else{//所有的字段值都转成字符串 插入// 时间 要特殊处理if(fieldInfo.getPropertyType().equals(LocalDateTime.class)){Date strDate= DateUtil.localDateToDateTime((LocalDateTime) field);tableFieldValue.add("'"+DateUtil.format(strDate,"yyyy-MM-dd HH:mm:ss")+"'");}else{tableFieldValue.add("'"+field+"'");}}}insertTableFieldValueList.add("( " +tableFieldValue.stream().map(String::valueOf).collect(Collectors.joining(","))+")");} else {//System.out.println("更新");Map<String,Object> updateFieldValueMap=new HashMap<>();//目前这个方法 只根据 主键id更新updateFieldValueMap.put("where",keyProperty+"="+idVal);List<String> updateFieldValue=new ArrayList<>();for (TableFieldInfo fieldInfoUpdate :tableInfo.getFieldList()){Object field = ReflectionKit.getFieldValue(entity, fieldInfoUpdate.getProperty());// System.out.println("sql字段:"+fieldInfoUpdate.getColumn());//  System.out.println("实体字段类型:"+fieldInfoUpdate.getPropertyType());//  System.out.println("实体类字段:"+fieldInfoUpdate.getProperty());//  System.out.println("实体类字段值:"+field);// 如果用mybatisplus框架的方法// 他会根据实体类的注解@TableField(updateStrategy = FieldStrategy.IGNORED )// 来判断 到底允许不允许更新 空字符串//如果 需要这种 空字符串的处理 这里需要根据 实体字段上面的注解 多做一个判断 应该就可以了if(ObjectUtils.isEmpty(field)){//自动填充 根据  mybatisplus 的注解 判断 填充就好if(FieldFill.INSERT_UPDATE.equals(fieldInfoUpdate.getFieldFill())){// 目前自动填充 只有 更新人 更新时间 创建人 和创建时间// 所以只用判断数据类型就好  不用针对某个字段进行判断if(fieldInfoUpdate.getPropertyType().equals(LocalDateTime.class)){Date strDate= DateUtil.localDateToDateTime(LocalDateTime.now());updateFieldValue.add(fieldInfoUpdate.getColumn()+"="+"'"+DateUtil.format(strDate,"yyyy-MM-dd HH:mm:ss")+"'");}if(fieldInfoUpdate.getPropertyType().equals(String.class)){updateFieldValue.add(fieldInfoUpdate.getColumn()+"="+"'"+RequestUtils.getUsername()+"'");}}else if(FieldFill.UPDATE.equals(fieldInfoUpdate.getFieldFill())){if(fieldInfoUpdate.getPropertyType().equals(LocalDateTime.class)){Date strDate= DateUtil.localDateToDateTime(LocalDateTime.now());updateFieldValue.add(fieldInfoUpdate.getColumn()+"="+"'"+DateUtil.format(strDate,"yyyy-MM-dd HH:mm:ss")+"'");}if(fieldInfoUpdate.getPropertyType().equals(String.class)){updateFieldValue.add(fieldInfoUpdate.getColumn()+"="+"'"+RequestUtils.getUsername()+"'");}}else{updateFieldValue.add(fieldInfoUpdate.getColumn()+"="+null);}}else{if(fieldInfoUpdate.getPropertyType().equals(LocalDateTime.class)){Date strDate= DateUtil.localDateToDateTime((LocalDateTime) field);updateFieldValue.add(fieldInfoUpdate.getColumn()+"="+"'"+DateUtil.format(strDate,"yyyy-MM-dd HH:mm:ss")+"'");}else{updateFieldValue.add(fieldInfoUpdate.getColumn()+"="+"'"+field+"'");}}updateFieldValueMap.put("fieldValue",updateFieldValue.stream().map(String::valueOf).collect(Collectors.joining(",")));}updateFieldValueList.add(updateFieldValueMap);}}if(!CollectionUtils.isEmpty(insertTableFieldList) && !CollectionUtils.isEmpty(insertTableFieldValueList)){//        System.out.println("最终字段:"+insertTableFieldList);//        System.out.println("最终字段值:"+insertTableFieldValueList);baseMapper.insertAll(WhlUtil.getSqlTableName(entityClass),insertTableFieldList.stream().map(String::valueOf).collect(Collectors.joining(",")),insertTableFieldValueList);}if(!CollectionUtils.isEmpty(updateFieldValueList)){// System.out.println("最终更新语句: "+updateFieldValueList);baseMapper.updateAll(WhlUtil.getSqlTableName(entityClass),updateFieldValueList);}return true;}}

工具类:
WhlUtil.getSqlTableName(EtMaintainsub.class) 这个工具类 主要是去拿实体类上的 表名

/*** @param clazz 实体类.class* @return 物理表名* **/public static  String getSqlTableName(Class<?> clazz){if(clazz.isAnnotationPresent(TableName.class)){TableName table = clazz.getAnnotation(TableName.class);String tableName = table.value();if(StringUtils.isEmpty(tableName)){throw new ErpRuntimeException("@TableName注解value不存在,无法获取表名");}return tableName;}else{throw new ErpRuntimeException("@TableName注解不存在,无法获取表名");}}

//调用 测试

saveOrUpdateBatchZdy(etMaintain.getEtMaintainsubList());

//以上就是封装的公共的方法 主要利用 泛型 封装成 公共的操作类

下面再分享一个 写死实体类的方法
下面方法没有 实现自动填充 自行补充就好

//没有控制 批量操作条数  调用mybatisplus框架方法的话 框架默认是 1000条@Transactional(rollbackFor = Exception.class)public boolean saveOrUpdateBatchZdy(Collection<EtMaintainsub> entityList) {TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);String keyProperty = tableInfo.getKeyProperty();//新增字段List<String> insertTableFieldList=new ArrayList<>();//新增字段值List<Object> insertTableFieldValueList=new ArrayList<>();//更新数据List<Map<String,Object>> updateFieldValueList=new ArrayList<>();for (EtMaintainsub entity : entityList) {//  System.out.println(entity);Object idVal = ReflectionKit.getFieldValue(entity, keyProperty);if (StringUtils.checkValNull(idVal) || Objects.isNull(getById((Serializable) idVal))) {//  System.out.println("新增");List<Object> tableFieldValue=new ArrayList<>();for (TableFieldInfo fieldInfo :tableInfo.getFieldList()){Object field = ReflectionKit.getFieldValue(entity, fieldInfo.getProperty());
//                    System.out.println("sql字段:"+fieldInfo.getColumn());
//                    System.out.println("实体字段类型:"+fieldInfo.getPropertyType());
//                    System.out.println("实体类字段:"+fieldInfo.getProperty());
//                    System.out.println("实体类字段值:"+field);if(!insertTableFieldList.contains(fieldInfo.getColumn())){insertTableFieldList.add(fieldInfo.getColumn());}//如果前端传个空字符串"" 应该会被执行 没有判断 "" 空字符串的情况,如果需要多个判断实体字段类型应该就可以了if(ObjectUtils.isEmpty(field)){tableFieldValue.add(field);}else{tableFieldValue.add("'"+field+"'");}}insertTableFieldValueList.add("( " +tableFieldValue.stream().map(String::valueOf).collect(Collectors.joining(","))+")");} else {System.out.println("更新");Map<String,Object> updateFieldValueMap=new HashMap<>();//目前这个方法 只根据 主键id更新updateFieldValueMap.put("where",keyProperty+"="+idVal);List<String> updateFieldValue=new ArrayList<>();for (TableFieldInfo fieldInfoUpdate :tableInfo.getFieldList()){Object field = ReflectionKit.getFieldValue(entity, fieldInfoUpdate.getProperty());// System.out.println("sql字段:"+fieldInfoUpdate.getColumn());//  System.out.println("实体字段类型:"+fieldInfoUpdate.getPropertyType());//  System.out.println("实体类字段:"+fieldInfoUpdate.getProperty());//  System.out.println("实体类字段值:"+field);// 如果用mybatisplus框架的方法// 他会根据实体类的注解@TableField(updateStrategy = FieldStrategy.IGNORED )// 来判断 到底允许不允许更新 空字符串//如果 需要这种 空字符串的处理 这里需要根据 实体字段类型 多做一个判断 应该就可以了if(ObjectUtils.isEmpty(field)){updateFieldValue.add(fieldInfoUpdate.getColumn()+"="+field);}else{updateFieldValue.add(fieldInfoUpdate.getColumn()+"="+"'"+field+"'");}updateFieldValueMap.put("fieldValue",updateFieldValue.stream().map(String::valueOf).collect(Collectors.joining(",")));}updateFieldValueList.add(updateFieldValueMap);}}if(!CollectionUtils.isEmpty(insertTableFieldList) && !CollectionUtils.isEmpty(insertTableFieldValueList)){//        System.out.println("最终字段:"+insertTableFieldList);//        System.out.println("最终字段值:"+insertTableFieldValueList);baseMapper.insertAll(WhlUtil.getSqlTableName(EtMaintainsub.class),insertTableFieldList.stream().map(String::valueOf).collect(Collectors.joining(",")),insertTableFieldValueList);}if(!CollectionUtils.isEmpty(updateFieldValueList)){// System.out.println("最终更新语句: "+updateFieldValueList);baseMapper.updateAll(WhlUtil.getSqlTableName(EtMaintainsub.class),updateFieldValueList);}return true;}

http://www.ppmy.cn/news/48257.html

相关文章

springboot web项目统一时区方案

背景 springboot项目国际化中&#xff0c;会遇到用户选择的时间和最终存到数据库的时间不一致&#xff0c;可能就是项目开发和部署时的时区没有处理好&#xff0c;导致时间转换出现了问题。 先了解时区都有哪些&#xff1a; 1.GMT&#xff1a;Greenwich Mean Time 格林威治…

Kubernetes Operator开发

案例一 Traefik Operator开发 1.kubebuilder 创建项目 2.Crontroller开发与部署 开发环境准备 kubebuilder 介绍 CRD的开发与部署 Crontroller开发与部署 Operator功能设计 借助operator完成 和企业内部注册中心打通 这里以Traefiketcd的模式为例进行演示说明 在这里etcd p…

1.2和1.3、GCC

1.2和1.3、GCC 1.2和1.3、GCC1.2.1、什么是GCC1.2.2、编程语言的发展1.2.3、GCC工作流程1.2.4、gcc和g的区别1.2.5、GCC常用参数选项实际操作①接下来进行预处理操作&#xff08;test.c为需要预处理的源代码&#xff0c;test.i为要生成的目标代码&#xff09;②汇编操作&#x…

多线程基础

1.多线程基础概念 多线程&#xff1a;让程序同时做多件事情 多线程作用&#xff1a;提高效率 并发&#xff1a;在同一时间&#xff0c;有多个指令在单个cpu上交替执行 并行&#xff1a;在同一时刻&#xff0c;有多个指令在多个cpu上同时执行 2.多线程的实现 (1)继承Thread类…

电脑卡顿反应慢怎么处理?电脑提速,4个方法!

案例&#xff1a;电脑卡顿反应慢怎么处理&#xff1f; 【快帮帮我&#xff01;我的电脑现在越用越卡了&#xff0c;有时候光是打开一个文件都要卡好几分钟&#xff0c;我真的太难了&#xff0c;有什么可以加速电脑反应速度的好方法吗&#xff1f;万分感谢&#xff01;】 随着…

es7.x集群部署-多台物理机部署-docker环境部署-docker-compose管理

es集群部署文档 部署es服务的三台服务器的ip和host分分别是&#xff1a; iphost_name192.168.1.2web02192.168.1.3storage02192.168.1.4Storage03 这个配置需要在服务器上编写对应的hosts文件&#xff0c;然后才可以使用host进行配置。 本次部署没有外挂配置文件&#xff0…

手把手教你 DVOL

分享本文在朋友圈的读者可获得本文数据和 Python 代码。留个言说已分享&#xff08;不用截屏&#xff09;我相信你&#xff0c;我会发给你百度盘下载链接。 本文长度为 6393 字&#xff0c;建议阅读 32 分钟 题图&#xff1a;SignalPlus Dashboard 0 引言 Deribit volatility (…

Qt音视频开发37-识别鼠标按下像素坐标

一、前言 在和视频交互过程中,用户一般需要在显示视频的通道上点击对应的区域,弹出对应的操作按钮,将当前点击的区域或者绘制的多边形区域坐标或者坐标点集合,发送出去,通知其他设备进行处理。比如识别到很多人脸,用户单击某个人脸后指定对该人脸进行详细的信息查询等;…

记录webpack安装

在安装完 yarn以后&#xff0c;yarn安装_阿巴资源站的博客-CSDN博客 然后开始安装webpack 看网上有人说npm install webpack-cli -g没成功&#xff0c;于是没试直接跳过坑 sudo npm install webpack -g --unsafe-permtrue --allow-root 然后提示&#xff1a; webpack -v On…

RabbitMQ:消息中间件

文章目录 概念管理界面简介4中常见交换器类型1.Direct交换器:2.Fanout交换器3.Topic交换器4.headers交换器 对象类型消息传递同步等待使用代码创建队列待续...... 概念 在微服务架构中项目之间项目A调用项目B 项目B调用项目C项目C调用项目D。。 用户必须等待项目之间内容依次的…

【闲聊杂谈】HTTPS原理详解

HTTPS和HTTP的区别 HTTP虽然使用极为广泛, 但是却存在不小的安全缺陷, 主要是其数据的明文传送和消息完整性检测的缺乏, 而这两点恰好是网络支付, 网络交易等新兴应用中安全方面最需要关注的。 关于 HTTP的明文数据传输, 攻击者最常用的攻击手法就是网络嗅探, 试图从传输过程…

亚马逊广告运营常见问答

同一个广告组中&#xff0c;建议投放多少个关键词呢&#xff1f; 同一广告组下我们建议投放的关键词至多不超过50个。 1.如关键词设置过少&#xff08;且前期无法用数据佐证其精准引流效果时&#xff09;&#xff0c;有可能导致广告曝光量较低&#xff1b; 2.如关键词设置过多…

SAS学习第9章:卡方检验之适合性检验与独立性检验

卡方检验就是统计样本的实际观测值与理论推断值之间的偏离程度&#xff0c;实际观测值与理论推断值之间的偏离程度就决定卡方值的大小&#xff0c;如果卡方值越大&#xff0c;二者偏差程度越大&#xff1b;反之&#xff0c;二者偏差越小&#xff1b;若两个值完全相等时&#xf…

查询练习:按等级查询

建立一个 grade 表代表学生的成绩等级&#xff0c;并插入数据&#xff1a; CREATE TABLE grade (low INT(3),upp INT(3),grade char(1) );INSERT INTO grade VALUES (90, 100, A); INSERT INTO grade VALUES (80, 89, B); INSERT INTO grade VALUES (70, 79, C); INSERT INTO …

U-Boot 命令使用

进入 uboot 的命令行模式以后输入“help”或者“&#xff1f;”&#xff0c;然后按下回车即可查看当前 uboot 所 支持的命令&#xff0c;如图 所示&#xff1a; 我们输入“help(或?) 命令名”既可以查看命令的详细用法&#xff0c;以“bootz”这 个命令为例&#xff0c;我们输…

使用FFMPEG库将YUV编码为H264

准备 ffmpeg 4.4 p准备一段yuv420p的格式的视频原始数据 这里我们使用命令直接提取 ffmpeg -i .\beautlWorld.mp4 -pixel_format yuv420p -s 1280x720 yuv420p_1280x720.yuv 编码流程 大致可以分为以下几步&#xff1a; 1.初始化编码器并设置参数 2.初始化AVPacket和AVFr…

适应大、中、小型医院的手术麻醉临床信息管理系统源码

手术麻醉管理系统是一款专门用于医院手术麻醉管理的软件系统&#xff0c;它可以帮助医院和医生更好地管理手术麻醉过程&#xff0c;提高手术麻醉的质量和安全性。本文将介绍手术麻醉管理系统的实现、功能概述、主要功能、系统设置、麻醉管理、术中记录、苏醒室记录、PCA实施及管…

gRPC(Java) keepAlive机制研究

文章目录 结论Client端KeepAlive使用入口简要时序列表 Server端KeepAlive使用入口简要时序列表KeepAliveEnforcer 基于java gRPC 1.24.2 分析 结论 gRPC keepAlive是grpc框架在应用层面连接保活的一种措施。即当grpc连接上没有业务数据时&#xff0c;是否发送pingpong,以保持连…

快手社招Java后端开发岗面试,被问麻了

社招面试是基于你的工作项目来展开问的&#xff0c;比如你项目用了 xxx 技术&#xff0c;那么面试就会追问你项目是怎么用 xxx 技术的&#xff0c;遇到什么难点和挑战&#xff0c;然后再考察一下这个 xxx 技术的原理。 今天就分享一位快手社招面经&#xff0c;岗位是后端开发&…

陪诊小程序开发|陪诊软件开发功能特色

为了提升就医的服务质量&#xff0c;人们对于医疗服务的需求也在不断提高。这几年随着生活水平和医疗水平的提升&#xff0c;陪诊服务越来越受到人们的重视和青睐&#xff0c;越来越多的人开始意识到&#xff0c;陪伴和关爱在疾病治疗过程中的重要性&#xff0c;为了更好的规划…