web-第7次课后作业-2

初步了解Mybatis

MyBatis 入门项目文档

一、项目概述

这是一个 Spring Boot 3 + MyBatis 3 的入门学习项目,演示如何用最简单的方式操作 SQL Server 数据库。

具体包括:MyBatis 是什么、怎么连接数据库、怎么做增删改查(CRUD)、MyBatis 帮你省掉了什么工作。


二、技术栈介绍

技术 版本 它的作用(新手友好解释)
Spring Boot 3.2.0 Java 项目的"管家",帮你管理依赖、启动应用、注入对象
MyBatis 3.0.3 一个持久层框架,让你用"声明接口"的方式操作数据库,不用手写 JDBC
SQL Server --- 微软的关系型数据库
Maven --- 包管理工具,自动下载依赖的 jar 包
Java 21 编程语言
JUnit 5 --- 单元测试框架(随 spring-boot-starter-test 引入)

三、项目文件结构

复制代码
pj4/
├── pom.xml                             # Maven 配置:声明依赖
├── init.sql                            # 数据库初始化脚本
├── mvnw / mvnw.cmd                     # Maven Wrapper(无需本地装 Maven)
├── src/
│   ├── main/
│   │   ├── java/com/example/pj4/
│   │   │   ├── Pj4Application.java     # Spring Boot 启动入口
│   │   │   ├── pojo/
│   │   │   │   └── User.java           # 实体类(POJO):一张表对应一个类
│   │   │   └── mapper/
│   │   │       └── UserMapper.java     # MyBatis Mapper 接口:定义数据库操作
│   │   └── resources/
│   │       └── application.properties  # 数据库连接配置
│   └── test/java/com/example/pj4/
│       └── Pj4ApplicationTests.java    # 单元测试
└── target/                             # 编译输出目录(自动生成,不用管)

四、各文件详解

4.1 pom.xml(Maven 依赖配置)

xml 复制代码
<dependencies>
    <!-- MyBatis 起步依赖:引入后就能用 MyBatis 的全部功能 -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>3.0.3</version>
    </dependency>

    <!-- SQL Server 驱动:Java 连接 SQL Server 必须的驱动包 -->
    <dependency>
        <groupId>com.microsoft.sqlserver</groupId>
        <artifactId>mssql-jdbc</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!-- Spring Boot 测试依赖:提供 JUnit、Spring Test 等 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

说明mybatis-spring-boot-starter 是核心------它内部已经包含了 MyBatis 核心包和 MyBatis-Spring 整合包,引入这一个依赖就能用 MyBatis。


4.2 init.sql(数据库初始化脚本)

在 SQL Server 中执行这个脚本,它会自动创建数据库和测试数据:

sql 复制代码
-- 创建数据库 mybatis
CREATE DATABASE [mybatis];

-- 切换到该数据库
USE [mybatis];

-- 创建 user 表(ID 自增)
CREATE TABLE [user] (
    id     INT IDENTITY(1,1) PRIMARY KEY,   -- 自增主键,从 1 开始每次 +1
    name   NVARCHAR(100) NULL,              -- 支持中文的字符串
    age    TINYINT NULL,
    gender TINYINT NULL,                    -- 1=男, 2=女
    phone  NVARCHAR(11) NULL
);

-- 插入 6 条测试数据(《倚天屠龙记》角色)
INSERT INTO [user] (name, age, gender, phone) VALUES (N'白眉鹰王', 55, 1, '18800000000');
INSERT INTO [user] (name, age, gender, phone) VALUES (N'金毛狮王', 45, 1, '18800000001');
INSERT INTO [user] (name, age, gender, phone) VALUES (N'青翼蝠王', 38, 1, '18800000002');
INSERT INTO [user] (name, age, gender, phone) VALUES (N'紫衫龙王', 42, 2, '18800000003');
INSERT INTO [user] (name, age, gender, phone) VALUES (N'光明左使', 37, 1, '18800000004');
INSERT INTO [user] (name, age, gender, phone) VALUES (N'光明右使', 48, 1, '18800000005');

4.3 application.properties(数据库连接配置)

properties 复制代码
spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.url=jdbc:sqlserver://localhost:1433;databaseName=mybatis;encrypt=false;trustServerCertificate=true
spring.datasource.username=sa
spring.datasource.password=qwe061003

每一行的含义

配置项 含义
driver-class-name 告诉 Java 用哪个数据库驱动(这里是 SQL Server 驱动)
url 数据库地址:localhost:1433 是本机 1433 端口,databaseName=mybatis 是数据库名。encrypt=falsetrustServerCertificate=true 是开发环境关闭 SSL 加密,生产环境不能这样写
username / password 数据库登录账号密码

你不需要写任何获取连接的代码。Spring Boot 读到这些配置后会自动创建数据库连接池(默认 HikariCP),MyBatis 会从这个池里拿连接。


4.4 Pj4Application.java(启动类)

java 复制代码
@SpringBootApplication                                        // 标记这是 Spring Boot 应用
public class Pj4Application {
    public static void main(String[] args) {
        SpringApplication.run(Pj4Application.class, args);    // 启动应用
    }
}

@SpringBootApplication 是干什么的? 它是三个注解的"快捷方式":

  • @Configuration:允许定义 Bean(Spring 管理的对象)
  • @EnableAutoConfiguration:自动配置------Spring Boot 读到 pom.xml 里引入了 MyBatis,就自动帮你配好 MyBatis
  • @ComponentScan:自动扫描当前包及子包下的所有组件(包括你的 @Mapper 接口)

4.5 User.java(实体类 / POJO)

java 复制代码
public class User {
    private Integer id;      // 数据库 int → Java Integer
    private String name;     // 数据库 nvarchar → Java String
    private Short age;       // 数据库 tinyint → Java Short
    private Short gender;
    private String phone;

    // 无参构造方法(MyBatis 反射创建对象时需要)
    // getter / setter(MyBatis 通过它们读写属性值)
    // toString()(打印输出用)
}

重要概念 :数据库的"一行"对应 Java 的"一个 User 对象",数据库的"列"对应 User 对象的"属性"。这个类就叫 POJO(Plain Old Java Object),也叫实体类(Entity)。


4.6 UserMapper.java(MyBatis Mapper 接口)------ 核心重点

java 复制代码
@Mapper                              // ① 告诉 MyBatis:这个接口我来接管
public interface UserMapper {

    @Select("SELECT id, name, age, gender, phone FROM [user]")
    List<User> list();               // ② 查询全部

    @Select("SELECT id, name, age, gender, phone FROM [user] WHERE id = #{id}")
    User getById(Integer id);        // ③ 按 ID 查询

    @Insert("INSERT INTO [user] (name, age, gender, phone) VALUES (#{name}, #{age}, #{gender}, #{phone})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    void insert(User user);          // ④ 新增

    @Update("UPDATE [user] SET name=#{name}, age=#{age}, gender=#{gender}, phone=#{phone} WHERE id=#{id}")
    void update(User user);          // ⑤ 更新

    @Delete("DELETE FROM [user] WHERE id=#{id}")
    void delete(Integer id);         // ⑥ 删除
}
逐行详解

@Mapper

告诉 Spring + MyBatis:"别看我只有一个接口声明,我会在运行时自动生成一个实现类来代理你。"

② 查询全部

  • @Select:里面写 SQL 语句
  • 返回类型 List<User>:MyBatis 自动把查出来的多行数据映射成 User 对象的列表
  • 因为数据库的列名(id, name, age, gender, phone)和 User 类的属性名完全一致,所以 MyBatis 会自动映射,不需要额外配置

③ 按 ID 查询

  • #{id}:这是 MyBatis 的参数占位符 ,相当于 JDBC 的 ?。MyBatis 会把方法参数 Integer id 的值填到这里
  • 安全#{ } 使用的是 PreparedStatement 参数绑定,不会有 SQL 注入风险

④ 新增

  • #{name}, #{age} 等:参数是一个 User 对象,MyBatis 会自动从对象里取 getName()getAge()
  • @Options(useGeneratedKeys = true, keyProperty = "id"):因为 [user] 表的 id 是自增列,插入时不需要提供 id。插入成功后,数据库生成的 id 值会自动回填 到传入的 User 对象的 id 属性上

⑤ 更新

  • 根据 User 对象的 id 找到对应行,把其他字段更新为对象中的值

⑥ 删除

  • 根据 id 删除对应行
MyBatis 注解速查表
注解 用途 对应 SQL
@Select 查询 SELECT ...
@Insert 插入 INSERT INTO ...
@Update 更新 UPDATE ...
@Delete 删除 DELETE FROM ...
@Options 附加选项(如主键回填) ---
#{} vs ${}(重要!)
写法 底层原理 安全性 使用场景
#{xxx} PreparedStatement? 占位符 安全,防 SQL 注入 始终优先使用
${xxx} 直接拼字符串到 SQL 里 不安全,可能被注入 只在需要动态表名/列名时用
MyBatis 是怎么帮你的?(对比 JDBC)
复制代码
你写的代码:                      MyBatis 帮你做的:
───────────────────────────────────────────────────
import org.apache.ibatis.         → 不需要 import JDBC 任何东西
annotations.*;

@Mapper                           → 不需要 Class.forName 加载驱动
public interface UserMapper {     → 不需要 DriverManager.getConnection
                                  → 不需要 try-finally 关闭连接
    @Select("SELECT ...")         → 不需要 PreparedStatement
    List<User> list();            → 不需要 ResultSet
                                  → 不需要 while(rs.next())
}                                 → 不需要 rs.getInt("id") ...
                                  → MyBatis 通过"反射"调用 setId/getName
                                    自动完成列→属性的映射

一句话:你只需定义一个接口、写一个 SQL 注解,MyBatis 自动生成实现类,帮你完成从"接口调用"到"返回 Java 对象"的全过程。


4.7 Pj4ApplicationTests.java(单元测试)

java 复制代码
@SpringBootTest      // 启动完整的 Spring 上下文,会连接真实数据库
@Transactional       // 每个测试方法执行后自动回滚,数据库不留痕迹
public class Pj4ApplicationTests {

    @Autowired
    private UserMapper userMapper;   // Spring 自动注入 MyBatis 生成的代理对象

    @Test
    public void testList() { ... }      // 测试查询全部
    @Test
    public void testGetById() { ... }   // 测试按 ID 查询
    @Test
    public void testInsert() { ... }    // 测试新增(看 id 是否回填)
    @Test
    public void testUpdate() { ... }    // 测试更新(看修改是否生效)
    @Test
    public void testDelete() { ... }    // 测试删除(看总数是否减少)
}

@Autowired 干了什么?

UserMapper 明明是个接口(没有实现类),但 MyBatis 在启动时:

  1. 用**动态代理(JDK Proxy)**生成了一个代理对象
  2. 代理对象里封装了 "根据注解拿 SQL → 获取连接 → 执行 → 映射结果" 的全套逻辑
  3. Spring 把这个代理对象注入到 userMapper 变量里
  4. 你调用 userMapper.list(),实际调用的是代理对象

@Transactional 的作用

每个测试方法执行时会开启一个数据库事务,方法结束后自动回滚。所以测试中的新增/更新/删除不会真正修改数据库数据。


五、运行结果

执行 ./mvnw test 后的输出:

text 复制代码
=== 查询全部 ===
User{id=1, name='白眉鹰王', age=55, gender=1, phone='18800000000'}
User{id=2, name='金毛狮王', age=45, gender=1, phone='18800000001'}
User{id=3, name='青翼蝠王', age=38, gender=1, phone='18800000002'}
User{id=4, name='紫衫龙王', age=42, gender=2, phone='18800000003'}
User{id=5, name='光明左使', age=37, gender=1, phone='18800000004'}
User{id=6, name='光明右使', age=48, gender=1, phone='18800000005'}

=== 按ID查询 ===
User{id=1, name='白眉鹰王', age=55, gender=1, phone='18800000000'}

=== 新增用户 ===
插入后返回的ID: 8                              ← 注意:ID 被自动回填了!
User{id=8, name='张无忌', age=22, gender=1, phone='19900000000'}

=== 更新前 ===
User{id=1, name='白眉鹰王', age=55, gender=1, phone='18800000000'}
=== 更新后 ===
User{id=1, name='殷天正', age=60, gender=1, phone='18800000000'}  ← 名字和年龄均已更新

=== 删除前,总数: 7 ===
=== 删除后,总数: 6 ===
text 复制代码
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS

六、数据流转全景图

复制代码
┌──────────────────────────────────────────────────────────────────┐
│  测试代码                                                         │
│  userMapper.list()  ← 你只写了一行方法调用                          │
└─────────────────────┬────────────────────────────────────────────┘
                      │
                      ▼
┌──────────────────────────────────────────────────────────────────┐
│  MyBatis 动态代理对象(框架自动生成)                                │
│  1. 从 @Select 拿到 SQL: "SELECT id,name,age,gender,phone FROM    │
│     [user]"                                                       │
│  2. 从连接池拿一个 Connection(HikariCP 管理)                      │
│  3. 创建 PreparedStatement,填入 #{参数} 的值                      │
│  4. 执行 executeQuery()                                           │
│  5. 遍历 ResultSet,每行创建一个 User 对象                          │
│  6. 列名 "id" → 调用 user.setId(),列名 "name" → user.setName()... │
│  7. 归还 Connection 到连接池                                       │
│  8. 返回 List<User>                                               │
└─────────────────────┬────────────────────────────────────────────┘
                      │
                      ▼
┌──────────────────────────────────────────────────────────────────┐
│  SQL Server 数据库                                                │
│  [user] 表 → 6行数据 → 通过网络返回给 Java                          │
└──────────────────────────────────────────────────────────────────┘

你只需关心 写 SQL 注解调用方法 两个动作,中间所有 JDBC 的脏活累活 MyBatis 全部代劳。


七、常见问题

Q1:@Mapper@Repository 有什么区别?

@Mapper 是 MyBatis 专属注解,告诉 MyBatis 为这个接口生成代理实现。@Repository 是 Spring 的通用注解,MyBatis 不识别它。

Q2:如果数据库列名和 Java 属性名不一致怎么办?

比如数据库叫 user_name,Java 叫 userName。两种解决方式:

  • application.properties 中设置 mybatis.configuration.map-underscore-to-camel-case=true(自动下划线转驼峰)
  • 在 SQL 中取别名:SELECT user_name AS userName FROM [user]

Q3:注解方式和 XML 方式用哪个?

  • 注解:适合简单 SQL,代码紧凑,本项目用的就是这种方式
  • XML:适合复杂 SQL(多表联合、动态条件),SQL 与 Java 代码分离
  • 两种方式可以混用

Q4:我怎么知道 MyBatis 真的执行了哪些 SQL?

application.properties 中加一行:

properties 复制代码
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

这样 MyBatis 执行的每条 SQL 和参数都会打印在控制台。