Mybatis-plus入门到精通

Why Mybatis-plus:

  • 少写 80% 重复代码:单表 CRUD 不用写 XML / 注解 SQL,继承 BaseMapper 直接调用,新增、查询、更新、删除一行代码搞定,告别 "复制粘贴" 式开发;
  • 动态 SQL 不用拼 :LambdaQueryWrapper 链式调用,条件查询像 "说话" 一样直观(比如ge(User::getAge, 18).like(User::getUserName, "张")),杜绝字段拼写错误和<if>/<where>标签繁琐配置;
  • 分页一键搞定 :内置物理分页插件,不用集成第三方工具,new Page(pageNum, pageSize)+selectPage()直接返回分页结果,性能比内存分页更优;
  • 代码生成省时间:AutoGenerator 一键生成 Entity、Mapper、Service、Controller 全套代码,新模块开发周期直接缩短 30%,不用手动建文件写模板;
  • 常用功能内置化:逻辑删除、乐观锁、主键策略、自动填充(创建时间 / 更新时间),不用手动写 SQL 控制,注解 + 简单配置就能实现,解决并发、数据安全等常见问题;
  • 零侵入兼容 MyBatis:完全保留 MyBatis 灵活性,复杂 SQL(多表联查、子查询)还能写 XML,既享受高效开发,又不牺牲自定义扩展能力;

快速上手:

1引入依赖:

java 复制代码
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>

由于这个starter包含对mybatis的自动装配,因此完全可以替换掉Mybatis的starter。

2定义Mapper

为了简化单表CRUD,MybatisPlus提供了一个基础的BaseMapper接口,其中已经实现了单表的CRUD:

因此我们自定义的Mapper只要实现了这个BaseMapper,就无需自己实现单表CRUD了。

例如:(需要指定BaseMapper需要操作的实体类类型)

java 复制代码
package com.itheima.mp.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;

public interface UserMapper extends BaseMapper<User> {
}

测试:

之后就能使用BaseMapper里已经拥有的丰富的增删改查功能:

java 复制代码
package com.itheima.mp.mapper;

import com.itheima.mp.domain.po.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDateTime;
import java.util.List;

@SpringBootTest
class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    void testInsert() {
        User user = new User();
        user.setId(5L);
        user.setUsername("Lucy");
        user.setPassword("123");
        user.setPhone("18688990011");
        user.setBalance(200);
        user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        userMapper.insert(user);
    }

    @Test
    void testSelectById() {
        User user = userMapper.selectById(5L);
        System.out.println("user = " + user);
    }

    @Test
    void testSelectByIds() {
        List<User> users = userMapper.selectBatchIds(List.of(1L, 2L, 3L, 4L, 5L));
        users.forEach(System.out::println);
    }

    @Test
    void testUpdateById() {
        User user = new User();
        user.setId(5L);
        user.setBalance(20000);
        userMapper.updateById(user);
    }

    @Test
    void testDelete() {
        userMapper.deleteById(5L);
    }
}

mp的默认规则:

MybatisPlus是根据BaseMapper中泛型PO实体(上面测试中的是User类)的信息来推断出表的信息,从而生成SQL的,我们想利用mp快速生成sql,就要遵守他的默认规则

默认情况下:

  • MybatisPlus会把PO实体的类名驼峰转下划线作为表名

  • MybatisPlus会把PO实体的所有变量名驼峰转下划线作为表的字段名,并根据变量类型推断字段类型

  • MybatisPlus会把名为id的字段作为主键

注意:但很多情况下,默认的实现与实际场景不符,因此MybatisPlus提供了一些注解便于我们声明表信息。

常用注解

**@TableName:**标识实体类对应的表

示例:

java 复制代码
@TableName("user")
public class User {
    private Long id;
    private String name;
}

TableName除了标识表名,还有其他属性:


**@TableId:**标识实体类中的主键字段

示例:

java 复制代码
@TableName("user")
public class User {
    @TableId
    private Long id;
    private String name;
}

属性:

其中IdType常见类型:

  • AUTO:利用数据库的id自增长

  • INPUT:手动生成id

  • ASSIGN_ID:雪花算法生成Long类型的全局唯一id,这是默认的ID策略

**TableField:**普通字段注解:标识数据库中对应的字段

示例

java 复制代码
@TableName("user")
public class User {
    @TableId
    private Long id;
    private String name;
    private Integer age;
    @TableField(is_married")
    private Boolean isMarried;
    @TableField("`concat`")
    private String concat;
}

注意:一般情况下我们并不需要给字段添加@TableField注解,一些特殊情况除外:

  • 成员变量名与数据库字段名不一致

  • 成员变量是以isXXX命名,并且类型为Boolean,按照JavaBean的规范,MybatisPlus识别字段时会把is去除,这就导致与数据库不符。

  • 成员变量名与数据库一致,但是与数据库的关键字冲突。使用@TableField注解给字段名添加转义字符:````````

常见其他属性:

mp的yaml配置:

MybatisPlus也支持基于yaml文件的自定义配置,官方文档:使用配置 | MyBatis-Plus

大多数的配置都有默认值,因此我们都无需配置。但还有一些是没有默认值的,例如:

  • 实体类的别名扫描包

  • 全局id类型

java 复制代码
mybatis-plus:
  type-aliases-package: com.dj.mp.domain.po
  global-config:
    db-config:
      id-type: auto # 全局id类型为自增长

MyBatisPlus也支持手写SQL的,而mapper文件的读取地址可以自己配置:

java 复制代码
mybatis-plus:
  mapper-locations: "classpath*:/mapper/**/*.xml" # Mapper.xml文件地址,当前这个是默认值。

核心功能:

条件构造器Wrapper--动态 SQL 的 "拼接神器"

除了新增以外,修改、删除、查询的SQL语句都需要指定where条件。因此BaseMapper中提供的相关方法除了以id作为where条件以外,还支持更加复杂的where条件。

Wrapper有很多默认实现

Wrapper的子类AbstractWrapper提供了where中包含的所有条件构造方法:

QueryWrapper在AbstractWrapper的基础上拓展了一个select方法,允许指定查询字段:

UpdateWrapper在AbstractWrapper的基础上拓展了一个set方法,允许指定SQL中的SET部分:

实际上我们只需要重点关注常见的三个

Wrapper 核心用法(LambdaQueryWrapper 为主,推荐)​

先明确核心原则:链式调用,条件方法自动拼接 AND/OR,无需手动处理逻辑运算符。

  1. 基础条件方法

1

  1. 逻辑运算符(AND/OR)​

默认多个条件用 AND 连接,如需 OR 可手动调用 .or() 方法:​

java 复制代码
// 示例:查询(年龄>=18 且 用户名含"张")OR(邮箱不为空)​

LambdaQueryWrapper new LambdaQueryWrapper​

.ge(User::getAge, 18)​

.like(User::getUserName, "张")​

.or() // 手动切换为OR​

.isNotNull(User::getEmail);​

// 对应SQL:WHERE age >= ? AND user_name LIKE ? OR email IS NOT NULL​

  1. 非空判断(自动过滤 NULL 值)​

Wrapper 会自动忽略值为 null 的条件,无需手动写 test="xxx != null">:​

java 复制代码
Integer minAge = null; // 前端未传该参数​

String userName = "张";​

​

LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper .ge(minAge != null, User::getAge, minAge) // 若minAge为null,该条件不拼接​

.like(User::getUserName, userName);​

// 最终SQL:WHERE user_name LIKE '%张%'(age条件被忽略)​

第一个参数可传布尔值,控制条件是否生效(如 .ge(Boolean.TRUE, ...) 强制生效)。​

不同 Wrapper 实战示例​

  1. LambdaQueryWrapper(查询场景,推荐)​

// 需求:查询年龄18-30岁、用户名含"张"、邮箱不为空的用户,按创建时间降序​

LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper .between(User::getAge, 18, 30)​

.like(User::getUserName, "张")​

.isNotNull(User::getEmail)​

.orderByDesc(User::getCreateTime);​

List = userMapper.selectList(wrapper);​

// 对应SQL:​

// SELECT id,user_name,age,email,create_time,update_time,is_deleted ​

// FROM user ​

// WHERE age BETWEEN ? AND ? ​

// AND user_name LIKE ? ​

// AND email IS NOT NULL ​

// ORDER BY create_time DESC​


2. QueryWrapper(非 Lambda,简单场景)​

// 注意:字段名需手动写字符串,容易拼错(不推荐复杂场景)​

QueryWrapper = new QueryWrapper​

.eq("age", 25)​

.like("user_name", "李")​

.orderByAsc("create_time");​

ListList = userMapper.selectList(wrapper);​


3. UpdateWrapper(动态更新场景)​

// 需求:更新用户(ID=1)的用户名和年龄,仅更新非空字段​

UpdateWrapper = new UpdateWrapper​

.set("user_name", "张三更新") // 强制更新该字段​

.set("age", 26)​

.eq("id", 1); // WHERE条件​

// 或用LambdaUpdateWrapper(避免字段名字符串):​

LambdaUpdateWrapper new LambdaUpdateWrapper​

.set(User::getUserName, "张三更新")​

.set(User::getAge, 26)​

.eq(User::getId, 1);​

userMapper.update(null, wrapper); // 第一个参数传null,用Wrapper的set条件​

// 对应SQL:UPDATE user SET user_name = ?, age = ? WHERE id = ?​


​多表关联:

理论上来讲MyBatisPlus是不支持多表查询的,不过我们可以利用Wrapper中自定义条件结合自定义SQL来实现多表查询的效果。

例如,我们要查询出所有收货地址在北京的并且用户id在1、2、4之中的用户 要是自己基于mybatis实现SQL,大概是这样的:

XML 复制代码
<select id="queryUserByIdAndAddr" resultType="com.itheima.mp.domain.po.User">
      SELECT *
      FROM user u
      INNER JOIN address a ON u.id = a.user_id
      WHERE u.id
      <foreach collection="ids" separator="," item="id" open="IN (" close=")">
          #{id}
      </foreach>
      AND a.city = #{city}
  </select>

可以看出其中最复杂的就是WHERE条件的编写,如果业务复杂一些,这里的SQL会更变态。

但是基于自定义SQL结合Wrapper的玩法,我们就可以利用Wrapper来构建查询条件,然后手写SELECT及FROM部分,实现多表查询。

查询条件这样来构建:

XML 复制代码
@Test
void testCustomJoinWrapper() {
    // 1.准备自定义查询条件
    QueryWrapper<User> wrapper = new QueryWrapper<User>()
            .in("u.id", List.of(1L, 2L, 4L))
            .eq("a.city", "北京");

    // 2.调用mapper的自定义方法
    List<User> users = userMapper.queryUserByWrapper(wrapper);

    users.forEach(System.out::println);
}

然后在UserMapper中自定义方法:

XML 复制代码
@Select("SELECT u.* FROM user u INNER JOIN address a ON u.id = a.user_id ${ew.customSqlSegment}")
List<User> queryUserByWrapper(@Param("ew")QueryWrapper<User> wrapper);

注意:通过${ew.customSqlSegment}来拼接我们wrapper的查询条件片段,其中@Param("ew")中ew是固定的约定写法,不能替换成别的词,如@Param("wrapper"),这样会报错


IService ------ Service 层的 "效率神器"

IService 是 MyBatis-Plus 提供的 Service 层接口,基于 BaseMapper 封装了更全面的 CRUD 方法,同时支持批量操作、链式调用、条件查询(结合 Wrapper)等高级特性。​

它解决了传统 Service 层的 2 大痛点:​

  • 无需手动编写 Service 接口和实现类的重复方法(如 getById、list、saveBatch);
  • 内置批量操作、分页查询等常用功能,不用自己封装工具类。

一:IService 核心价值:​

  • 接口 + 实现类一键生成,减少模板代码;
  • 方法命名更贴合业务(如 save 替代 insert、remove 替代 delete);
  • 支持 Lambda 条件查询,与 Wrapper 无缝衔接;
  • 批量操作默认优化性能(如分批插入)。

二、快速搭建 IService(3 步搞定)​

以之前的 User 实体为例,基于 Spring Boot + MP 环境,3 步搭建完整 Service 层:

1创建 UserService 接口,继承 MP 提供的 `IService 添加方法:​

XML 复制代码
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.mp.entity.User;

// 泛型指定实体类User
public interface UserService extends IService> {
    // 无需手动添加CRUD方法,IService已内置全套
    // 如需自定义业务方法,可在此添加(如 getUserByUserName)
}

2. 实现 Service 类(继承 ServiceImpl)​

创建 UserServiceImpl 实现类,继承 ServiceImpl>,并实现自定义接口:​

XML 复制代码
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;​

import com.example.mp.entity.User;​

import com.example.mp.mapper.UserMapper;​

import com.example.mp.service.UserService;​

import org.springframework.stereotype.Service;​

​

// 泛型参数1:对应的Mapper接口;参数2:实体类​

@Service // 注入Spring容器​

public class UserServiceImpl extends ServiceImpl, User> implements UserService {​

// 无需重写CRUD方法,ServiceImpl已实现所有IService接口方法​

// 如需自定义业务逻辑,在此实现(如重写或新增方法)​

}​

3. 注入使用(Controller / 其他 Service)​

直接在 Controller 中注入 UserService,即可调用内置方法:​

XML 复制代码
import com.example.mp.entity.User;​

import com.example.mp.service.UserService;​

import org.springframework.beans.factory.annotation.Autowired;​

import org.springframework.web.bind.annotation.*;​

​

import java.util.List;​

​

@RestController​

@RequestMapping("/user")​

public class UserController {​

​

@Autowired​

private UserService userService; // 注入IService实现类​

​

// 直接调用内置方法,无需手动写Service逻辑​

@GetMapping("/{id}")​

public User getById(@PathVariable Long id) {​

return userService.getById(id); // IService内置方法​

}​

}​

​

三、IService 核心内置方法(常用分类)​

IService 封装了近 50 个常用方法,覆盖查询、新增、更新、删除、批量操作、分页等场景,以下是高频用法分类:​

1. 单条数据操作​

|--------------------------|-------------------|---------------------------------------------------|
| 方法名​ | 作用​ | 示例代码​ |
| getById(Long id)​ | 根据 ID 查询单条数据​ | userService.getById(1L)​ |
| save(User entity)​ | 新增单条数据​ | userService.save(new User("张三", 25))​ |
| updateById(User entity)​ | 根据 ID 更新​ | userService.updateById(new User(1L, "张三更新", 26))​ |
| removeById(Long id)​ | 根据 ID 删除(支持逻辑删除)​ | userService.removeById(1L)​ |

2. 批量操作

|-------------------------------|------------------------|-----------------------------------------------------|
| 方法名​ | 作用​ | 示例代码​ |
| saveBatch(List)​ | 批量新增(默认分批插入,优化性能)​ | userService.saveBatch(Arrays.asList(user1, user2))​ |
| saveOrUpdateBatch(List list)​ | 批量新增或更新(存在则更新,不存在则新增)​ | userService.saveOrUpdateBatch(list)​ |
| updateBatchById(List> list)​ | 批量更新(根据 ID)​ | userService.updateBatchById(list)​ |
| removeByIds(Collection ids)​ | 批量删除(根据 ID 集合)​ | userService.removeByIds(Arrays.asList(1L, 2L))​ |

​对于批量新增,MySQL的客户端连接参数中有这样的一个参数:rewriteBatchedStatements,可以重写批处理的statement语句默认是关闭,我们可以将其打开,能够提升批处理速度

原因:会启用 MySQL Connector 驱动对批量语句的重写功能。具体来说:

它会将多条 SQL 语句合并成一条语句发送到数据库。

例如,原本需要执行多次的 INSERT 语句会被合并为一条批量插入语句。
如何使用? :

修改项目中的application.yml文件,在jdbc的url后面添加参数&rewriteBatchedStatements=true:

java 复制代码
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: MySQL123
  1. 条件查询(结合 Wrapper,核心)​

与 Wrapper 无缝衔接,支持 Lambda 条件构造,无需手动调用 Mapper:​

java 复制代码
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;​

​

// 示例1:条件查询列表​

LambdaQueryWrapper wrapper = new LambdaQueryWrapper<User>()​

.ge(User::getAge, 18)​

.like(User::getUserName, "张");​

List> userList = userService.list(wrapper); // 直接用Service查询​

​

// 示例2:条件查询单条数据​

User user = userService.getOne(wrapper); // 对应SELECT ONE​

​

// 示例3:条件删除​

boolean success = userService.remove(wrapper); // 对应DELETE WHERE​

  1. 分页查询(内置分页支持)​

结合 MP 分页插件,一行代码实现分页,无需手动处理 Page 对象:​

java 复制代码
import com.baomidou.mybatisplus.core.metadata.IPage;​

import com.baomidou.mybatisplus.core.toolkit.Wrappers;​

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;​

​

// 需求:分页查询年龄>=18的用户,第2页,每页10条​

Page page = new Page, 10); // 页码(从1开始)、每页条数​

LambdaQueryWrapper Wrappers.lambdaQuery(User.class)​

.ge(User::getAge, 18);​

​

IPage userService.page(page, wrapper); // 内置分页方法​

// 分页结果:userPage.getRecords()(数据列表)、userPage.getTotal()(总条数)、userPage.getPages()(总页数)​

​
  1. 其他实用方法​

|------------------------------------|-------------|-----------------------------------------------------------|
| 方法名​ | 作用​ | 示例代码​ |
| count()​ | 查询总记录数​ | long total = userService.count();​ |
| count(LambdaQueryWrapper wrapper)​ | 条件查询记录数​ | long count = userService.count(wrapper);​ |
| list()​ | 查询所有数据​ | List = userService.list();​ |
| chain()​ | 链式调用(高级用法)​ | userService.chain().like(User::getUserName, "张").list();​ |

四、自定义业务方法(扩展 IService)​

如果内置方法满足不了业务需求,可在接口中新增自定义方法,在实现类中实现:​

  1. 自定义接口方法​

java 复制代码
public interface UserService extends IService // 自定义方法:根据用户名查询用户(模糊匹配)​

List getUserByUserNameLike(String userName);​

}​

  1. 实现类中实现(可调用 baseMapper 或 Wrapper)​​
java 复制代码
@Service​

public class UserServiceImpl extends ServiceImpl User> implements UserService {​

​

@Override​

public ListUserNameLike(String userName) {​

// 方式1:调用 baseMapper(ServiceImpl内置属性,无需注入)​

return baseMapper.selectList(Wrappers.lambdaQuery(User.class)​

.like(User::getUserName, userName));​

​

// 方式2:直接用 Service 内置方法 + Wrapper(推荐)​

// return this.list(Wrappers.lambdaQuery(User.class).like(User::getUserName, userName));​

}​

}​

​
  1. 调用自定义方法​
java 复制代码
​



@GetMapping("/like")​

public ListLike(@RequestParam String userName) {​

return userService.getUserByUserNameLike(userName); // 调用自定义方法​

}​

​

扩展功能:

1代码生成插件mybatis-plus

1 分钟生成单表全套代码(Entity+Mapper+IService+ServiceImpl+Controller);

安装插件:

使用:先连接数据库

生成代码:

最终基础代码会自动生成

2静态工具Db:

有的时候Service之间也会相互调用,为了避免出现循环依赖问题,MybatisPlus提供一个静态工具类:Db,其中的一些静态方法与IService中方法签名基本一致,也可以帮助我们实现CRUD功能:

3枚举处理器和Json处理器

数据库中常有 "状态类字段"(如 user_status:0 - 正常、1 - 禁用、2 - 冻结),传统做法是用 Integer 存储,代码中用常量判断(如 if (userStatus == 1)),存在 2 个痛点:​

  • 可读性差:数字无法直观表达含义;
  • 易出错:硬编码数字可能写错(如把 2 写成 3)。

MP 枚举处理器的价值:让数据库字段与 Java 枚举直接映射,无需手动转换,代码可读性和安全性大幅提升。

首先定义枚举:

java 复制代码
package com.itheima.mp.enums;

import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;

@Getter
public enum UserStatus {
    NORMAL(1, "正常"),
    FREEZE(2, "冻结")
    ;
    private final int value;
    private final String desc;

    UserStatus(int value, String desc) {
        this.value = value;
        this.desc = desc;
    }
}

然后把User类中的status字段的Inteager类型改为UserStatus 类型:

要让MybatisPlus处理枚举与数据库类型自动转换,我们必须告诉MybatisPlus,枚举中的哪个字段的值作为数据库值MybatisPlus提供了@EnumValue注解来标记枚举属性:

并且,在UserStatus枚举中通过@JsonValue注解标记JSON序列化时展示的字段:

最后配上枚举处理器

XML 复制代码
mybatis-plus:
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler

Json处理器

数据库中常有 "复杂结构字段"(如 ext_info 存储用户额外信息:{"address":"北京","hobby":"篮球","读书"}),传统做法是用 varchar 存储 JSON 字符串,存在 2 个痛点:​

  • 代码冗余:需手动将 Java 对象序列化为 JSON 字符串,查询后再反序列化为对象;
  • 易出错:手动序列化可能导致 JSON 格式错误,查询时解析复杂。

MP JSON 处理器的价值:让 Java 对象与数据库 JSON 字段直接映射,自动完成序列化 / 反序列化,无需手动处理。

JSON 处理器使用步骤(3 步搞定)​

步骤 1:定义 JSON 对应的 Java 实体类​

创建存储 JSON 结构的实体类(如用户额外信息):

java 复制代码
package com.itheima.mp.domain.po;

import lombok.Data;

@Data
public class UserInfo {
    private Integer age;
    private String intro;
    private String gender;
}

2接下来,将User类的info字段修改为UserInfo类型,并声明类型处理器:

同时,在User类上添加一个注解,声明自动映射:


分页插件

在未引入分页插件的情况下,MybatisPlus是不支持分页功能的,IServiceBaseMapper中的分页方法都无法正常起效。 所以,我们必须配置分页插件。

配置分页插件

在项目中新建一个配置类:

java 复制代码
package com.itheima.mp.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        // 初始化核心插件
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

测试:

java 复制代码
@Test
void testPageQuery() {
    // 1.分页查询,new Page()的两个参数分别是:页码、每页大小
    Page<User> p = userService.page(new Page<>(2, 2));
    // 2.总条数
    System.out.println("total = " + p.getTotal());
    // 3.总页数
    System.out.println("pages = " + p.getPages());
    // 4.数据
    List<User> records = p.getRecords();
    records.forEach(System.out::println);
}
相关推荐
OxYGC16 分钟前
[AI工程] Spring AI 第十四篇:Agent 五种模式在 2.0 里怎么写
java·spring·ai·ai编程
code2cat17 分钟前
【随笔】从聊天到调用工具:理解MCP在AI应用中的位置
java·人工智能
2501_9339232525 分钟前
Spring Bean作用域揭秘:单例、原型、请求与会话的区别
java·后端·spring·java-ee
学编程就要猛43 分钟前
基于Spring AI 的智能聊天机器人
java·spring ai·chat robot
修炼室1 小时前
Java开发工程师笔试经验贴【高频知识】
java
木井巳1 小时前
【记忆化搜索】最长递增子序列
java·算法·leetcode·深度优先·剪枝·推荐算法
杨杨杨大侠1 小时前
知识库已经有了,Java 程序员还要做什么?Spring AI RAG 实战
java·openai·ai编程
伊信1 小时前
Spring AI 基础学习与应用
java·后端
夜郎king1 小时前
基于 Java + Playwright 实现网站自动访问与数据采集(以腾讯云开发者社区为例)
java·playwright·网页数据获取
Anastasiozzzz1 小时前
重新定义 Agent 基建:Redis 在现代 AI 与智能体系统中的工程实践
java·人工智能·redis·ai