一、前言
前面三篇我们已经完成了:
- Spring Boot 项目创建
- Controller 接口开发
- DTO 参数接收与校验
- Service 分层
- Result 统一返回
- 全局异常处理
- 日志
但目前还有一个核心问题:
数据没有真正落库
也就是说,现在的接口只是"能跑",但还不是真正的后端系统。
本篇开始,我们接入:
MySQL + MyBatis
把项目真正打通成:
Controller → Service → Mapper → MySQL
二、本篇目标
实现一个最小用户中心闭环:
用户注册(入库)
根据 ID 查询用户
根据 ID 删除用户
技术栈:
Spring Boot + MyBatis + MySQL
三、先加依赖
在 pom.xml 中加入这几个依赖。
html
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
四、配置 application.yml
html
server:
port: 8080
spring:
application:
name: ark-backend
datasource:
url: jdbc:mysql://localhost:3306/user_center?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf-8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: org.example.arkbackend.entity
logging:
level:
org.example.arkbackend: debug
五、先建数据库和表
1. 建库
sql
CREATE DATABASE user_center DEFAULT CHARACTER SET utf8mb4;
2. 建表
sql
CREATE TABLE user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(64) NOT NULL,
password VARCHAR(128) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
六、项目结构调整
这一篇完成后,推荐最小结构如下:
org.example.arkbackend
├── common
│ └── Result.java
├── controller
│ └── UserController.java
├── dto
│ └── UserRegisterDTO.java
├── entity
│ └── User.java
├── exception
│ └── GlobalExceptionHandler.java
├── mapper
│ └── UserMapper.java
├── service
│ ├── UserService.java
│ └── impl
│ └── UserServiceImpl.java
└── ArkBackendApplication.java
resources 目录下新增:
resources
└── mapper
└── UserMapper.xml
七、定义实体类
新建:
entity/User.java
java
package org.example.arkbackend.entity;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class User {
private Long id;
private String username;
private String password;
private LocalDateTime createTime;
}
八、DTO 继续沿用
java
package org.example.arkbackend.dto;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class UserRegisterDTO {
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
private String password;
}
九、定义 Mapper 接口
新建:
mapper/UserMapper.java
java
package org.example.arkbackend.mapper;
import org.example.arkbackend.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface UserMapper {
int insert(User user);
User selectById(@Param("id") Long id);
int deleteById(@Param("id") Long id);
User selectByUsername(@Param("username") String username);
}
十、编写 Mapper XML
新建:
resources/mapper/UserMapper.xml
XML
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.arkbackend.mapper.UserMapper">
<resultMap id="UserResultMap" type="org.example.arkbackend.entity.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="createTime" column="create_time"/>
</resultMap>
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (username, password)
VALUES (#{username}, #{password})
</insert>
<select id="selectById" resultMap="UserResultMap">
SELECT id, username, password, create_time
FROM user
WHERE id = #{id}
</select>
<delete id="deleteById">
DELETE FROM user
WHERE id = #{id}
</delete>
<select id="selectByUsername" resultMap="UserResultMap">
SELECT id, username, password, create_time
FROM user
WHERE username = #{username}
LIMIT 1
</select>
</mapper>
十一、Service 接口
java
package org.example.arkbackend.service;
import org.example.arkbackend.dto.UserRegisterDTO;
import org.example.arkbackend.entity.User;
public interface UserService {
String register(UserRegisterDTO dto);
User getUserById(Long id);
void deleteUser(Long id);
}
十二、Service 实现类
java
package org.example.arkbackend.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.example.arkbackend.dto.UserRegisterDTO;
import org.example.arkbackend.entity.User;
import org.example.arkbackend.mapper.UserMapper;
import org.example.arkbackend.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public String register(UserRegisterDTO dto) {
log.info("开始注册用户:{}", dto.getUsername());
User existed = userMapper.selectByUsername(dto.getUsername());
if (existed != null) {
throw new RuntimeException("用户名已存在");
}
User user = new User();
user.setUsername(dto.getUsername());
user.setPassword(dto.getPassword());
userMapper.insert(user);
return "注册成功,用户ID:" + user.getId();
}
@Override
public User getUserById(Long id) {
log.info("查询用户:{}", id);
User user = userMapper.selectById(id);
if (user == null) {
throw new RuntimeException("用户不存在");
}
return user;
}
@Override
public void deleteUser(Long id) {
log.info("删除用户:{}", id);
int rows = userMapper.deleteById(id);
if (rows == 0) {
throw new RuntimeException("用户不存在,删除失败");
}
}
}
十三、Controller 改造
java
package org.example.arkbackend.controller;
import jakarta.validation.Valid;
import org.example.arkbackend.common.Result;
import org.example.arkbackend.dto.UserRegisterDTO;
import org.example.arkbackend.entity.User;
import org.example.arkbackend.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/register")
public Result<String> register(@Valid @RequestBody UserRegisterDTO dto) {
return Result.success(userService.register(dto));
}
@GetMapping("/{id}")
public Result<User> getUser(@PathVariable Long id) {
return Result.success(userService.getUserById(id));
}
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable Long id) {
userService.deleteUser(id);
return Result.success(null);
}
}
十四、统一返回结构
java
package org.example.arkbackend.common;
import lombok.Data;
@Data
public class Result<T> {
private int code;
private String message;
private T data;
public static <T> Result<T> success(T data) {
Result<T> r = new Result<>();
r.setCode(0);
r.setMessage("成功");
r.setData(data);
return r;
}
public static <T> Result<T> fail(String message) {
Result<T> r = new Result<>();
r.setCode(1);
r.setMessage(message);
r.setData(null);
return r;
}
}
十五、全局异常处理
java
package org.example.arkbackend.exception;
import org.example.arkbackend.common.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public Result<?> handleException(Exception e) {
return Result.fail(e.getMessage());
}
}
十六、如何测试
1. 注册用户
POST /user/register
Content-Type: application/json
{
"username": "test",
"password": "123456"
}
返回示例:
{
"code": 0,
"message": "成功",
"data": "注册成功,用户ID:1"
}
2. 查询用户
GET /user/1
返回示例:
{
"code": 0,
"message": "成功",
"data": {
"id": 1,
"username": "test",
"password": "123456",
"createTime": "2026-04-16T10:30:00"
}
}
3. 删除用户
DELETE /user/1
返回示例:
{
"code": 0,
"message": "成功",
"data": null
}
十七、这一篇你真正掌握了什么
你现在打通的是:
Controller → Service → Mapper → MySQL
这意味着你已经从:会写接口
进入到了:会做最小后端项目
这一层非常关键。
十八、常见坑总结
1. 启动报数据库错误
原因通常是:
- MySQL 没启动
- 数据库不存在
- 用户名密码错误
- 没加 MySQL 驱动
2. Mapper 找不到
检查:
UserMapper.xml路径是否在resources/mappernamespace是否和接口全限定名一致application.yml中mapper-locations是否正确
3. 表字段映射不上
重点看:
create_time ↔ createTime
这种场景要么写 resultMap,要么开驼峰映射。
十九、一句话总结
MyBatis + MySQL 的接入,本质是把接口层和真实数据打通,让 Spring Boot 项目从"演示代码"变成"真正系统"
下一篇:
Spring Boot 实战(五):接口工程化升级(统一返回 + 异常处理 + 错误码体系 + 异常流转机制)
目标不是再加新功能,而是把"能跑的项目"升级成一个更像真实项目的工程化版本。