MyBatis-Plus必知必会:告别低效CRUD,高效开发持久层

本文同步更新于微信公众号:

mp.weixin.qq.com/s/j8__DcSTy...


theme: awesome-green

前言

日常Java后台开发中,绝大多数业务都是单表增删改查。原生MyBatis虽然灵活,但存在大量重复劳动:每张表都要手写insert、update、select、delete的XML语句,动态条件拼接需要手动处理字符串判空,分页、时间戳、软删除等通用能力需要自己封装工具类,开发效率低且重复代码难以维护。

本文带你从零入门MyBatis-Plus,理清核心能力与基础用法,适合新手快速上手。

一、背景:MyBatis-Plus 解决了什么问题

1. MyBatis 的痛点

  1. 单表CRUD必须手动编写XML/注解SQL,实体越多,重复代码越多;

  2. 动态查询条件需要手动拼接字符串,极易出现SQL注入、括号逻辑错乱;

  3. 分页、逻辑删除、创建更新时间、并发乐观锁、分布式主键等通用功能无内置实现,每个项目重复封装;

  4. 实体与数据库字段映射、多条件查询样板代码冗余。

2. MyBatis-Plus(简称MP)定位

MP 是**「MyBatis 的增强工具,只增强、不修改」**,完全兼容原生MyBatis所有能力,底层依旧依赖MyBatis执行SQL。

核心目标:「消除单表CRUD冗余代码,内置企业级通用持久层能力,大幅提升后台开发效率」。

3. 核心优势

  • 无侵入:原有MyBatis XML、自定义SQL完全保留,可平滑迁移;

  • 开箱即用:CRUD、分页、条件构造器无需手写SQL;

  • 内置通用语法糖:逻辑删除、自动填充、乐观锁、分布式主键;

  • Lambda语法构造条件,编译校验字段,杜绝字符串硬编码错误。

适用场景:管理后台、CRUD为主业务、中小型服务;复杂多表联查、统计报表依旧可以手写原生MyBatis XML。

二、MyBatis-Plus 核心增强功能

2.1 强大CRUD封装:BaseMapper + IService 双层持久层

MP 提供两套分层CRUD封装,分别对应DAO层与Service层,职责清晰。

1)DAO层:BaseMapper(Mapper接口继承)

所有Mapper接口继承BaseMapper<T>,无需编写任何方法,自动拥有单表基础操作能力,直接操作数据库。

java 复制代码
// Mapper接口,无需自定义CRUD方法
@Mapper
public interface UserMapper extends BaseMapper<User> {
}

常用内置方法:

  • insert(T entity):新增单条数据;

  • selectById(id)、deleteById(id)、updateById(entity):根据主键查询/删除/更新;

  • selectList(Wrapper)、delete(Wrapper):按条件批量查询、批量删除;

  • selectPage(Page, Wrapper):分页查询。

  • 「自定义SQL方法」:开发者可在Mapper接口中自定义任意抽象方法,配合XML/注解编写专属SQL,和BaseMapper自带方法共存使用。

2)Service层:IService + ServiceImpl(业务层封装)

业务层做二次封装,在BaseMapper基础上新增批量操作、链式查询、语义化方法,规范业务代码分层。

  • 接口:继承IService<T>;

  • 实现类:继承ServiceImpl<M,T>(内置注入Mapper,封装通用逻辑)。

java 复制代码
// Service接口
public interface UserService extends IService<User> {
}

// Service实现类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

IService独有增强能力:

  1. 批量操作:saveBatch批量新增、updateBatchById批量更新;

  2. 链式Lambda查询:lambdaQuery().eq().list()极简写法;

  3. 语义化方法:getById、list、page,区分DAO层底层方法,业务可读性更强。

  4. 「主键批量操作(高频实用)」:IService 内置开箱即用批量主键操作方法,无需手动拼接 IN 条件,底层优化批量SQL性能:

  • listByIds(Collection<ID> idList):根据主键批量查询多条数据
  • removeByIds(Collection<ID> idList):根据主键批量删除数据
  • updateBatchById(Collection<T> entityList):批量更新多条数据
  • saveBatch(Collection<T> entityList):批量新增数据
ini 复制代码
// 批量查询
List<Long> idList = Arrays.asList(1L,2L,3L);
List<User> userList = userService.listByIds(idList);

// 批量删除
userService.removeByIds(idList);

两者区别总结

类 层级 核心特点 使用场景
BaseMapper Mapper/DAO 底层原生数据库操作,无批量封装 简单单条操作、自定义Mapper内部调用
IService Service业务层 封装批量、链式查询、事务友好 项目标准分层开发,Controller直接调用

2.2 条件构造器 Wrapper:动态拼接Where条件(重点完整版)

Wrapper 是 MyBatis-Plus 最核心的语法糖,「彻底解决原生MyBatis动态SQL繁琐拼接问题」。无需写XML、无需拼接字符串、无需判空,链式调用自动生成安全的 WHERE 条件,同时杜绝 SQL 注入风险。

1)四大构造器介绍(项目固定选型)

Wrapper 分为查询、更新两大体系,「开发一律优先 Lambda 版本」,编译校验不报错、重构不翻车。

  • 「QueryWrapper」:查询/删除,字段写字符串(硬编码,不推荐)
  • 「LambdaQueryWrapper」:查询/删除,Lambda方法引用(企业首选)
  • 「UpdateWrapper」:更新条件,字符串字段(不推荐)
  • 「LambdaUpdateWrapper」:更新条件,Lambda安全写法(企业首选)
ini 复制代码
// 快速创建方式(推荐)
LambdaQueryWrapper<User> lqw = Wrappers.lambdaQuery();
LambdaUpdateWrapper<User> luw = Wrappers.lambdaUpdate();

2)常用条件语法全覆盖

下面是开发 「99% 场景」 会用到的条件,整理成可直接复制的模板。

① 等值条件(eq / ne)
  • eq:等于 =

  • ne:不等于 <>

perl 复制代码
// name = "张三" AND age != 18
lqw.eq(User::getName, "张三")
   .ne(User::getAge, 18);
② 区间大小条件(gt / ge / lt / le / between)
  • gt:大于 >

  • ge:大于等于 >=

  • lt:小于 <

  • le:小于等于 <=

  • between:区间范围

less 复制代码
// age > 18 AND age <= 60
lqw.gt(User::getAge, 18)
   .le(User::getAge, 60);

// age BETWEEN 18 AND 30
lqw.between(User::getAge, 18, 30);
③ 模糊查询(like 系列)
  • like:前后模糊 %值%

  • likeRight:右模糊 值%

  • likeLeft:左模糊 %值

less 复制代码
// 姓名包含"李"
lqw.like(User::getName, "李");
// 手机号以 138 开头
lqw.likeRight(User::getPhone, "138");
④ 空值判断(isNull / isNotNull)
less 复制代码
// 邮箱为 NULL
lqw.isNull(User::getEmail);
// 手机号不为空
lqw.isNotNull(User::getPhone);
⑤ 包含集合(in / notIn)
less 复制代码
// 部门ID在 1、2、3 中
lqw.in(User::getDeptId, Arrays.asList(1L,2L,3L));

3)逻辑关系:AND / OR 与括号嵌套(重点难点)

「默认规则」 :链式连续调用默认是AND 关系。

「普通OR」:直接拼接 or()

perl 复制代码
// name=张三 OR age>18
lqw.eq(User::getName, "张三")
   .or()
   .gt(User::getAge, 18);

「带括号复杂逻辑(必须用lambda子表达式)」

场景:name = 张三 AND (age > 18 OR deptId = 2)

perl 复制代码
lqw.eq(User::getName, "张三")
   .and(w -> w.gt(User::getAge, 18).or().eq(User::getDeptId, 2));

场景:name = 张三 OR (age > 18 AND deptId = 2)

perl 复制代码
lqw.eq(User::getName, "张三")
   .or(w -> w.gt(User::getAge, 18).eq(User::getDeptId, 2));

💡 核心要点:「只要需要括号,必须使用 and(λ) / or(λ) 嵌套」,否则SQL优先级错乱。

4)condition 动态条件(开发最常用、消除大量if)

Wrapper 所有条件方法**「第一个参数为 boolean condition」**:

  • 「condition = true」:拼接该SQL条件

  • 「condition = false」:直接忽略该条件,不拼接

专门用于:前端传参、非必填查询条件,「彻底消灭 if 判断嵌套」。

ini 复制代码
// 前端参数
String name = "";
Integer minAge = 18;

// 有姓名才模糊查,有最小年龄才判断
lqw.like(StringUtils.hasText(name), User::getName, name)
   .ge(minAge != null, User::getAge, minAge);

等价传统写法,但代码精简 80%:

scss 复制代码
if (StringUtils.hasText(name)) {
    lqw.like(User::getName, name);
}
if (minAge != null) {
    lqw.ge(User::getAge, minAge);
}

5、字段查询过滤(指定返回字段)

Wrapper 支持 select() 方法自定义查询字段,只返回需要的列,避免查询全字段、查询大文本字段,优化SQL性能、减少返回数据体积,是日常优化高频用法。只支持 Lambda 安全写法,杜绝字段硬编码。

ini 复制代码
// 只查询 id、姓名、手机号,忽略其他所有字段
LambdaQueryWrapper<User> lqw = Wrappers.lambdaQuery();
lqw.select(User::getId, User::getName, User::getPhone)
   .eq(User::getDeleted, 0);

List<User> userList = userMapper.selectList(lqw);

适用场景:列表页展示、分页查询、接口返回精简数据,过滤冗余大字段(头像、备注、大文本)。

6)更新构造器与核心注意事项

更新构造器(LambdaUpdateWrapper)用于构建动态更新条件,实现安全的局部字段更新,是业务开发中替代实体全量更新的主流写法。

1、基础使用示例

通过链式set指定需要更新的字段,搭配where条件精准更新数据,仅修改声明的字段,其余数据库字段保留原值。

ini 复制代码
// 将部门ID=1的所有用户,年龄统一更新为25
LambdaUpdateWrapper<User> luw = Wrappers.lambdaUpdate();
// 仅设置需要更新的字段
luw.set(User::getAge, 25)
   .eq(User::getDeptId, 1L);
// 第一个参数传null:表示不使用实体对象赋值,全部由Wrapper控制更新逻辑
userMapper.update(null, luw);
2、开发核心注意事项(两大高频坑点)

「注意事项1:普通set() 存在空值覆盖数据风险」

原生无参set()无自动判空逻辑,若传入null、空字符串,会强制覆盖数据库原有字段值。前端局部编辑、动态传参场景极易造成数据清空。解决方案:使用**「带条件set(condition)**「自定义非空判断,或直接使用」**setIfPresent」**自动忽略null值,规避空值覆盖问题。

「注意事项2:updateById 实体更新存在全量覆盖坑」

updateById属于**「全量字段覆盖更新」,原理是根据实体主键匹配数据,实体中所有null字段会直接清空数据库对应原值。仅适合完整实体保存、全字段修改场景,「绝对不适合局部编辑」**。

「核心选型总结」:日常局部动态更新,优先使用 Wrapper 链式更新(增量更新、安全不丢数据);避免直接使用 updateById 处理部分字段修改。

2.3 分页功能:开箱即用物理分页

MP提供内置分页拦截器,无需手写limit,自动适配MySQL、Oracle等多数据库,纯物理分页,性能稳定。

1. 前置配置(必须注册分页插件)

java 复制代码
@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mpInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // MySQL分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

2. Mapper层分页使用

ini 复制代码
// 参数1:当前页,参数2:每页条数
Page<User> page = new Page<>(1, 10);
LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery().eq(User::getDeleted,0);
Page<User> pageResult = userMapper.selectPage(page, wrapper);

// 分页结果获取
List<User> dataList = pageResult.getRecords(); // 当前页数据
long total = pageResult.getTotal(); // 总条数
long pages = pageResult.getPages(); // 总页数

3. Service层极简分页(推荐业务使用)

perl 复制代码
// 链式lambda分页
Page<User> pageData = userService.lambdaQuery()
        .eq(User::getAge, 18)
        .page(new Page<>(1,10));

4. 自定义SQL分页(多表联查/复杂统计 必备)

上述分页方式仅适用于 MP 链式单表查询,实际开发中,「多表联查、子查询、复杂统计、自定义条件SQL」 无法使用 Wrapper,需要手写 XML 自定义SQL。

MP 分页插件完美支持**「自定义SQL分页」**,无需手动写 count 总数、无需拼接 limit,只需遵循固定格式,插件自动 「拦截SQL、自动分页、自动统计总条数」。

核心规则:「Mapper方法第一个参数必须是 Page 对象,返回值必须是 Page 对象」。

第一步:Mapper 接口定义方法
less 复制代码
// 自定义多表分页查询
Page<UserVO> selectUserPage(@Param("page") Page<UserVO> page,
                            @Param("name") String name);
第二步:XML 手写自定义SQL(无需写limit、无需写count)
sql 复制代码
<select id="selectUserPage" resultType="com.xxx.vo.UserVO">
    SELECT u.id, u.name, u.phone, d.dept_name
    FROM sys_user u
    LEFT JOIN sys_dept d ON u.dept_id = d.id
    <where>
        <if test="name != null and name != ''">
            AND u.name LIKE CONCAT('%',#{name},'%')
        </if>
        AND u.deleted = 0
    </where>
</select>
第三步:业务层调用
ini 复制代码
// 构建分页参数
Page<UserVO> page = new Page<>(1,10);
// 调用自定义分页方法
Page<UserVO> resultPage = userMapper.selectUserPage(page, "张三");

// 获取分页数据
List<UserVO> records = resultPage.getRecords();
long total = resultPage.getTotal();
关键说明
  • 「无需手动分页」:MP插件自动拦截SQL,拼接 LIMIT 分页语句
  • 「无需手动COUNT」:插件自动生成 count 查询统计总条数
  • 「支持返回VO」:自定义分页可以返回自定义视图对象,不限于实体类
  • 「适配所有复杂SQL」:多表联查、分组统计、子查询全部支持

5. 分页扩展特性(性能&容错优化)

  1. 「关闭Count统计(大数据分页性能优化)」 :对于海量数据列表、无需展示总条数/总页数的导出、滚动加载场景,可手动关闭count总数查询,避免全表统计耗时,大幅提升分页性能。构造Page对象时传入第三个参数 false 即可:new Page<>(1,10,false)。

  2. 「分页溢出容错」 :开启插件溢出适配,当请求页码超过最大页数时,自动跳转到最后一页,避免返回空数据,配置:setOverflow(true)。

  3. 「兼容自定义复杂SQL分页」:手写XML多表联查、子查询、分组统计等复杂SQL,可直接传入Page对象,MP分页插件自动拦截SQL、自动处理分页Limit与总数统计,无需手动编写分页逻辑。

2.4 其他实用语法糖(企业开发标配 + 完整配置)

1)逻辑删除(软删除)

不物理删除数据,通过deleted字段标记删除状态,适配数据溯源、恢复场景。

  1. yml全局配置
arduino 复制代码
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted
      logic-not-delete-value: 0 # 未删除
      logic-delete-value: 1     # 已删除
  1. 实体增加标记字段
java 复制代码
@TableLogic
private Integer deleted;

生效原理:无需业务代码干预,调用 Mapper 层 delete 删除方法时,底层自动将 DELETE 语句转为 UPDATE 更新语句;所有查询、分页、列表查询会自动拼接 WHERE deleted = 0,自动过滤已删除数据,无需手动处理。

2)自动填充 创建/更新时间

无需业务代码手动set时间,新增、修改时自动赋值create_time、update_time。

  1. 实体字段注解
ini 复制代码
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;

@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
  1. 注册全局自动填充处理器(核心配置,必须配置)

新建自定义填充类,实现 MetaObjectHandler,交给Spring管理,统一自动填充创建时间、更新时间、创建人、更新人等字段。

kotlin 复制代码
@Component
public class MyMetaHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        // 新增时自动填充创建时间、更新时间
        this.strictInsertFill(metaObject, "createTime", LocalDateTime::now, LocalDateTime.class);
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        // 修改时只更新更新时间
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
    }
}

生效原理:新增/修改数据时,MP 自动触发填充逻辑,无需业务代码手动 set 时间字段,全局统一规范。

3)乐观锁(并发更新防覆盖)

高并发场景防止多人同时更新覆盖数据,依靠version版本号实现无锁并发控制。

  1. 实体添加版本字段
java 复制代码
@Version
private Integer version;
  1. 注册乐观锁插件(核心配置,必须配置)

在 MP 全局配置类中添加乐观锁拦截器,否则版本号不会自动递增、并发控制失效。

java 复制代码
@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mpInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        // 乐观锁插件(新增)
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}
  1. 更新执行逻辑:更新数据时自动携带 where 版本条件,更新成功后版本号自动+1;若并发导致版本不匹配,更新行数为0,更新失败,避免数据覆盖。

  2. 更新逻辑:更新时携带原版本号,版本匹配才更新,版本不匹配更新失败。

4)主键策略 @TableId

统一管理主键生成规则,适配单机自增、分布式唯一ID:

kotlin 复制代码
@TableId(type = IdType.AUTO)
private Long id;

可通过 YAML 全局统一配置,无需每个实体单独注解(可选):

python 复制代码
mybatis-plus:
  global-config:
    db-config:
      id-type: assign_id # 全局默认雪花算法

常用策略说明:

  • AUTO:数据库自增(MySQL单机);

  • ASSIGN_ID:雪花算法,分布式Long唯一ID(MP默认);

  • ASSIGN_UUID:字符串UUID主键。

MyBatis-Plus 除基础CRUD与语法糖外,内置大量企业级高阶插件与防护能力,适配生产环境安全、性能、SaaS多租户、数据脱敏等场景,全部开箱即用。

2.5 高阶增强功能(企业级进阶能力)

1)SQL 日志打印(开发调试必备)

全局开启SQL日志,完整打印执行SQL、参数、执行耗时,方便排查BUG、优化SQL。

yml配置:

yaml 复制代码
mybatis-plus:
  configuration:
    # 开启SQL日志输出
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

效果:控制台完整输出最终执行的SQL语句、查询参数、结果行数,可直接复制到数据库执行调试。

2)SQL 性能分析插件(慢SQL监控)

用于开发、测试环境监控慢SQL,拦截超时SQL,避免劣质SQL上线导致数据库卡顿。可自定义超时阈值,超时自动打印警告日志。

ini 复制代码
@Bean
public MybatisPlusInterceptor mpInterceptor(){
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    // 慢SQL监控:超时200ms告警
    PerformanceInnerInterceptor performanceInterceptor = new PerformanceInnerInterceptor();
    performanceInterceptor.setMaxTime(200);
    interceptor.addInnerInterceptor(performanceInterceptor);
    return interceptor;
}

生产环境建议关闭,仅用于开发调优。

3)防全表更新/删除(生产安全核心)

MP 内置**「防误操作安全机制」**,默认禁止无条件全表更新、全表删除,彻底杜绝开发人员误写空条件导致的数据清空事故,生产刚需。

原理:当 Wrapper 无任何查询条件、直接执行 update、delete 时,直接抛出异常阻断SQL执行。

如需临时关闭(超级管理员、后台批量操作场景),可手动配置放行,禁止业务代码随意关闭。

4)数据脱敏插件(隐私数据防护)

内置字段自动脱敏能力,无需手动代码处理,查询返回自动脱敏,适配手机号、身份证、邮箱、地址等隐私字段。

支持注解配置脱敏规则:掩码、中间隐藏、首尾展示等,新增/修改数据库存储原始明文,「仅查询返回脱敏」,不影响数据库真实数据。

5)多租户插件(SaaS系统必备)

MP 官方内置多租户数据隔离插件,适配 SaaS、多商户、多团队共用一套数据库场景,实现**「代码零侵入全局数据隔离」**。

核心原理:拦截所有CRUD SQL,自动追加租户条件,无需业务代码手动拼接租户ID。

核心细节:

  • 默认租户字段:tenant_id(可自定义任意字段名,如 merchant_id、company_id)
  • 租户ID由业务拦截器解析Token后存入 ThreadLocal,插件仅读取不生成
  • 支持自定义忽略表(字典表、公共配置表、租户表本身不隔离)
  • 支持超级管理员关闭租户隔离,查询全量数据

核心配置示例:

less 复制代码
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
    // 获取当前登录租户ID
    @Override
    public Long getTenantId() {
        return UserContext.getTenantId();
    }
    // 自定义租户字段名(可不写,默认tenant_id)
    @Override
    public String getTenantField() {
        return "tenant_id";
    }
    // 忽略隔离的公共表
    @Override
    public boolean ignoreTable(String tableName) {
        return Arrays.asList("sys_dict","sys_tenant").contains(tableName);
    }
}));

6)游标分页(大数据量高性能分页)

区别于普通 offset 分页,「游标分页适配百万级大数据列表、数据导出场景」,解决传统分页越往后越慢的性能痛点。

优势:

  • 无需 count 统计总数,性能极高
  • 基于主键游标滚动查询,避免深分页偏移性能衰减
  • 适合批量数据同步、文件导出、数据清洗场景

三、入门快速上手步骤(极简流程)

  1. SpringBoot项目引入mybatis-plus-boot-starter依赖,移除原生MyBatis启动器;

  2. application.yml配置数据库、开启下划线转驼峰、打印SQL日志;

  3. 实体类使用@TableName、@TableId、@TableField映射数据库字段;

  4. Mapper接口继承BaseMapper,启动类添加@MapperScan扫描Mapper;

  5. Service层实现IService+ServiceImpl分层;

  6. 需要分页、乐观锁则注册对应插件;

  7. 使用LambdaWrapper拼接动态条件,调用内置CRUD方法完成业务。

四、总结

  1. MP核心价值:封装单表通用操作,把开发人员从重复SQL中解放;

  2. 分层CRUD:BaseMapper负责底层DAO,IService适配业务层批量、链式操作;

  3. Wrapper条件构造器优先使用Lambda写法,配合condition动态条件,彻底消灭冗余if,支持复杂and/or括号逻辑;

  4. 分页、逻辑删除、自动填充、乐观锁、主键策略均为内置语法糖(2.4章节),开箱即用;高阶能力(2.5章节)支持SQL监控、数据脱敏、多租户隔离、大数据游标分页、生产防误删防护;

  5. MP不替代MyBatis,复杂多表、统计查询依旧支持自定义XML,两者可共存。

参考文献

1 MyBatis-Plus 官方文档. baomidou.com/

2 MyBatis 官方使用文档. mybatis.org/mybatis-3/

❝

(注:部分内容可能由 AI 生成)

❞

本文使用 markdown.com.cn 排版

相关推荐
金銀銅鐵9 小时前
[Java] 用 GUI 展示 EnumSet 增减元素时的内部变化(上)
java·后端·ai编程
念何架构之路10 小时前
go-grpc设计思想
开发语言·后端·golang
system_error_x10 小时前
SpringBoot 集成 Thymeleaf 简洁入门
java·spring boot·后端
步行cgn12 小时前
Spring MVC 视图机制的核心接口与类
java·开发语言·后端·spring·mvc
码事漫谈12 小时前
AI 读你的提示词,其实很没耐心
后端
用户83562907805113 小时前
如何使用 Python 格式化 Excel 单元格中的长文本
后端·python
我命由我1234513 小时前
Python 开发 - Python 的 pathlib
java·开发语言·后端·python·学习·java-ee·学习方法
IT_陈寒13 小时前
Java性能优化完全指南
前端·人工智能·后端
电子IT笔记13 小时前
对接群晖NAS,上传和删除踩坑记录
前端·后端
Mk5987813 小时前
P4:把 Agent 的记忆搬进 Redis —— AgentScope-Java 工程化实战
后端