后端开发CRUD实现

目录

[一、环境依赖(pom.xml 核心依赖)](#一、环境依赖(pom.xml 核心依赖))

二、数据库表设计(以sys_user为例)

三、核心代码实现

[1. 实体类(Entity)](#1. 实体类(Entity))

[2. DTO 类(数据传输对象)](#2. DTO 类(数据传输对象))

[(1)新增 / 修改请求 DTO](#(1)新增 / 修改请求 DTO)

[(2)条件查询相关 DTO: 涵盖单条件精确查询、多条件组合查询、模糊查询、范围查询等常见场景](#(2)条件查询相关 DTO: 涵盖单条件精确查询、多条件组合查询、模糊查询、范围查询等常见场景)

[(3)响应 DTO](#(3)响应 DTO)

[3. MapStruct 映射接口](#3. MapStruct 映射接口)

[4. Mapper 接口(MyBatis-Plus)](#4. Mapper 接口(MyBatis-Plus))

[5. Service 层(业务逻辑)](#5. Service 层(业务逻辑))

[(1)Service 接口](#(1)Service 接口)

[(2)Service 实现类](#(2)Service 实现类)

[6. Controller 层(接口暴露)](#6. Controller 层(接口暴露))

四、关键配置

[1. MyBatis-Plus 配置](#1. MyBatis-Plus 配置)

[2. application.yml 配置](#2. application.yml 配置)


基于 Spring Boot + MyBatis-Plus + MapStruct 的完整 CRUD 实现,包含实体、DTO、映射、Service、Controller 全流程。

一、环境依赖(pom.xml 核心依赖)

复制代码
<!-- Spring Boot Web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- MyBatis-Plus -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>

<!-- MapStruct(DTO与Entity映射) -->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.5.5.Final</version>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.5.5.Final</version>
    <scope>provided</scope>
</dependency>

<!-- 数据库驱动(以MySQL为例) -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

<!-- Lombok(简化实体类) -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

二、数据库表设计(以sys_user为例)

复制代码
CREATE TABLE `sys_user` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `username` varchar(50) NOT NULL COMMENT '用户名',
  `password` varchar(100) NOT NULL COMMENT '密码',
  `email` varchar(100) DEFAULT NULL COMMENT '邮箱',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

三、核心代码实现

1. 实体类(Entity)

与数据库表字段一一对应,用于 MyBatis-Plus 操作数据库

复制代码
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;

@Data
@TableName("sys_user")
public class UserEntity {
    @TableId(type = IdType.AUTO)
    private Long id;                // 主键ID
    private String username;        // 用户名
    private String password;        // 密码
    private String email;           // 邮箱
    private LocalDateTime createTime; // 创建时间
    private LocalDateTime updateTime; // 更新时间
}

2. DTO 类(数据传输对象)

用于接收前端请求参数和返回响应数据,避免直接暴露 Entity。

(1)新增 / 修改请求 DTO
复制代码
// 新增用户请求DTO
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Email;
import javax.validation.constraints.Size;

@Data
public class UserAddDTO {
    @NotBlank(message = "用户名不能为空")
    @Size(max = 50, message = "用户名长度不能超过50字符")
    private String username;        // 用户名

    @NotBlank(message = "密码不能为空")
    @Size(min = 6, max = 20, message = "密码长度需在6-20字符之间")
    private String password;        // 密码

    @Email(message = "邮箱格式不正确")
    private String email;           // 邮箱
}

// 修改用户请求DTO
@Data
public class UserUpdateDTO {
    @NotBlank(message = "用户ID不能为空")
    private Long id;                // 主键ID(修改需传)

    @Size(max = 50, message = "用户名长度不能超过50字符")
    private String username;        // 用户名(可选修改)

    @Size(min = 6, max = 20, message = "密码长度需在6-20字符之间")
    private String password;        // 密码(可选修改)

    @Email(message = "邮箱格式不正确")
    private String email;           // 邮箱(可选修改)
}
(2)条件查询相关 DTO: 涵盖单条件精确查询、多条件组合查询、模糊查询、范围查询等常见场景
复制代码
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Pattern;
import java.time.LocalDateTime;
import java.util.List;

/**
 * 用户表条件查询DTO
 * 支持:分页参数、模糊查询、精确查询、范围查询、多值查询、排序参数
 * 说明:所有参数均为可选,不传则不参与筛选
 */
@Data
public class UserQueryDTO {

    // ======================== 分页参数(用于分页查询,非分页查询可忽略) ========================
    /**
     * 页码(默认1,最小1)
     */
    @Min(value = 1, message = "页码不能小于1")
    private Integer pageNum = 1;

    /**
     * 每页条数(默认10,最小1,最大100)
     */
    @Min(value = 1, message = "每页条数不能小于1")
    @Max(value = 100, message = "每页条数不能大于100")
    private Integer pageSize = 10;


    // ======================== 模糊查询条件 ========================
    /**
     * 用户名(模糊匹配,例如输入"张"可查询所有姓张的用户)
     */
    private String username;

    /**
     * 邮箱(模糊匹配,例如输入"example.com"可查询所有该域名的邮箱)
     * 格式要求:为空或符合邮箱格式(如xxx@xxx.com)
     */
    @Pattern(regexp = "^$|^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$", message = "邮箱格式不正确(可为空)")
    private String email;


    // ======================== 精确查询条件 ========================
    /**
     * 用户状态(精确匹配:1-正常,2-禁用,不传则不筛选)
     */
    private Integer status;


    // ======================== 范围查询条件 ========================
    /**
     * 创建时间起始(>=,格式:yyyy-MM-dd HH:mm:ss,例如"2023-01-01 00:00:00")
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTimeStart;

    /**
     * 创建时间结束(<,格式:yyyy-MM-dd HH:mm:ss,例如"2023-12-31 23:59:59")
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTimeEnd;

    /**
     * 用户ID范围起始(>=,例如100表示查询ID>=100的用户)
     */
    private Long idStart;

    /**
     * 用户ID范围结束(<=,例如200表示查询ID<=200的用户)
     */
    private Long idEnd;


    // ======================== 多值查询条件 ========================
    /**
     * 用户ID列表(精确匹配列表中的ID,例如[1,2,3]表示查询ID为1、2、3的用户)
     */
    private List<Long> idList;

    /**
     * 排除的用户ID列表(排除列表中的ID,例如[4,5,6]表示查询ID不是4、5、6的用户)
     */
    private List<Long> excludeIdList;


    // ======================== 排序参数 ========================
    /**
     * 排序字段(默认createTime,可选值:id、username、createTime)
     */
    @Pattern(regexp = "^$|id|username|createTime", message = "排序字段只能是id、username、createTime")
    private String sortField = "createTime";

    /**
     * 排序方式(asc-升序,desc-降序,默认desc)
     */
    @Pattern(regexp = "^(asc|desc)$", message = "排序方式只能是asc或desc")
    private String sortOrder = "desc";
}
(3)响应 DTO
复制代码
// 用户响应DTO(前端展示用,隐藏敏感字段如密码)
import lombok.Data;
import java.time.LocalDateTime;

@Data
public class UserRespDTO {
    private Long id;                // 主键ID
    private String username;        // 用户名
    private String email;           // 邮箱
    private LocalDateTime createTime; // 创建时间
}

3. MapStruct 映射接口

用于 DTO 与 Entity 之间的自动转换,替代手动BeanUtils.copyProperties

复制代码
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;

// 映射接口,componentModel = "spring"表示交给Spring管理
@Mapper(componentModel = "spring")
public interface UserMapper {
    // 单例实例(也可通过Spring注入)
    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    // UserAddDTO -> UserEntity(新增时转换)
    UserEntity addDtoToEntity(UserAddDTO dto);

    // UserUpdateDTO -> UserEntity(更新时转换,覆盖已有字段)
    void updateDtoToEntity(UserUpdateDTO dto, @MappingTarget UserEntity entity);

    // UserEntity -> UserRespDTO(查询响应时转换)
    UserRespDTO entityToRespDto(UserEntity entity);
}

4. Mapper 接口(MyBatis-Plus)

继承BaseMapper,无需手动写 SQL(简单 CRUD)。

复制代码
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;

@Repository
public interface UserMapper extends BaseMapper<UserEntity> {
    // 复杂查询可在此添加注解SQL或XML映射
}

5. Service 层(业务逻辑)

(1)Service 接口
复制代码
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;

public interface UserService extends IService<UserEntity> {
    // 新增用户
    boolean addUser(UserAddDTO dto);

    // 修改用户
    boolean updateUser(UserUpdateDTO dto);

    // 根据ID删除用户
    boolean removeUserById(Long id);

    // 根据ID查询用户
    UserRespDTO getUserById(Long id);

    // 查询所有用户
    List<UserRespDTO> listAllUsers();
}
(2)Service 实现类
复制代码
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements UserService {

    @Autowired
    private UserMapper userMapper; // MapStruct映射接口

    @Override
    public boolean addUser(UserAddDTO dto) {
        // DTO转Entity
        UserEntity entity = userMapper.addDtoToEntity(dto);
        // 保存到数据库
        return save(entity);
    }

    @Override
    public boolean updateUser(UserUpdateDTO dto) {
        // 先查询原实体
        UserEntity entity = getById(dto.getId());
        if (entity == null) {
            return false; // 用户不存在
        }
        // DTO数据更新到Entity
        userMapper.updateDtoToEntity(dto, entity);
        // 保存更新
        return updateById(entity);
    }

    @Override
    public boolean removeUserById(Long id) {
        return removeById(id);
    }

    @Override
    public UserRespDTO getUserById(Long id) {
        UserEntity entity = getById(id);
        return entity != null ? userMapper.entityToRespDto(entity) : null;
    }

    @Override
    public List<UserRespDTO> listAllUsers() {
        List<UserEntity> entityList = list();
        // 批量Entity转DTO
        return entityList.stream()
                .map(userMapper::entityToRespDto)
                .collect(Collectors.toList());
    }
}

6. Controller 层(接口暴露)

接收前端请求,调用 Service 处理,返回响应结果。

复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequestMapping("/api/users")
public class UserController {

    @Autowired
    private UserService userService;

    // 新增用户
    @PostMapping
    public ResponseEntity<Boolean> addUser(@Validated @RequestBody UserAddDTO dto) {
        boolean success = userService.addUser(dto);
        return new ResponseEntity<>(success, HttpStatus.CREATED);
    }

    // 修改用户
    @PutMapping
    public ResponseEntity<Boolean> updateUser(@Validated @RequestBody UserUpdateDTO dto) {
        boolean success = userService.updateUser(dto);
        return ResponseEntity.ok(success);
    }

    // 删除用户
    @DeleteMapping("/{id}")
    public ResponseEntity<Boolean> removeUser(@PathVariable Long id) {
        boolean success = userService.removeUserById(id);
        return ResponseEntity.ok(success);
    }

    // 根据ID查询用户
    @GetMapping("/{id}")
    public ResponseEntity<UserRespDTO> getUserById(@PathVariable Long id) {
        UserRespDTO dto = userService.getUserById(id);
        return dto != null ? ResponseEntity.ok(dto) : ResponseEntity.notFound().build();
    }

    // 查询所有用户
    @GetMapping
    public ResponseEntity<List<UserRespDTO>> listAllUsers() {
        List<UserRespDTO> dtoList = userService.listAllUsers();
        return ResponseEntity.ok(dtoList);
    }
}
@Validated 分组校验

新增用户时无需校验 id ,修改时必须校验 id ,可通过分组实现:

7. 全局异常处理(捕获校验失败信息)

添加全局异常处理器,统一返回校验失败的提示,避免直接返回 400 原始错误:

复制代码
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

/**
 * 全局异常处理器:处理参数校验失败异常
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 捕获 @Validated 分组校验失败的异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Map<String, String> handleValidationExceptions(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        // 获取所有校验失败的字段信息
        BindingResult bindingResult = ex.getBindingResult();
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            // 字段名 -> 错误提示
            errors.put(fieldError.getField(), fieldError.getDefaultMessage());
        }
        return errors;
    }
}

校验失败示例响应:

复制代码
{
  "username": "用户名不能为空",
  "password": "密码长度需在6-20字符之间"
}

四、关键配置

1. MyBatis-Plus 配置

复制代码
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("com.example.demo.mapper") // Mapper接口所在包
public class MyBatisPlusConfig {

    // 分页插件(可选,用于复杂查询分页)
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

2. application.yml 配置

复制代码
spring:
  # 数据库配置
  datasource:
    url: jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis-plus:
  # 实体类包名(用于自动扫描)
  type-aliases-package: com.example.demo.entity
  #  mapper.xml文件路径(若有自定义SQL)
  mapper-locations: classpath:mapper/*.xml
  # 日志配置(打印SQL,开发环境用)
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
相关推荐
海盗猫鸥2 小时前
「C++」vector的使用及接口模拟详解
开发语言·c++
wjs20242 小时前
CSS 下拉菜单:设计与实践指南
开发语言
天道有情战天下2 小时前
Lua使用
开发语言·lua
爱分享的鱼鱼2 小时前
Java基础(六:线程、线程同步,线程池)
java·后端
随便叫个啥呢2 小时前
java使用poi-tl模版+vform自定义表单生成word,使用LibreOffice导出为pdf,批量下载为压缩文件
java·pdf·word·zip
CodeCraft Studio2 小时前
国产化Word处理控件Spire.Doc教程:使用Java将RTF文件转换为PDF的全面教程
java·pdf·word·spire.doc·rtf转pdf·文件格式转换·文档开发sdk
随便叫个啥呢2 小时前
java使用poi-tl模版+vform自定义表单生成word
java·word·poi-tl
开心星人3 小时前
Leetcode hot100 Java刷题(二)
java·算法·leetcode
喵手3 小时前
Java与Microservices架构的结合:构建可扩展、高可用的系统!
java·架构·华为云