【数据篇】持久化核心:整合 JPA/MyBatis 实现优雅的数据库操作

系列回顾:

在上一篇中,我们成功搭建了第一个 Spring Boot 应用,并创建了一个简单的 "Hello World" API。但它就像一个没有记忆的大脑,每次重启后一切都将归零。真正的应用需要将数据持久化,以便随时存取和管理。今天,我们将为我们的应用装上这颗至关重要的"心脏"------数据库。

欢迎回来,未来的实战派开发者!

几乎所有的业务系统,无论是电商网站、社交平台还是企业内部系统,其核心都是对数据的增、删、改、查(CRUD)。如何优雅、高效地与数据库交互,是衡量一个后端开发者能力的重要标准。

幸运的是,Spring Boot 让这一切变得前所未有的简单。它完美地集成了两大主流的持久化框架:

  1. JPA (Java Persistence API): 一种 ORM (Object-Relational Mapping) 规范。它允许我们像操作普通 Java 对象一样操作数据库表,屏蔽了繁琐的 SQL 语句。对于新项目和快速开发,这是首选。
  2. MyBatis: 一个半自动的 SQL 映射框架。它能让我们自己编写和控制 SQL,同时又免去了 JDBC 的大量模板代码。对于需要精细优化 SQL 或对接复杂遗留数据库的场景,它更具优势。

本文将以 JPA 为主线,带你完整地走完整合数据库、实现 CRUD 的全过程。同时,我们也会提供 MyBatis 的整合方案作为对比和备选。


准备工作:搭建数据库环境

在开始编码前,你需要一个数据库。我们将使用世界上最流行的开源数据库------MySQL

  1. 安装 MySQL: 如果你还没有安装,可以参考官方文档或搜索相关教程进行安装。
  2. 创建数据库: 使用你喜欢的数据库客户端(如 DBeaver, Navicat, SQLyog)连接到 MySQL,并执行以下 SQL 命令来创建一个新的数据库和一张用于测试的用户表。
sql 复制代码
-- 创建一个名为 'springboot_db' 的数据库
CREATE DATABASE springboot_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 切换到该数据库
USE springboot_db;

-- 创建一张用户表
CREATE TABLE `user` (
  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `name` VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
  `email` VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- (可选) 插入一条测试数据
INSERT INTO `user` (name, email) VALUES ('Jack', '[email protected]');

好了,我们的数据"仓库"已经建好,现在开始写代码把它连接起来!


主线剧情:使用 Spring Data JPA 实现 CRUD

我们将通过 6 个清晰的步骤,为上一篇的 my-first-app 项目添加完整的数据库操作功能。

第一步:添加依赖 (pom.xml)

打开项目的 pom.xml 文件,在 <dependencies> 标签内,添加以下两个新的依赖:

xml 复制代码
<!-- Spring Data JPA 启动器 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- MySQL 连接驱动 -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>
  • spring-boot-starter-data-jpa: 这是 JPA 的核心启动器,它包含了操作数据库所需的一切,包括 JPA API、Hibernate 实现、Spring Data Commons 等。
  • mysql-connector-j: 这是连接 MySQL 数据库所必需的 JDBC 驱动。scope=runtime 表示这个依赖只在运行时需要。

添加完毕后,记得让 IDEA 刷新 Maven 依赖。

第二步:配置数据源 (application.properties)

打开 src/main/resources/application.properties 文件,添加以下数据库连接信息:

properties 复制代码
# -- Database Settings --
# 数据源 URL
spring.datasource.url=jdbc:mysql://localhost:3306/springboot_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
# 数据库用户名
spring.datasource.username=root
# 数据库密码 (请替换为你自己的密码)
spring.datasource.password=your_password

# -- JPA/Hibernate Settings --
# 让 Hibernate 根据实体类自动更新表结构 (开发时常用, 生产环境慎用)
spring.jpa.hibernate.ddl-auto=update
# 在控制台打印执行的 SQL 语句,方便调试
spring.jpa.show-sql=true
# 格式化打印的 SQL
spring.jpa.properties.hibernate.format_sql=true

配置解读:

  • spring.datasource.*: 这些是标准的数据源配置,告诉 Spring Boot 如何连接到你的 springboot_db 数据库。
  • spring.jpa.hibernate.ddl-auto=update: 这是一个神奇的配置。它会让 Hibernate(JPA 的一种实现)在应用启动时,对比你的实体类和数据库表结构,如果实体类有新增字段,它会自动在表中添加对应的列。create 会每次都删表重建,validate 只校验不修改,none 什么都不做。在开发阶段使用 update 非常方便,但在生产环境,请务必使用 validatenone,并通过数据库迁移工具(如 Flyway/Liquibase)管理表结构变更。
  • spring.jpa.show-sql=true: 让你能清楚地看到每次操作背后实际执行的 SQL 是什么,是调试的绝佳帮手。
第三步:创建实体类 (Entity)

实体类是 Java 世界里对数据库表的映射。在 com.example.myfirstapp 包下创建一个 entity 包,然后在其中创建 User.java 类:

java 复制代码
package com.example.myfirstapp.entity;

import jakarta.persistence.*;

@Entity                 // 告诉 JPA 这是一个实体类,它将映射到数据库中的一张表
@Table(name = "user")   // 指定映射的表名。如果省略,则默认为类名(小写)
public class User {

    @Id // 声明这是主键
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 主键生成策略,IDENTITY 表示自增
    private Long id;

    @Column(name = "name", length = 30) // 映射到表中的 'name' 列
    private String name;

    @Column(name = "email", length = 50) // 映射到表中的 'email' 列
    private String email;

    // --- Getter 和 Setter ---
    // (为了代码简洁,此处省略。请使用 IDE 自动生成 Getter 和 Setter)
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

通过简单的注解,我们就将一个普通的 Java Pojo 变成了能被 JPA 管理的实体。

第四步:创建仓库接口 (Repository)

Repository 是 Spring Data JPA 提供的最神奇的特性。你只需要定义一个接口,无需编写任何实现代码,就能获得一套完整的 CRUD 功能。

com.example.myfirstapp 包下创建一个 repository 包,然后在其中创建 UserRepository.java 接口:

java 复制代码
package com.example.myfirstapp.repository;

import com.example.myfirstapp.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
    // 这就是全部代码!
}
  • 我们让 UserRepository 继承了 JpaRepository
  • JpaRepository<User, Long> 两个泛型参数分别代表:这个 Repository 操作的实体类是 User,该实体类的主键类型是 Long

就这么简单!现在,UserRepository 已经自动拥有了 save(), findById(), findAll(), deleteById() 等一系列方法。Spring Data JPA 会在运行时为我们动态生成实现。

第五步:创建控制器 (Controller) 并实现 API

现在,我们来改造 controller,让它调用 UserRepository 来操作数据库。创建一个新的 UserController.java

java 复制代码
package com.example.myfirstapp.controller;

import com.example.myfirstapp.entity.User;
import com.example.myfirstapp.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/users") // 给这个 Controller 下的所有 API 添加一个 /users 的前缀
public class UserController {

    @Autowired // 自动注入 UserRepository 的实例
    private UserRepository userRepository;

    // 1. 新增用户
    @PostMapping("/add")
    public User addUser(@RequestBody User user) {
        return userRepository.save(user);
    }

    // 2. 根据 ID 查询用户
    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        return userRepository.findById(id).orElse(null); // orElse(null) 表示如果找不到就返回 null
    }

    // 3. 查询所有用户
    @GetMapping("/all")
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    // 4. 根据 ID 删除用户
    @DeleteMapping("/delete/{id}")
    public String deleteUserById(@PathVariable Long id) {
        userRepository.deleteById(id);
        return "User deleted successfully!";
    }
}

代码解读:

  • @Autowired: Spring 的依赖注入注解,它会自动帮我们找到 UserRepository 的实例并注入到 userController 中。
  • @RequestMapping("/users"): 定义了该控制器下所有接口的公共路径前缀。
  • @PostMapping("/add"): 处理 HTTP POST 请求,用于创建资源。@RequestBody 表示请求体中的 JSON 数据将被自动转换成 User 对象。
  • @GetMapping("/{id}"): 处理 HTTP GET 请求,用于查询资源。@PathVariable 表示 URL 路径中的 {id} 值将被绑定到方法的参数 id 上。
  • @DeleteMapping("/delete/{id}"): 处理 HTTP DELETE 请求,用于删除资源。
第六步:测试你的 API

重新启动你的 Spring Boot 应用。现在,我们需要一个 API 测试工具(如 PostmanInsomnia)来验证我们的接口。

  1. 新增用户 (POST)

    • URL: http://localhost:8080/users/add

    • Method: POST

    • Body (raw, JSON):

      json 复制代码
      {
        "name": "Alice",
        "email": "[email protected]"
      }
    • 响应: 你会看到返回了包含 id 的完整用户信息。

  2. 查询所有用户 (GET)

    • URL: http://localhost:8080/users/all
    • Method: GET
    • 响应: 你会看到一个包含 Jack 和 Alice 的 JSON 数组。
  3. 根据 ID 查询用户 (GET)

    • URL: http://localhost:8080/users/1 (假设 Jack 的 id 是 1)
    • Method: GET
    • 响应: 你会看到 Jack 的用户信息。
  4. 删除用户 (DELETE)

    • URL: http://localhost:8080/users/delete/1
    • Method: DELETE
    • 响应: User deleted successfully!
    • 再次查询所有用户,你会发现 Jack 已经不在了。

至此,你已经完全掌握了使用 Spring Data JPA 进行数据库操作的核心流程!


支线剧情:使用 MyBatis 实现 CRUD (快速对比)

如果你更喜欢手写 SQL,或者你的项目有此要求,整合 MyBatis 也同样简单。

  1. 添加依赖:pom.xml 中的 spring-boot-starter-data-jpa 替换为:

    xml 复制代码
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>3.0.3</version> <!-- 使用一个较新版本 -->
    </dependency>
  2. 配置 application.properties:

    properties 复制代码
    # (数据源配置不变)
    
    # MyBatis 配置
    mybatis.mapper-locations=classpath:mapper/*.xml # 指定 XML 映射文件的位置
    mybatis.configuration.map-underscore-to-camel-case=true # 开启下划线到驼峰的自动映射
  3. 创建 Mapper 接口: Repository 在 MyBatis 中被称为 Mapper。创建 UserMapper.java 接口。

    java 复制代码
    package com.example.myfirstapp.mapper;
    
    import com.example.myfirstapp.entity.User;
    import org.apache.ibatis.annotations.*;
    
    @Mapper // 告诉 Spring Boot 这是一个 MyBatis 的 Mapper 接口
    public interface UserMapper {
    
        @Select("SELECT * FROM user WHERE id = #{id}")
        User findById(Long id);
    
        @Insert("INSERT INTO user(name, email) VALUES(#{name}, #{email})")
        @Options(useGeneratedKeys = true, keyProperty = "id") // 获取自增主键
        void insert(User user);
    
        // ... 其他如 update, delete, findAll 等
    }

    你也可以将 SQL 语句写在 resources/mapper/UserMapper.xml 文件中,实现 SQL 与 Java 代码的解耦。

  4. 在 Controller 中使用:UserRepository 替换为 UserMapper 即可。

对比总结:

  • JPA: 几乎不用写 SQL,开发速度极快,更面向对象。
  • MyBatis: SQL 自由度高,易于优化,学习曲线更平缓。

两者没有绝对的好坏,选择哪个取决于你的团队技术栈和项目需求。


总结与展望

今天,我们为应用安装了"记忆",实现了最核心的数据持久化功能。你已经掌握了:

  • 如何配置 Spring Boot 连接到 MySQL 数据库。
  • 使用 Spring Data JPA,通过定义 Entity 和 Repository 接口,优雅地实现数据库 CRUD。
  • 使用 Postman 等工具测试后端的 RESTful API。
  • (了解了)整合 MyBatis 的基本方法。

现在,你的应用已经具备了一个真实业务系统的雏形。但是,当前的 API 还比较"粗糙":

  • 如果查询一个不存在的用户,会返回 null,前端可能看到一个空白页面。
  • 如果新增用户时不传 name,数据库会报错,而前端会收到一个不友好的 500 错误。
  • 每个接口的返回格式都不一样,有的是对象,有的是字符串。

如何让我们的 API 变得更专业、更健壮、更统一?

在下一篇文章 《【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验》 中,我们将着手解决这些问题,让你的应用具备"专业范儿"。我们下期见!

相关推荐
遗忘妳1 分钟前
mysql高可用
数据库·mysql
小灰灰搞电子14 分钟前
Qt多线程访问同一个数据库源码分享(基于Sqlite实现)
数据库·qt·sqlite
麓殇⊙17 分钟前
redis--黑马点评--Redisson快速入门
数据库·redis·缓存
爱思德学术22 分钟前
DBLP数据库是什么?
数据库
onlooker666625 分钟前
Go 语言底层(四) : 深入 Context 上下文
开发语言·数据库·golang
岁忧27 分钟前
MySQL中【正则表达式】用法
数据库·mysql·正则表达式
浠寒AI1 小时前
PostgreSQL 与 SQL 基础:为 Fast API 打下数据基础
数据库·sql·postgresql
金州饿霸1 小时前
MySQL--慢查询日志、日志分析工具mysqldumpslow
数据库·mysql
zhujilisa2 小时前
MySql中的锁
数据库·mysql
百度Geek说2 小时前
BaikalDB 架构演进实录:打造融合向量化与 MPP 的 HTAP 查询引擎
数据库·分布式·架构