SpringBoot3 + MyBatis-Plus 搞定宠物管理:从0到1实现增删改查
日常开发中,只要涉及到数据库操作,"增删改查"几乎是绕不开的基础场景。比如宠物门店需要管理宠物信息,手动写SQL不仅繁琐,还容易出错------有没有更高效的方式?
今天就以宠物信息管理为例,聊聊如何用SpringBoot3整合MyBatis-Plus,零冗余实现基础的CRUD操作。整个过程我会拆解得足够细,哪怕是刚接触SpringBoot的同学,也能跟着一步步做出来。
先明确核心目标
我们要实现的功能很清晰:
- 新增宠物信息(姓名、品种、年龄)
- 根据ID查询单条宠物信息
- 查询所有宠物信息
- 根据ID修改宠物信息
- 根据ID删除宠物信息
对应的实体类Pet已经给定,我们基于这个基础展开。
第一步:环境准备
1.1 基础依赖(Maven)
SpringBoot3要求JDK版本至少为17,先确保环境符合。然后在pom.xml中引入核心依赖:
xml
<!-- SpringBoot父工程 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/>
</parent>
<dependencies>
<!-- SpringWeb:提供Web接口能力 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis-Plus:简化MyBatis开发 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.5</version>
</dependency>
<!-- MySQL驱动:连接MySQL数据库 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok:简化实体类代码(省去getter/setter等) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 测试依赖(可选) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
1.2 数据库准备
创建MySQL数据库(比如命名为pet_manage),并新建pet表,字段和实体类对应:
sql
CREATE TABLE `pet` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '宠物ID',
`name` varchar(50) DEFAULT NULL COMMENT '宠物名称',
`breed` varchar(50) DEFAULT NULL COMMENT '宠物品种',
`age` int(11) DEFAULT NULL COMMENT '宠物年龄',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
第二步:核心配置
在src/main/resources下创建application.yml,配置数据库连接和MyBatis-Plus基础参数:
yaml
spring:
# 数据库连接配置
datasource:
url: jdbc:mysql://localhost:3306/pet_manage?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root # 替换为你的MySQL用户名
password: 123456 # 替换为你的MySQL密码
driver-class-name: com.mysql.cj.jdbc.Driver
# MyBatis-Plus配置
mybatis-plus:
# 实体类别名包(简化XML中的类名引用)
type-aliases-package: com.example.petmanage.entity
configuration:
# 打印SQL日志(开发阶段方便调试)
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 下划线转驼峰(数据库字段pet_name → 实体类petName)
map-underscore-to-camel-case: true
第三步:代码实现
3.1 实体类(Pet)
按照要求创建实体类,放在com.example.petmanage.entity包下:
java
package com.example.petmanage.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data // 自动生成getter、setter、toString、hashCode、equals
@NoArgsConstructor // 无参构造器
@AllArgsConstructor // 全参构造器
public class Pet {
private Long id; // 宠物ID(唯一标识)
private String name; // 宠物名称
private String breed; // 宠物品种(如"金毛"、"布偶猫")
private Integer age; // 宠物年龄(单位:岁)
}
3.2 Mapper层(核心:复用MyBatis-Plus的BaseMapper)
MyBatis-Plus的核心优势之一就是封装了基础CRUD的Mapper方法,我们只需要创建接口继承BaseMapper即可,无需手动写SQL。
创建PetMapper接口,放在com.example.petmanage.mapper包下:
java
package com.example.petmanage.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.petmanage.entity.Pet;
import org.apache.ibatis.annotations.Mapper;
// 注解标记为MyBatis的Mapper接口,SpringBoot会自动扫描
@Mapper
public interface PetMapper extends BaseMapper<Pet> {
// 无需写任何方法!BaseMapper已经封装了insert、selectById、updateById、deleteById、selectList等方法
}
3.3 Service层(封装业务逻辑,可选但推荐)
实际开发中,我们会在Service层封装业务逻辑,MyBatis-Plus也提供了IService/ServiceImpl简化Service层开发。
3.3.1 Service接口
java
package com.example.petmanage.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.petmanage.entity.Pet;
public interface PetService extends IService<Pet> {
// 基础CRUD方法已由IService封装,如需自定义业务方法可在此扩展
}
3.3.2 Service实现类
java
package com.example.petmanage.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.petmanage.entity.Pet;
import com.example.petmanage.mapper.PetMapper;
import com.example.petmanage.service.PetService;
import org.springframework.stereotype.Service;
@Service
public class PetServiceImpl extends ServiceImpl<PetMapper, Pet> implements PetService {
// 无需写基础CRUD实现!ServiceImpl已基于PetMapper实现了IService的所有方法
}
3.4 Controller层(提供Web接口)
创建PetController,暴露HTTP接口供前端调用,放在com.example.petmanage.controller包下:
java
package com.example.petmanage.controller;
import com.example.petmanage.common.ResultCode;
import com.example.petmanage.entity.Pet;
import com.example.petmanage.common.R;
import com.example.petmanage.service.PetService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/pet")
public class PetController {
@Autowired
private PetService petService;
/**
* 新增宠物信息
* @param pet 宠物信息(JSON格式)
* @return 统一返回结果
*/
@PostMapping("/add")
public R<Boolean> addPet(@RequestBody Pet pet) {
// 参数校验:名称和品种不能为空,年龄需大于0
if (pet == null || !StringUtils.hasText(pet.getName()) || !StringUtils.hasText(pet.getBreed())) {
return R.error(ResultCode.PARAM_ERROR);
}
if (pet.getAge() != null && pet.getAge() <= 0) {
return R.error(400, "宠物年龄必须大于0");
}
// 执行新增操作
boolean saveFlag = petService.save(pet);
if (saveFlag) {
return R.success(true);
} else {
return R.error(ResultCode.ERROR);
}
}
/**
* 根据ID查询宠物信息
* @param id 宠物ID
* @return 统一返回结果(包含宠物信息)
*/
@GetMapping("/{id}")
public R<Pet> getPetById(@PathVariable Long id) {
// 参数校验:ID不能为空且大于0
if (id == null || id <= 0) {
return R.error(ResultCode.PARAM_ERROR);
}
// 执行查询操作
Pet pet = petService.getById(id);
if (pet != null) {
return R.success(pet);
} else {
return R.error(ResultCode.DATA_NOT_FOUND);
}
}
/**
* 查询所有宠物信息
* @return 统一返回结果(包含宠物列表)
*/
@GetMapping("/list")
public R<List<Pet>> getAllPets() {
List<Pet> petList = petService.list();
// 即使列表为空,也返回成功(空列表也是合法数据)
return R.success(petList);
}
/**
* 根据ID修改宠物信息
* @param pet 要修改的宠物信息(需包含ID)
* @return 统一返回结果
*/
@PutMapping("/update")
public R<Boolean> updatePet(@RequestBody Pet pet) {
// 参数校验:ID不能为空,名称/品种不能为空,年龄需大于0
if (pet == null || pet.getId() == null || pet.getId() <= 0) {
return R.error(ResultCode.PARAM_ERROR);
}
if (!StringUtils.hasText(pet.getName()) || !StringUtils.hasText(pet.getBreed())) {
return R.error(ResultCode.PARAM_ERROR);
}
if (pet.getAge() != null && pet.getAge() <= 0) {
return R.error(400, "宠物年龄必须大于0");
}
// 先校验数据是否存在
if (!petService.existsById(pet.getId())) {
return R.error(ResultCode.DATA_NOT_FOUND);
}
// 执行修改操作
boolean updateFlag = petService.updateById(pet);
if (updateFlag) {
return R.success(true);
} else {
return R.error(ResultCode.ERROR);
}
}
/**
* 根据ID删除宠物信息
* @param id 宠物ID
* @return 统一返回结果
*/
@DeleteMapping("/{id}")
public R<Boolean> deletePet(@PathVariable Long id) {
// 参数校验:ID不能为空且大于0
if (id == null || id <= 0) {
return R.error(ResultCode.PARAM_ERROR);
}
// 先校验数据是否存在
if (!petService.existsById(id)) {
return R.error(ResultCode.DATA_NOT_FOUND);
}
// 执行删除操作
boolean deleteFlag = petService.removeById(id);
if (deleteFlag) {
return R.success(true);
} else {
return R.error(ResultCode.ERROR);
}
}
}
关于统一返回类的设计见Springboot3 | 统一返回类设计:从问题到实现
3.5 启动类
最后创建SpringBoot启动类,放在com.example.petmanage包下:
java
package com.example.petmanage;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
// 扫描Mapper接口(也可以在Mapper接口上加@Mapper注解,二选一即可)
@MapperScan("com.example.petmanage.mapper")
public class PetManageApplication {
public static void main(String[] args) {
SpringApplication.run(PetManageApplication.class, args);
}
}
第四步:接口测试
启动项目后,我们可以用PostMan、Apifox等工具测试接口,这里给出核心测试用例:
4.1 新增宠物
- 请求方式:POST
- 请求地址:http://localhost:8080/pet/add
- 请求体(JSON):
json
{
"name": "旺财",
"breed": "金毛",
"age": 3
}
- 响应:true(新增成功)
4.2 查询单个宠物
- 请求方式:GET
- 请求地址:http://localhost:8080/pet/1(1是新增后返回的ID)
- 响应:
json
{
"id": 1,
"name": "旺财",
"breed": "金毛",
"age": 3
}
4.3 查询所有宠物
- 请求方式:GET
- 请求地址:http://localhost:8080/pet/list
- 响应:
json
[
{
"id": 1,
"name": "旺财",
"breed": "金毛",
"age": 3
}
]
4.4 修改宠物信息
- 请求方式:PUT
- 请求地址:http://localhost:8080/pet/update
- 请求体(JSON):
json
{
"id": 1,
"name": "旺财",
"breed": "金毛",
"age": 4
}
- 响应:true(修改成功)
4.5 删除宠物
- 请求方式:DELETE
- 请求地址:http://localhost:8080/pet/1
- 响应:true(删除成功)
核心逻辑总结
整个过程中,我们几乎没写一行SQL,核心就是复用了MyBatis-Plus的两个核心类:
BaseMapper:封装了Mapper层的基础CRUD方法,无需手动编写XML或注解SQL;IService/ServiceImpl:进一步封装了Service层的业务逻辑,提供了更友好的调用方式。
这种方式的优势很明显:
- 减少重复代码:不用为每个实体类写基础的增删改查SQL;
- 降低出错概率:MyBatis-Plus的封装经过了大量验证,比手写SQL更稳定;
- 提升开发效率:专注业务逻辑,而非基础的数据库操作。
如果需要扩展复杂查询(比如按品种查询宠物),也可以基于MyBatis-Plus的条件构造器QueryWrapper实现,后续有机会再聊。
总之,SpringBoot3+MyBatis-Plus的组合,能让我们以极低的成本搞定基础的数据库操作,把更多精力放在业务本身------这也是日常开发中"偷懒"的智慧。