【ORM 框架】MyBatis-Plus 核心特性、条件构造器、分页插件、乐观锁插件(附《思维导图》、《问题排查与实践清单》和《面试高频考点汇总》)

目录

MyBatis-Plus 系统性知识体系总结

一、整体定位与设计理念

1.1 框架定位

MyBatis-Plus(简称 MP)是一款MyBatis 增强工具,在 MyBatis 原生能力基础上遵循"只增强、不改变"的原则,对单表 CRUD、条件构建、分页、并发控制等场景做了通用封装,是国内 Java 生态最主流的 ORM 增强框架之一。

1.2 核心设计原则

  • 无侵入:不修改 MyBatis 原生架构与 SQL 编写模式,原有项目可平滑接入
  • 低损耗:启动阶段完成通用方法注入,运行时无额外性能开销
  • 通用性:覆盖 90% 以上单表操作场景,减少重复代码
  • 可扩展:插件化架构,支持自定义拦截器与功能扩展

二、核心特性体系

2.1 通用 CRUD 层封装

(1)BaseMapper(DAO 层通用接口)

内置 17+ 基础单表 CRUD 方法,无需编写 XML 映射文件即可完成数据操作:

  • 新增:insertinsertBatch
  • 删除:deleteByIddeleteBatchIdsdeleteByMapdelete
  • 修改:updateByIdupdate
  • 查询:selectByIdselectBatchIdsselectByMapselectOneselectListselectCountselectPage
(2)IService(业务层通用接口)

在 BaseMapper 之上封装业务语义,提供更丰富的批量操作与链式调用能力:

  • 批量操作:saveBatchsaveOrUpdatesaveOrUpdateBatch
  • 批量查询:listByIdslistByMaplist
  • 分页查询:pagepageMaps
  • 链式调用:query().eq().list() 流式编程风格

2.2 主键生成策略

通过 @TableId 注解的 type 属性配置,内置多种主键生成方案:

策略类型 说明 适用场景
AUTO 数据库自增主键 MySQL、PostgreSQL 等支持自增的数据库
ASSIGN_ID 雪花算法生成分布式全局唯一 ID(默认策略) 分布式系统、分库分表场景
ASSIGN_UUID 生成无中划线的 32 位 UUID 简单分布式场景
INPUT 用户手动传入主键值 自定义主键生成逻辑
NONE 无状态,跟随全局配置 项目统一策略管控

2.3 公共字段自动填充

通过 MetaObjectHandler 接口实现创建时间、更新时间、操作人等公共字段的自动赋值:

  • 字段标记:@TableField(fill = FieldFill.INSERT)(插入时填充)、@TableField(fill = FieldFill.INSERT_UPDATE)(插入更新时填充)
  • 自定义实现:实现 insertFillupdateFill 方法,统一设置字段值

2.4 逻辑删除

内置逻辑删除能力,无需手动编写删除标记更新逻辑:

  • 全局配置删除/未删除标识值(如 deleted=1 表示删除,0 表示未删除)
  • 实体类字段标注 @TableLogic
  • 框架自动改写查询、删除、更新 SQL,默认过滤已删除数据

2.5 代码生成器

提供一键式代码生成能力,快速生成项目分层代码:

  • 旧版 AutoGenerator:基于 Velocity 模板,配置灵活度高
  • 新版 FastAutoGenerator:更简洁的流式 API,支持自定义模板与生成策略
  • 生成范围:Entity、Mapper、Service、Controller、XML 映射文件

2.6 插件化扩展体系

基于 MyBatis 拦截器机制,提供可插拔的功能插件,核心包括:

  • 分页插件(PaginationInnerInterceptor)
  • 乐观锁插件(OptimisticLockerInnerInterceptor)
  • 多租户插件(TenantLineInnerInterceptor)
  • 动态表名插件(DynamicTableNameInnerInterceptor)
  • 性能分析插件(PerformanceInterceptor)

三、条件构造器(Wrapper)体系

3.1 类层级与核心实现

Wrapper 是 MP 动态 SQL 构建的顶层抽象,通过链式 API 拼接 WHERE 条件,核心类继承关系:

复制代码
Wrapper(顶层接口)
└── AbstractWrapper(抽象基类,实现条件拼接核心逻辑)
    ├── QueryWrapper:查询条件构造器,支持指定查询字段
    │   └── LambdaQueryWrapper:Lambda 形式查询构造器
    ├── UpdateWrapper:更新条件构造器,支持 SET 字段赋值
    │   └── LambdaUpdateWrapper:Lambda 形式更新构造器
    └── StringLambdaQueryWrapper:字符串形式 Lambda 构造器

3.2 核心使用方式

(1)QueryWrapper(查询场景)

用于构建 SELECT 语句的 WHERE 条件,支持指定返回字段:

复制代码
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("id", "username")
       .eq("status", 1)
       .like("name", "张")
       .orderByDesc("create_time");
List<User> list = userMapper.selectList(wrapper);
(2)UpdateWrapper(更新场景)

用于构建 UPDATE 语句的 SET 赋值与 WHERE 条件:

复制代码
UpdateWrapper<User> wrapper = new UpdateWrapper<>();
wrapper.set("status", 0)
       .eq("age", 18);
userMapper.update(null, wrapper);
(3)Lambda 构造器(生产推荐)

通过 Lambda 表达式引用实体类字段,编译期校验字段合法性,彻底避免硬编码字段名导致的运行时错误:

复制代码
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getStatus, 1)
       .likeRight(User::getName, "李")
       .ge(User::getAge, 20);

3.3 常用条件方法

基础比较类
  • eq / ne:等于 / 不等于
  • gt / ge:大于 / 大于等于
  • lt / le:小于 / 小于等于
  • between / notBetween:区间范围查询
模糊与包含类
  • like / notLike:全模糊匹配
  • likeLeft / likeRight:左模糊 / 右模糊匹配
  • in / notIn:集合包含 / 不包含
  • inSql / notInSql:子查询形式 IN 条件
空值与排序类
  • isNull / isNotNull:字段为空 / 不为空
  • orderByAsc / orderByDesc:升序 / 降序排列
  • orderBy:自定义排序规则
逻辑与嵌套类
  • and / or:逻辑与 / 逻辑或拼接
  • nested:嵌套条件,生成括号包裹的子条件
  • groupBy / having:分组与分组后筛选
自定义 SQL 扩展
  • apply:拼接自定义 SQL 片段
  • last:在 SQL 末尾追加自定义内容(如 limit 1
  • exists / notExists:EXISTS 子查询

3.4 核心优势

  1. 动态 SQL 零配置 :无需在 XML 中编写 <if> 标签,Java 代码中灵活拼接条件
  2. 类型安全:Lambda 构造器编译期检查字段,提前发现错误
  3. 链式编程:代码简洁易读,条件逻辑清晰
  4. 条件复用:可封装通用条件,适配多场景查询

四、分页插件(PaginationInnerInterceptor)

4.1 插件定位与版本演进

MP 官方提供的物理分页插件,是最核心的插件之一,基于 MyBatis 拦截器机制实现:

  • 3.4.0 之前 :独立 PaginationInterceptor 拦截器
  • 3.4.0 及之后 :统一纳入 MybatisPlusInterceptor 核心拦截器,以 PaginationInnerInterceptor 内部拦截器形式注册

4.2 配置方式(Spring Boot)

复制代码
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加分页拦截器,指定数据库方言
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

4.3 基础使用方式

(1)Mapper 层分页
复制代码
// 第1页,每页10条数据
Page<User> page = new Page<>(1, 10);
Page<User> result = userMapper.selectPage(page, queryWrapper);

// 分页结果字段
long total = result.getTotal();      // 总记录数
long pages = result.getPages();      // 总页数
List<User> records = result.getRecords(); // 当前页数据
long current = result.getCurrent();  // 当前页码
long size = result.getSize();        // 每页条数
(2)Service 层分页
复制代码
Page<User> page = new Page<>(1, 10);
IPage<User> result = userService.page(page, queryWrapper);

4.4 分页实现原理

  1. SQL 拦截 :拦截 Executor 的 query 方法,识别分页参数对象
  2. 方言适配 :根据配置的数据库类型,生成对应语法的分页 SQL
    • MySQL:拼接 LIMIT offset, size
    • Oracle:通过 ROWNUM 嵌套实现分页
    • PostgreSQL:拼接 LIMIT size OFFSET offset
  3. Count 查询:自动生成并执行 count 统计 SQL,获取总记录数
  4. 结果封装 :将分页数据与总条数封装为 IPage 对象返回

4.5 高级特性

  1. 溢出处理 :设置 page.setOverflow(true),当当前页大于总页数时,自动返回最后一页数据
  2. 单页条数限制 :配置 maxLimit 参数,限制单页最大查询条数,防止恶意拉取全量数据
  3. 自定义 Count SQL:复杂多表查询可自定义 count 语句,优化分页性能
  4. 多方言支持:内置 20+ 数据库方言,覆盖主流关系型数据库
  5. Count 优化:自动移除不必要的排序与字段,简化 count 语句

4.6 注意事项

  • 必须指定正确的 DbType 方言,否则生成分页 SQL 语法错误
  • 多数据源场景下,需为每个数据源单独配置对应方言
  • 复杂 SQL(含子查询、多表复杂关联)自动生成的 count 可能不准确,建议自定义 count
  • 插件执行顺序:分页插件需在多租户、动态表名等插件之后注册

五、乐观锁插件(OptimisticLockerInnerInterceptor)

5.1 设计思想与适用场景

(1)核心原理

基于版本号机制实现乐观锁:读取数据时获取当前版本号,更新时将版本号作为 WHERE 条件,若版本号不匹配则更新失败;更新成功则版本号自增 1。

(2)适用场景
  • 读多写少、并发冲突概率低的业务(如用户信息更新、订单状态变更)
  • 对性能要求高,不希望悲观锁阻塞的场景
  • 分布式环境下的轻量级并发控制

5.2 配置与使用步骤

(1)注册插件
复制代码
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    // 注册乐观锁插件
    interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    return interceptor;
}
(2)实体类添加版本字段
复制代码
@Data
public class User {
    private Long id;
    private String name;
    // 版本号字段,标注 @Version 注解
    @Version
    private Integer version;
}
(3)业务代码使用

直接调用更新方法,插件自动处理版本号逻辑:

复制代码
User user = userMapper.selectById(1L);
user.setName("新名称");
// 更新时自动携带 version 条件,成功后 version 自动 +1
int rows = userMapper.updateById(user);

// rows = 0 表示更新失败(数据已被其他线程修改)
if (rows == 0) {
    // 处理并发冲突:重试、抛出业务异常等
}

5.3 实现机制

  1. 拦截 update 语句,在 WHERE 条件中追加 version = 当前版本号
  2. 在 SET 语句中追加 version = version + 1
  3. 执行更新后,若影响行数为 0,说明数据已被修改,更新失败
  4. 更新成功时,自动同步实体类中的 version 字段为最新值

5.4 支持的数据类型

  • 数值型:intIntegerlongLong(推荐使用,性能最佳)
  • 时间型:DateTimestampLocalDateTime

5.5 限制与注意事项

  1. 支持方法有限 :仅对 updateByIdupdate(entity, wrapper) 方法生效
  2. 单版本字段 :一个实体类只能有一个 @Version 字段
  3. 不支持复合主键:复合主键场景下无法正常工作
  4. 无异常抛出:更新失败不会抛出异常,需通过返回的影响行数判断结果
  5. 初始值要求:插入数据时版本字段必须有初始值(如 Integer 默认 0)
  6. Wrapper 不可复用:使用 UpdateWrapper 时,不能复用同一个 wrapper 对象多次更新

六、插件底层架构总览

6.1 基础依赖:MyBatis 拦截器

MP 插件体系基于 MyBatis 的 Interceptor 接口,通过 JDK 动态代理拦截 Executor、StatementHandler 等核心对象的方法,实现 SQL 改写与逻辑增强。

6.2 MP 分层插件架构

3.4.0+ 版本采用核心拦截器 + 内部拦截器的分层设计:

  • MybatisPlusInterceptor:对外的核心拦截器,负责方法拦截与调度
  • InnerInterceptor:内部功能拦截器接口,分页、乐观锁等均为其实现类
  • 架构优势:插件可灵活组合、执行顺序可控,避免多插件重复代理

6.3 推荐插件执行顺序

多个内部拦截器按注册顺序执行,推荐优先级:

  1. 动态表名插件
  2. 多租户插件
  3. 分页插件
  4. 乐观锁插件

《常见问题排查与最佳实践清单》

1.1 高频踩坑问题排查

(1)分页插件不生效,查询返回全量数据
  • 常见原因
    • 未注册 MybatisPlusInterceptor 核心拦截器,或未添加 PaginationInnerInterceptor 内部拦截器
    • 未指定数据库方言 DbType,或方言与实际数据库不匹配
    • 分页参数 Page 不是 Mapper 方法的第一个参数
    • 自定义 SQL 方法名不符合拦截规则,或方法返回值不是 IPage 类型
    • 多数据源场景下,仅给单个数据源配置了插件
  • 解决方案
    • 确保拦截器注入 Spring 容器,显式指定 DbType(如 DbType.MYSQL
    • 分页方法统一将 IPage 作为第一个入参,返回值为 IPage<T>
    • 多数据源下,每个 SqlSessionFactory 单独加载分页插件
(2)乐观锁插件失效,更新不携带版本号条件
  • 常见原因
    • 实体类版本字段未标注 @Version 注解
    • 版本字段类型不支持(仅支持数值型、时间型)
    • 使用 update(wrapper) 重载方法时未传入 entity 对象
    • 复合主键场景下插件无法正常工作
    • 复用同一个 UpdateWrapper 对象执行多次更新
  • 解决方案
    • 数值型字段加 @Version,插入数据时给版本号赋初始值(如 0
    • 优先使用 updateById 方法;使用 update(entity, wrapper) 时必须传入实体对象
    • 每次更新都新建 UpdateWrapper,禁止复用
(3)Lambda 构造器字段映射错误
  • 常见原因
    • 未开启驼峰下划线转换,或实体字段与数据库列名不匹配
    • 字段未加 @TableField 显式指定列名,且命名规则不符合默认转换
    • 使用了数据库关键字作为字段名
  • 解决方案
    • 全局配置 map-underscore-to-camel-case: true(默认开启)
    • 特殊字段通过 @TableField("column_name") 手动映射
    • 关键字段添加反引号转义,如 @TableField("order")
(4)逻辑删除不生效,查询仍返回已删除数据
  • 常见原因
    • 未全局配置逻辑删除标识值,或字段未加 @TableLogic 注解
    • 自定义 XML SQL 不会被插件自动改写
    • 联合唯一索引未包含删除标识字段,导致唯一约束冲突
  • 解决方案
    • 全局配置 logic-delete-valuelogic-not-delete-value
    • 自定义 SQL 需手动拼接删除条件,或使用 MP 提供的条件构造器
    • 逻辑删除表的唯一索引需设计为「业务字段 + 删除标识」联合唯一
(5)公共字段自动填充不生效
  • 常见原因
    • 实现类未加 @Component 注解,未被 Spring 容器管理
    • 实体字段未标注 @TableField(fill = FieldFill.INSERT) 等填充策略
    • 填充字段类型与赋值类型不匹配
    • 主键字段无法通过自动填充赋值
  • 解决方案
    • 实现 MetaObjectHandler 接口并注入 Spring 容器
    • 严格匹配字段类型,日期类统一用 LocalDateTime
    • 主键赋值使用主键生成策略,不使用自动填充
(6)批量插入性能差
  • 常见原因
    • saveBatch 默认是逐条执行 INSERT,并非真正的批量 SQL
    • 未开启 JDBC 批量执行参数
  • 解决方案
    • JDBC 连接地址添加 rewriteBatchedStatements=true 参数
    • 大数据量插入时分批提交(如每 1000 条提交一次),避免内存溢出

1.2 工程最佳实践

编码规范
  • 优先使用 Lambda 构造器:避免硬编码字段名,编译期校验字段合法性,降低运行时风险
  • 条件复用封装:通用查询条件封装为独立方法,避免重复拼接 Wrapper
  • 分层职责清晰:Controller 层不直接操作 Mapper,业务逻辑收敛在 Service 层
  • 实体与 DTO 分离:数据库实体不直接作为前端入参/出参,通过 VO/DTO 做数据隔离
性能优化
  • 按需查询字段 :使用 select() 指定返回字段,禁止 select *,减少数据传输与内存占用
  • 分页 Count 优化 :复杂多表分页禁用自动 count,通过 setCountSql 自定义 count 语句
  • 限制单页条数 :全局配置 maxLimit(如 1000),防止恶意请求拉取全量数据拖垮数据库
  • 复杂查询手写 SQL:多表关联、子查询等复杂场景,直接在 XML 中编写原生 SQL,不要强行用 Wrapper 拼接
插件配置
  • 显式指定方言 :不要依赖自动识别方言,生产环境明确配置 DbType,避免兼容性问题
  • 控制插件顺序:多个内部拦截器按「动态表名 → 多租户 → 分页 → 乐观锁」顺序注册
  • 按需启用插件:乐观锁、多租户等插件仅在对应业务表使用,不要全项目全局开启
  • 溢出处理开启 :分页对象设置 overflow = true,页码超出总页数时自动返回最后一页
业务落地
  • 主键策略 :分布式系统统一使用 ASSIGN_ID 雪花算法主键,避免自增主键的分库分表扩展问题
  • 乐观锁重试:乐观锁更新失败时,根据业务场景实现重试机制(如重试 3 次),而非直接报错
  • 逻辑删除配合归档:定期归档已删除的历史数据,避免逻辑删除表数据量过大影响查询性能
  • 自动填充统一规范:创建时间、更新时间、创建人、更新人四个公共字段统一自动填充,业务代码不手动赋值

《面试高频考点汇总》

2.1 基础认知篇

  1. MyBatis-Plus 和 MyBatis、JPA(Hibernate)的区别?
    • MyBatis:原生持久层框架,SQL 完全手写,灵活度高但单表操作重复代码多
    • MyBatis-Plus:MyBatis 的增强工具,遵循「只增强不改变」原则,封装单表 CRUD、条件构造、分页等通用能力,保留原生 SQL 灵活性,属于半 ORM 框架
    • JPA:全自动化 ORM 规范,SQL 对开发者透明,开发效率高但复杂 SQL 优化难度大,适合简单业务场景
  2. MyBatis-Plus 的核心组件有哪些?
    • 基础层:BaseMapper(DAO 层通用 CRUD)、IService(业务层通用封装)
    • 条件层:Wrapper 条件构造器体系(Query/Update/Lambda 等实现)
    • 插件层:分页、乐观锁、多租户、动态表名等内置插件
    • 辅助能力:主键生成策略、自动填充、逻辑删除、代码生成器
  3. BaseMapper 和 IService 的区别与联系?
    • 定位不同:BaseMapper 是 DAO 层接口,面向数据库操作;IService 是业务层接口,面向业务逻辑
    • 能力不同:BaseMapper 提供基础单表 CRUD;IService 在其之上封装批量操作、链式查询、saveOrUpdate 等业务语义方法
    • 关系:ServiceImpl 实现类中注入 BaseMapper,通过组合方式调用 Mapper 能力

2.2 核心功能篇

  1. 条件构造器有哪些实现类?各自适用场景?
    • QueryWrapper:查询场景,支持指定返回字段,硬编码数据库列名
    • UpdateWrapper:更新场景,支持 set 语句赋值,硬编码列名
    • LambdaQueryWrapper / LambdaUpdateWrapper:生产推荐,通过 Lambda 引用实体字段,编译期校验,避免字段名拼写错误
    • StringLambdaQueryWrapper:支持字符串形式的字段名自动转 Lambda,适用于动态字段场景
  2. 主键生成策略有哪几种?默认策略是什么?
    • AUTO:数据库自增主键
    • ASSIGN_ID默认策略,雪花算法生成分布式全局唯一 ID
    • ASSIGN_UUID:生成 32 位无横线 UUID
    • INPUT:用户手动传入主键值
    • NONE:无状态,跟随全局配置
  3. 雪花算法的结构与优缺点?
    • 结构:1 位符号位 + 41 位时间戳 + 10 位工作机器 ID + 12 位序列号
    • 优点:全局唯一、趋势递增、生成性能极高、无第三方依赖
    • 缺点:依赖系统时钟,时钟回拨会导致生成重复 ID;机器 ID 分配不当会导致冲突
  4. 逻辑删除的实现原理?
    框架通过拦截器改写 SQL:
    • DELETE 语句改写为 UPDATE 语句,设置删除标识字段为已删除值
    • 查询、更新语句自动拼接 WHERE 删除标识 = 未删除值 条件,过滤已删除数据
    • 对业务代码完全透明,开发者无需手动处理删除标识

2.3 原理机制篇

  1. 分页插件的实现原理?
    • 基于 MyBatis Interceptor 机制,拦截 Executor.query 方法
    • 识别方法入参中的 IPage 对象,判断是否为分页查询
    • 根据配置的数据库方言,生成对应语法的分页 SQL(如 MySQL 的 LIMIT、Oracle 的 ROWNUM
    • 自动生成并执行 COUNT 查询,获取总记录数
    • 将查询结果与总条数封装为 IPage 对象返回
  2. 乐观锁插件的实现原理?
    • 拦截 update 语句,在 WHERE 条件中追加 version = 当前版本号
    • SET 语句中追加 version = version + 1
    • 执行 SQL 后,若影响行数为 0,说明数据已被其他线程修改,更新失败
    • 更新成功时,自动同步实体对象中的版本号为最新值
  3. 3.4.0 版本之后的插件架构为什么改成「核心拦截器+内部拦截器」?
  • 旧版每个功能都是独立的 MyBatis 拦截器,会对同一个方法多次代理,性能损耗大
  • 新版外层只有一个 MybatisPlusInterceptor 核心拦截器,内部通过 InnerInterceptor 接口扩展功能
  • 优势:统一调度、执行顺序可控、避免重复代理、插件组合更灵活
  1. BaseMapper 中的通用方法是如何执行的?
  • 项目启动时,扫描所有 BaseMapper 的子接口
  • 解析实体类的注解信息(表名、字段、主键等),为每个通用方法生成对应的 SQL 语句
  • 将生成的 SQL 封装为 MappedStatement 注册到 MyBatis 容器中
  • 执行时和手写 SQL 一样,走 MyBatis 原生执行流程

2.4 实战踩坑篇

  1. 分页插件不生效的排查思路?
    按优先级依次检查:
  2. 是否注册了 MybatisPlusInterceptor 并添加了分页内部拦截器
  3. 是否正确指定了数据库方言 DbType
  4. 分页参数 Page 是否为方法第一个参数
  5. 方法返回值是否为 IPage 类型
  6. 多数据源下是否每个数据源都配置了插件
  7. MyBatis-Plus 的局限性有哪些?
  • 单表操作能力强,多表复杂关联查询仍需手写 XML SQL
  • 分页插件对复杂子查询、多表关联的自动 count 语句可能不准确
  • 乐观锁仅支持 updateByIdupdate(entity, wrapper) 方法,场景有限
  • 默认批量插入并非真正的批量 SQL,需要额外配置 JDBC 参数
  • 逻辑删除与唯一索引天然冲突,需要额外设计联合唯一键
  1. 如何优化 MP 的批量插入性能?
  • JDBC 连接串添加 rewriteBatchedStatements=true,驱动会将多条 INSERT 合并为批量执行
  • 使用 IService.saveBatch 方法,设置合理的批次大小(默认 1000)
  • 超大数据量分批提交,避免单次事务过大导致数据库压力过高
相关推荐
clorinda12 分钟前
SQL 快速入门:题目单知识点精炼总结
java·数据库·sql
AI人工智能+电脑小能手22 分钟前
大白话说Java设计模式-40-备忘录模式(业务实战篇)
java·设计模式·事务回滚·备忘录模式·状态保存·spring transactional·购物车快照
SimonKing23 分钟前
白嫖国产多模态大模型:商汤 SenseNova 接入指南
java·后端·程序员
小江的记录本23 分钟前
【ORM框架】MyBatis核心原理、ORM思想、MyBatis vs JPA
java·数据库·后端·spring·spring cloud·oracle·mybatis
CODER030423 分钟前
win11系统编译安装cuda版llama-cpp-python(踩完所有的坑)
开发语言·python·llama
严谨的麻辣烫30 分钟前
批量静态 IP 如何管理?用 Python 建立一个简单的 IP 资源监控方案
运维·服务器·网络·python·tcp/ip
水饺编程31 分钟前
第5章,[Win32 章节] :绘制填充区域
c语言·c++·windows·visual studio
_明月34 分钟前
汇川技术股份有限公司面试--Java外包岗
java·面试·职场和发展
卷无止境35 分钟前
FastAPI生产环境密钥管理全解析,从一个.env文件说起
后端·python·fastapi