BaseMapper 接口介绍
基于 mybatis-mapper/provider 核心部分实现的基础的增删改查操作,提供了一个核心的 io.mybatis.mapper.BaseMapper
接口和一个 预定义 的 io.mybatis.mapper.Mapper
接口,BaseMapper
接口定义如下:
/*** 基础 Mapper 方法,可以在此基础上继承覆盖已有方法** @param <T> 实体类类型* @param <I> 主键类型* @author liuzh*/
public interface BaseMapper<T, I extends Serializable>extends EntityMapper<T, I>, ExampleMapper<T, Example<T>>, CursorMapper<T, Example<T>> {/*** Example 查询封装*/default ExampleWrapper<T, I> wrapper() {return new ExampleWrapper<>(BaseMapper.this, example());}/*** 根据主键更新实体中不为空的字段,强制字段不区分是否 null,都更新* <p>* 当前方法来自 {@link io.mybatis.mapper.fn.FnMapper},该接口中的其他方法用 {@link ExampleMapper} 也能实现** @param entity 实体类* @param forceUpdateFields 强制更新的字段,不区分字段是否为 null,通过 {@link Fn#of(Fn...)} 创建 {@link Fn.Fns}* @return 1成功,0失败*/@Lang(Caching.class)@UpdateProvider(type = FnProvider.class, method = "updateByPrimaryKeySelectiveWithForceFields")int updateByPrimaryKeySelectiveWithForceFields(@Param("entity") T entity, @Param("fns") Fn.Fns<T> forceUpdateFields);/*** 根据指定字段集合查询:field in (fieldValueList)* <p>* 这个方法是个示例,你也可以使用 Java8 的默认方法实现一些通用方法** @param field 字段* @param fieldValueList 字段值集合* @param <F> 字段类型* @return 实体列表*/default <F> List<T> selectByFieldList(Fn<T, F> field, Collection<F> fieldValueList) {Example<T> example = new Example<>();example.createCriteria().andIn((Fn<T, Object>) field.in(entityClass()), fieldValueList);return selectByExample(example);}/*** 根据指定字段集合删除:field in (fieldValueList)* <p>* 这个方法是个示例,你也可以使用 Java8 的默认方法实现一些通用方法** @param field 字段* @param fieldValueList 字段值集合* @param <F> 字段类型* @return 实体列表*/default <F> int deleteByFieldList(Fn<T, F> field, Collection<F> fieldValueList) {Example<T> example = new Example<>();example.createCriteria().andIn((Fn<T, Object>) field.in(entityClass()), fieldValueList);return deleteByExample(example);}}
这个接口展示了好几个通用方法的特点:
1.可以继承其他通用接口
2.可以直接复制其他接口中的通用方法定义
3.可以使用 Java8 默认方法灵活实现通用方法
在看 Mapper
接口:
/*** 自定义 Mapper 示例,这个 Mapper 基于主键自增重写了 insert 方法,主要用作示例* <p>* 当你使用 Oracle 或其他数据库时,insert 重写时也可以使用 @SelectKey 注解对主键进行定制** @param <T> 实体类类型* @param <I> 主键类型* @author liuzh*/
public interface Mapper<T, I extends Serializable> extends BaseMapper<T, I> {/*** 保存实体,默认主键自增,并且名称为 id* <p>* 这个方法是个示例,你可以在自己的接口中使用相同的方式覆盖父接口中的配置** @param entity 实体类* @return 1成功,0失败*/@Override@Lang(Caching.class)//@SelectKey(statement = "SELECT SEQ.NEXTVAL FROM DUAL", keyProperty = "id", before = true, resultType = long.class)@Options(useGeneratedKeys = true, keyProperty = "id")@InsertProvider(type = EntityProvider.class, method = "insert")int insert(T entity);/*** 保存实体中不为空的字段,默认主键自增,并且名称为 id* <p>* 这个方法是个示例,你可以在自己的接口中使用相同的方式覆盖父接口中的配置** @param entity 实体类* @return 1成功,0失败*/@Override@Lang(Caching.class)//@SelectKey(statement = "SELECT SEQ.NEXTVAL FROM DUAL", keyProperty = "id", before = true, resultType = long.class)@Options(useGeneratedKeys = true, keyProperty = "id")@InsertProvider(type = EntityProvider.class, method = "insertSelective")int insertSelective(T entity);}
这个接口中通过重写继承接口对主键进行了设置,除非你系统正好使用自增的 id
字段作为主键,否则不应该继承 Mapper
接口使用,应该使用 BaseMapper
作为基础。这个接口主要体现了一个特点:
4. 可以重写继承接口的定义。
除了上面已经提到的4个特点外,在下面内容中,还能看到一个特点,5. 那就是一个 provider 实现,通过修改接口方法的返回值和入参,就能变身无数个通用方法,通用方法的实现极其容易。
下面开始详细介绍这些特性。
2.1.1 继承其他通用接口
上面接口定义中,继承了 EntityMapper
, ExampleMapper
和 CursorMapper
接口。这些接口中定义了大量的通用方法, 通过继承使得 BaseMapper
接口获得了大量的通用方法,通过继承可以组合不同类别的方法。 你可以以 BaseMapper
为基础创建自己的基类接口,也可以完全自己创建集成 EntityMapper
等接口来选择需要的通用方法。
提供的最基础的接口可以通过 2.2~2.7 来了解其中具体的方法。
2.1.2 复制其他接口中的通用方法定义
这是最灵活的一点,在 BaseMapper
中直接复制了 FnMapper
的一个方法:
/*** 根据主键更新实体中不为空的字段,强制字段不区分是否 null,都更新* <p>* 当前方法来自 {@link io.mybatis.mapper.fn.FnMapper},该接口中的其他方法用 {@link ExampleMapper} 也能实现** @param entity 实体类* @param forceUpdateFields 强制更新的字段,不区分字段是否为 null,通过 {@link Fn#of(Fn...)} 创建 {@link Fn.Fns}* @return 1成功,0失败*/
@Lang(Caching.class)
@UpdateProvider(type = FnProvider.class, method = "updateByPrimaryKeySelectiveWithForceFields")
int updateByPrimaryKeySelectiveWithForceFields(@Param("entity") T entity, @Param("fns") Fn.Fns<T> forceUpdateFields);
这就是完全的复制粘贴,利用这一点,你可以不用 BaseMapper
接口作为自己的基类接口,你可以定义一个自己的接口,复制粘贴自己的需要的通用方法作为基础接口, 例如一个 GuozilanMapper
示例如下:
public interface GuozilanMapper<T> {/*** 保存实体** @param entity 实体类* @return 1成功,0失败*/@Lang(Caching.class)@InsertProvider(type = EntityProvider.class, method = "insert")int insert(T entity);/*** 根据主键查询实体** @param id 主键* @return 实体*/@Lang(Caching.class)@SelectProvider(type = EntityProvider.class, method = "selectByPrimaryKey")Optional<T> selectByPrimaryKey(Long id);
}
只要继承了上面的接口,你就直接拥有了这两个基础方法。
使用这种方式可以自定义一些自己项目需要用到的不同类别的通用接口,例如,如果你有大量实体都没有主键,默认的
BaseMapper<T, I>
就不太适合, 此时你可以自己创建一个NoIdMapper<T>
,把除了主键操作方法外的其他方法(有选择的)都拷过来,就形成了符合自己实际需要的通用 Mapper。
推而广之之后,还有更绝的用法,不继承接口,或者基础接口没有某个方法,直接复制注解过来,不需要自己写 XML:
public interface UserMapper {/*** 保存实体** @param entity 实体类* @return 1成功,0失败*/@Lang(Caching.class)@InsertProvider(type = EntityProvider.class, method = "insert")int insert(User entity);
}
你不需要任何具体的 SQL,上面的 insert 方法就可以直接使用了。
2.1.3 使用 Java8 默认方法灵活实现通用方法
在 BaseMapper
接口中,利用现有的 Example
方法,实现了两个非常常用的通用方法:
/*** 根据指定字段集合查询:field in (fieldValueList)* <p>* 这个方法是个示例,你也可以使用 Java8 的默认方法实现一些通用方法** @param field 字段* @param fieldValueList 字段值集合* @param <F> 字段类型* @return 实体列表*/
default <F> List<T> selectByFieldList(Fn<T, F> field, List<F> fieldValueList) {Example<T> example = new Example<>();example.createCriteria().andIn((Fn<T, Object>) field, fieldValueList);return selectByExample(example);
}/*** 根据指定字段集合删除:field in (fieldValueList)* <p>* 这个方法是个示例,你也可以使用 Java8 的默认方法实现一些通用方法** @param field 字段* @param fieldValueList 字段值集合* @param <F> 字段类型* @return 实体列表*/
default <F> int deleteByFieldList(Fn<T, F> field, List<F> fieldValueList) {Example<T> example = new Example<>();example.createCriteria().andIn((Fn<T, Object>) field, fieldValueList);return deleteByExample(example);
}
这两个方法可以直接根据某个字段值的集合进行批量查询或者删除,用法示例如下:
List<User> users = mapper.selectByFieldList(User::getUserName, Arrays.asList("张无忌", "赵敏", "周芷若"));
mapper.deleteByFieldList(User::getUserName, Arrays.asList("张无忌", "赵敏", "周芷若"));
除了这个例子外,还有一段 EntityMapper
被注释的示例:
/*** 根据实体字段条件分页查询** @param entity 实体类* @param rowBounds 分页信息* @return 实体列表*/
List<T> selectList(T entity, RowBounds rowBounds);/*** 根据查询条件获取第一个结果** @param entity 实体类* @return 实体*/
default Optional<T> selectFirst(T entity) {List<T> entities = selectList(entity, new RowBounds(0, 1));if (entities.size() == 1) {return Optional.of(entities.get(0));}return Optional.empty();
}/*** 根据查询条件获取指定的前几个对象** @param entity 实体类* @param n 指定的个数* @return 实体*/
default List<T> selectTopN(T entity, int n) {return selectList(entity, new RowBounds(0, n));
}
合理的通过 Java8 的默认方法,能够实现海量的通用方法。至于那些是真正需要用到的通用方法,就需要根据自己的需要来选择,因此虽然上面的方法能通用, 但是在缺乏频繁使用场景的情况下,BaseMapper
接口并没有接纳这几个方法。
特别注意
上面示例中List<T> selectList(T entity, RowBounds rowBounds);
没有添加@SelectProvider
注解, 这是因为 MyBatis 中不允许出现相同名称的方法,同时对于RowBounds
参数有特殊处理, 这个方法会直接复用List<T> selectList(T entity);
方法,这个方法已经有了@SelectProvider
注解配置。
2.1.4 重写继承接口的定义
在 EntityMapper
中有 insert
方法定义如下:
/*** 保存实体** @param entity 实体类* @return 1成功,0失败*/
@Lang(Caching.class)
@InsertProvider(type = EntityProvider.class, method = "insert")
int insert(T entity);
这个定义没有处理主键,需要自己设置好主键后调用该方法新增数据。
特别注意 在 2.x 版本之后支持在实体上配置主键策略,因此在实体配置主键策略的情况下,这个方法可以直接使用。 主键策略示例如下:
@Entity.Table("user")
public class User {@Entity.Column(value = "user_id", id = true, useGeneratedKeys = true)private Long userId;
当调用 insert(user)
方法的时候会自动处理主键,而且也可以避免主键名称必须固定为统一名称的问题。
如果我使用的 MySql 自增怎么办?主键null也能直接保存,但是不回写。
如果使用 Oracle 序列怎么办?直接用这个方法是没有办法的。
因为可以 重写继承接口的定义,所以可以支持所有 MyBatis 本身能支持的所有主键方式。
在 Mapper
中,覆盖定义如下:
/*** 保存实体,默认主键自增,并且名称为 id* <p>* 这个方法是个示例,你可以在自己的接口中使用相同的方式覆盖父接口中的配置** @param entity 实体类* @return 1成功,0失败*/
@Override
@Lang(Caching.class)
@Options(useGeneratedKeys = true, keyProperty = "id")
@InsertProvider(type = EntityProvider.class, method = "insert")
int insert(T entity);
首先 @Override
是重写父接口定义,然后和原来相比增加了下面的注解:
@Options(useGeneratedKeys = true, keyProperty = "id")
这个注解对应 xml 中的配置如下:
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
seGeneratedKeys
意思是要用JDBC接口方式取回主键,主键字段对应的属性名为 id
,就是要回写到 id
字段。
上面的配置对 MySQL 这类自增数据库是可行的,如果你自己的主键不叫 id
,甚至如果每个表的主键都不统一(如 {tableName}_id
), 你需要在每个具体实现的接口中重写。例如:
public interface UserMapper extends Mapper<User, Long> {/*** 保存实体,默认主键自增,并且名称为 id* <p>* 这个方法是个示例,你可以在自己的接口中使用相同的方式覆盖父接口中的配置** @param entity 实体类* @return 1成功,0失败*/@Override@Lang(Caching.class)@Options(useGeneratedKeys = true, keyProperty = "userId")@InsertProvider(type = EntityProvider.class, method = "insert")int insert(User entity);}
如果是Oracle序列或者需要执行SQL生成主键或者取回主键时,可以配置 @SelectKey
注解,示例如下:
@Override
@Lang(Caching.class)
@SelectKey(statement = "CALL IDENTITY()", keyProperty = "id", resultType = Long.class, before = false)
@InsertProvider(type = EntityProvider.class, method = "insert")
int insert(User entity);
上面还只是通过增加注解重新定义了接口方法。实际上你还可以更换 @InsertProvider(type = EntityProvider.class, method = "insert")
, 将其中的实现换成其他的也可以,如果对默认的方法和逻辑不满意,就可以改成别的。
通过 重写继承接口的定义,应该能感觉出有多强大,多么灵活。
特别注意 在 2.x 版本之后支持在实体上配置主键策略,这种方式更方便,详情看 3. 实体类注解
2.1.5 通过修改接口方法的返回值和入参,就能变身无数个通用方法
以 EntityProvider
中的 select
方法为例,方法的具体实现如下:
/*** 根据实体字段条件查询唯一的实体,根据实体字段条件批量查询,查询结果的数量由方法定义** @param providerContext 上下文* @return cacheKey*/
public static String select(ProviderContext providerContext) {return SqlScript.caching(providerContext, new SqlScript() {@Overridepublic String getSql(EntityTable entity) {return "SELECT " + entity.baseColumnAsPropertyList()+ " FROM " + entity.table()+ ifParameterNotNull(() ->where(() ->entity.whereColumns().stream().map(column ->ifTest(column.notNullTest(), () -> "AND " + column.columnEqualsProperty())).collect(Collectors.joining(LF))))+ entity.groupByColumn().orElse("")+ entity.havingColumn().orElse("")+ entity.orderByColumn().orElse("");}});
}
最终会生成一个 SELECT .. FROM .. WHERE ...
的 SQL,在 MyBatis 中,SQL 只定义了如何在数据库执行, 执行后的结果和取值方式是通过接口方法定义决定的,因此就这样一个 SELECT 查询,能够实现很多个方法,举例如下:
@Lang(Caching.class)
@SelectProvider(type = EntityProvider.class, method = "select")
Optional<T> selectOne(T entity);@Lang(Caching.class)
@SelectProvider(type = EntityProvider.class, method = "select")
List<T> selectList(T entity);@Lang(Caching.class)
@SelectProvider(type = EntityProvider.class, method = "select")
List<T> selectAll();@Lang(Caching.class)
@SelectProvider(type = EntityProvider.class, method = "select")
Cursor<T> selectCursor(T entity);
利用这一特点,通过修改接口方法的返回值和入参,就能变身无数个通用方法。
如果在加个
RowBounds
分页参数,通用方法直接翻倍。
相关文章:

BaseMapper 接口介绍
基于 mybatis-mapper/provider 核心部分实现的基础的增删改查操作,提供了一个核心的 io.mybatis.mapper.BaseMapper 接口和一个 预定义 的 io.mybatis.mapper.Mapper 接口,BaseMapper 接口定义如下: /*** 基础 Mapper 方法,可以在…...
HAL-Cubemax定时器使用记录
title: HAL-Cubemax定时器使用记录 tags: STM32HalCubemax 文章目录 HAL-Cubemax定时器使用记录分享一种思路1.创建一个ms(毫秒)级延时中断2.创建计数的变量3.在需要延时的函数中对变量阈值进行判断4.验证实例--完整使用记录代码 问题往期内容基础库HAL cubemax VSCODE GCC …...

同时使用磁吸充电器和Lightning时,iPhone充电速度会变快吗?
在智能手机的世界里,续航能力一直是用户关注的焦点。苹果公司以其创新的MagSafe技术和传统的Lightning接口,为iPhone用户提供了多样化的充电解决方案。 然而,当这两种技术同时使用时,它们能否带来更快的充电速度?本文…...

零成本搭建个人图床服务器
前言 图床服务器是一种用于存储和管理图片的服务器,可以给我们提供将图片上传后能外部访问浏览的服务。这样我们在写文章时插入的说明图片,就可以集中放到图床里,既方便多平台文章发布,又能统一管理和备份。 当然下面通过在 Git…...

SpringBoot 搭建sftp服务 实现远程上传和下载文件
maven依赖: <dependency><groupId>com.jcraft</groupId><artifactId>jsch</artifactId><version>0.1.55</version> </dependency>application.yml sftp:protocol: sftphost: port: 22username: rootpassword: sp…...

IDEA中使用leetcode 刷题
目录 1.IDEA下载leetcode插件 2.侧边点开插件 3.打开网页版登录找到cookie复制 4.回到IDEA登录 5.刷题 6.共勉 1.IDEA下载leetcode插件 2.侧边点开插件 3.打开网页版登录找到cookie复制 4.回到IDEA登录 5.刷题 6.共勉 算法题来了不畏惧, 挑战前行是成长的舞台…...

华为海思CPU解读
安全可靠CPU测评结果(华为海思篇) 中国信息安全测评中心于2024年5月20日发布安全可靠测评结果公告(2024年第1号),公布依据《安全可靠测评工作指南(试行)》的测评结果,自发布起有效期…...
中介子方程三十三
XXFXXuXXWXXuXXdXXrXXαXXuXpXXdXXpXuXXαXXrXXdXXuXWXπXXWXeXyXeXbXπXpXXNXXqXeXXrXXαXXuXpXXdXXpXuXXαXXrXXeXqXXNXXpXπXbXeXyXeXWXXπXWXuXXdXXrXXαXXuXpXXdXXpXuXXαXXrXXdXXuXXWXXuXXFXXEXXyXXEXXrXXαXXuXpXXdXXpXuXXαXXrXXEXXyXXαXiXXαXiXrXkXtXyXXpXVXXdXuXWX…...

今年哪两个行业可能有贝塔?
银行和综合板块存在比较明显的行业贝塔,背后原因是:银行板块中,最小的几家银行市值也不小;综合板块中,最大的几家市值也不大。 一、今年哪两个行业可能有贝塔? 我们一直强调今年市场呈现出【行业弱beta、风…...

嵌入式软件开发工具使用介绍
软件开发工具 辅助开发工具 硬件工具与仪器设备 逻辑分析仪使用 串口数据解码分析 示波器使用 1.示波器简介 TBS 1052B(Tektronix)系列数字存储示波器在紧凑的设计中提供了经济的性能。 由于多种标配功能, 包括 USB 连接、34 种自动测量、…...

【TB作品】MSP430G2553,单片机,口袋板, 交通灯控制系统
题8 交通灯控制系统 十字路口交通灯由红、绿两色LED显示器(两位8段LED显示器)组成,LED显示器显示切换倒计时,以秒为单位,每秒更新一次;为确保安全,绿LED计数到0转红,经5秒延时&#…...

windows 安装 Kubernetes(k8s)
windows 安装 docker 详情见: https://blog.csdn.net/sinat_32502451/article/details/133026301 minikube Minikube 是一种轻量级的Kubernetes 实现,可在本地计算机上创建VM 并部署仅包含一个节点的简单集群。 下载地址:https://github.…...

C语言 | Leetcode C语言题解之第189题轮转数组
题目: 题解: void swap(int* a, int* b) {int t *a;*a *b, *b t; }void reverse(int* nums, int start, int end) {while (start < end) {swap(&nums[start], &nums[end]);start 1;end - 1;} }void rotate(int* nums, int numsSize, int…...

【安全审核】音视频审核开通以及计费相关
融云控制台音视频审核入口:音视频审核 1 音视频审核文档:融云开发者文档 1 提示: 开发环境: 免费体验 7 天(含 21 万分钟音频流和 420 万张视频审核用量),免费额度用尽后,将关停服务…...

【实战】Spring Cloud Stream 3.1+整合Kafka
文章目录 前言新版版本优势实战演示增加maven依赖增加applicaiton.yaml配置新增Kafka通道消费者新增发送消息的接口 实战测试postman发送一个正常的消息postman发送异常消息 前言 之前我们已经整合过Spring Cloud Stream 3.0版本与Kafka、RabbitMQ中间件,简直不要太…...
java之可变字符串之append方法
可变字符串如果要添加内容,需要用到append方法 语法格式如下 sbf.append(obj) 其中sbf是任意的可变字符串 obj是任意数据类型的对象 这个方法是将任意数据转换成字符串,然后添加到此序列中 public class Buffer {public static void main(String[]…...

[保姆级教程]uniapp自定义导航栏
文章目录 导文隐藏默认导航栏:全局隐藏当前页面隐藏 添加自定义导航栏视图:手写导航栏组件导航栏 导文 在 UniApp 中,自定义导航栏通常涉及到隐藏默认的导航栏,并在页面顶部添加自定义的视图组件来模拟导航栏的功能。 隐藏默认导航…...

项目训练营第二天
项目训练营第二天 用户登录逻辑 1、账户名不少于4位 2、密码不少于8位 3、数据库表中能够查询到账户、密码 4、密码查询时用同样加密脱敏处理手段处理后再和数据库中取出字段进行对比,如果账户名未查询到,直接返回null 5、后端设置相应的脱敏后用户的s…...

考研数学一有多难?130+背后的残酷真相
考研数学一很难 大家平时在网上上看到很多人说自己考了130,其实这些人只占参加考研数学人数的极少部分,有个数据可以展示出来考研数学到底有多难: 在几百万考研大军中,能考到120分以上的考生只有2%。绝大多数人的分数集中在30到…...
vue2脚手架笔记总结1
1.什么是组件 组件是实现局部代码和功能资源的集合 2.vue.config.js配置文件 使用vue inspect > output.js可以查看到Vue脚手架的默认配置,但是在这里面修改不会影响实际的配置,如果需要修改配置需要使用用vue.config.js文件,详情见:https://cli.vuej…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?
FTP(File Transfer Protocol)本身是一个基于 TCP 的协议,理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况,主要原因包括: ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...
Monorepo架构: Nx Cloud 扩展能力与缓存加速
借助 Nx Cloud 实现项目协同与加速构建 1 ) 缓存工作原理分析 在了解了本地缓存和远程缓存之后,我们来探究缓存是如何工作的。以计算文件的哈希串为例,若后续运行任务时文件哈希串未变,系统会直接使用对应的输出和制品文件。 2 …...
Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解
文章目录 一、开启慢查询日志,定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...