web-第7次课后作业-2

初步了解Mybatis

MyBatis 入门项目文档

一、项目概述

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

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


二、技术栈介绍

技术版本它的作用(新手友好解释)
Spring Boot3.2.0Java 项目的"管家",帮你管理依赖、启动应用、注入对象
MyBatis3.0.3一个持久层框架,让你用"声明接口"的方式操作数据库,不用手写 JDBC
SQL Server微软的关系型数据库
Maven包管理工具,自动下载依赖的 jar 包
Java21编程语言
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 依赖配置)

<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 中执行这个脚本,它会自动创建数据库和测试数据:

-- 创建数据库 mybatisCREATEDATABASE[mybatis];-- 切换到该数据库USE[mybatis];-- 创建 user 表(ID 自增)CREATETABLE[user](idINTIDENTITY(1,1)PRIMARYKEY,-- 自增主键,从 1 开始每次 +1name NVARCHAR(100)NULL,-- 支持中文的字符串ageTINYINTNULL,genderTINYINTNULL,-- 1=男, 2=女phone NVARCHAR(11)NULL);-- 插入 6 条测试数据(《倚天屠龙记》角色)INSERTINTO[user](name,age,gender,phone)VALUES(N'白眉鹰王',55,1,'18800000000');INSERTINTO[user](name,age,gender,phone)VALUES(N'金毛狮王',45,1,'18800000001');INSERTINTO[user](name,age,gender,phone)VALUES(N'青翼蝠王',38,1,'18800000002');INSERTINTO[user](name,age,gender,phone)VALUES(N'紫衫龙王',42,2,'18800000003');INSERTINTO[user](name,age,gender,phone)VALUES(N'光明左使',37,1,'18800000004');INSERTINTO[user](name,age,gender,phone)VALUES(N'光明右使',48,1,'18800000005');

4.3 application.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=你猜

每一行的含义

配置项含义
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(启动类)

@SpringBootApplication// 标记这是 Spring Boot 应用publicclassPj4Application{publicstaticvoidmain(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)

publicclassUser{privateIntegerid;// 数据库 int → Java IntegerprivateStringname;// 数据库 nvarchar → Java StringprivateShortage;// 数据库 tinyint → Java ShortprivateShortgender;privateStringphone;// 无参构造方法(MyBatis 反射创建对象时需要)// getter / setter(MyBatis 通过它们读写属性值)// toString()(打印输出用)}

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


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

@Mapper// ① 告诉 MyBatis:这个接口我来接管publicinterfaceUserMapper{@Select("SELECT id, name, age, gender, phone FROM [user]")List<User>list();// ② 查询全部@Select("SELECT id, name, age, gender, phone FROM [user] WHERE id = #{id}")UsergetById(Integerid);// ③ 按 ID 查询@Insert("INSERT INTO [user] (name, age, gender, phone) VALUES (#{name}, #{age}, #{gender}, #{phone})")@Options(useGeneratedKeys=true,keyProperty="id")voidinsert(Useruser);// ④ 新增@Update("UPDATE [user] SET name=#{name}, age=#{age}, gender=#{gender}, phone=#{phone} WHERE id=#{id}")voidupdate(Useruser);// ⑤ 更新@Delete("DELETE FROM [user] WHERE id=#{id}")voiddelete(Integerid);// ⑥ 删除}
逐行详解

@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(单元测试)

@SpringBootTest// 启动完整的 Spring 上下文,会连接真实数据库@Transactional// 每个测试方法执行后自动回滚,数据库不留痕迹publicclassPj4ApplicationTests{@AutowiredprivateUserMapperuserMapper;// Spring 自动注入 MyBatis 生成的代理对象@TestpublicvoidtestList(){...}// 测试查询全部@TestpublicvoidtestGetById(){...}// 测试按 ID 查询@TestpublicvoidtestInsert(){...}// 测试新增(看 id 是否回填)@TestpublicvoidtestUpdate(){...}// 测试更新(看修改是否生效)@TestpublicvoidtestDelete(){...}// 测试删除(看总数是否减少)}

@Autowired干了什么?
UserMapper明明是个接口(没有实现类),但 MyBatis 在启动时:

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

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


五、运行结果

执行./mvnw test后的输出:

=== 查询全部 === 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 ===
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中加一行:

mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

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