阶段二:数据库与持久层(2周)
数据库是后端开发的核心基础。本阶段将学习MySQL数据库、JDBC以及主流ORM框架MyBatis。
2.1 数据库基础(第1-4天)
2.1.1 MySQL安装与配置
下载安装
- 下载MySQL:MySQL官网
- 选择Windows版本(MSI安装器推荐)
- 安装时设置root密码
- 配置环境变量:
Path添加C:\Program Files\MySQL\MySQL Server 8.0\bin
常用GUI工具
- Navicat(付费,功能强大)
- DBeaver(免费开源,推荐)
- MySQL Workbench(官方免费)
- DataGrip(JetBrains出品,付费)
验证安装
bash
mysql -u root -p2.1.2 MySQL基础操作
数据库操作
sql
-- 查看所有数据库
SHOW DATABASES;
-- 创建数据库
CREATE DATABASE test_db;
-- 使用数据库
USE test_db;
-- 删除数据库
DROP DATABASE test_db;
-- 查看当前数据库
SELECT DATABASE();数据类型
数值类型:
| 类型 | 大小 | 范围 | 说明 |
|---|---|---|---|
| TINYINT | 1字节 | -128~127 | 小整数 |
| SMALLINT | 2字节 | -32768~32767 | 短整数 |
| INT | 4字节 | -2^31~2^31-1 | 整数(最常用) |
| BIGINT | 8字节 | -2^63~2^63-1 | 长整数 |
| FLOAT | 4字节 | 单精度 | 浮点数 |
| DOUBLE | 8字节 | 双精度 | 浮点数(最常用) |
| DECIMAL(M,D) | 可变 | 精确 | 精确数值(金额用) |
字符串类型:
| 类型 | 大小 | 说明 |
|---|---|---|
| CHAR(n) | n字符 | 定长字符串 |
| VARCHAR(n) | n字符 | 变长字符串(最常用) |
| TEXT | 64KB | 长文本 |
| LONGTEXT | 4GB | 超长文本 |
日期时间类型:
| 类型 | 格式 | 说明 |
|---|---|---|
| DATE | YYYY-MM-DD | 日期 |
| TIME | HH:MM:SS | 时间 |
| DATETIME | YYYY-MM-DD HH:MM:SS | 日期时间(最常用) |
| TIMESTAMP | YYYY-MM-DD HH:MM:SS | 时间戳(自动更新) |
| YEAR | YYYY | 年份 |
2.1.3 表操作
创建表
sql
-- 创建用户表
CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
username VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名',
password VARCHAR(100) NOT NULL COMMENT '密码',
email VARCHAR(100) COMMENT '邮箱',
age INT COMMENT '年龄',
status TINYINT DEFAULT 1 COMMENT '状态:0-禁用,1-启用',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
-- 创建订单表
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
order_no VARCHAR(50) NOT NULL UNIQUE COMMENT '订单号',
user_id BIGINT NOT NULL COMMENT '用户ID',
amount DECIMAL(10,2) NOT NULL COMMENT '订单金额',
status TINYINT DEFAULT 0 COMMENT '状态:0-待支付,1-已支付,2-已取消',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
FOREIGN KEY (user_id) REFERENCES users(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';修改表
sql
-- 查看表结构
DESC users;
SHOW CREATE TABLE users;
-- 添加字段
ALTER TABLE users ADD COLUMN phone VARCHAR(20) COMMENT '手机号';
-- 修改字段
ALTER TABLE users MODIFY COLUMN email VARCHAR(150) COMMENT '邮箱';
-- 修改字段名
ALTER TABLE users CHANGE COLUMN phone mobile VARCHAR(20) COMMENT '手机号';
-- 删除字段
ALTER TABLE users DROP COLUMN mobile;
-- 修改表名
ALTER TABLE users RENAME TO user_info;
-- 删除表
DROP TABLE users;2.1.4 CRUD操作
插入数据(INSERT)
sql
-- 插入单条
INSERT INTO users (username, password, email, age)
VALUES ('zhangsan', '123456', 'zhangsan@example.com', 25);
-- 插入多条
INSERT INTO users (username, password, email, age) VALUES
('lisi', '123456', 'lisi@example.com', 28),
('wangwu', '123456', 'wangwu@example.com', 30),
('zhaoliu', '123456', 'zhaoliu@example.com', 22);查询数据(SELECT)
sql
-- 查询所有
SELECT * FROM users;
-- 查询指定字段
SELECT id, username, email FROM users;
-- 条件查询
SELECT * FROM users WHERE age > 25;
SELECT * FROM users WHERE age BETWEEN 20 AND 30;
SELECT * FROM users WHERE age IN (25, 28, 30);
SELECT * FROM users WHERE username LIKE 'zhang%';
SELECT * FROM users WHERE email IS NULL;
SELECT * FROM users WHERE email IS NOT NULL;
-- 排序
SELECT * FROM users ORDER BY age ASC;
SELECT * FROM users ORDER BY age DESC;
SELECT * FROM users ORDER BY age DESC, id ASC;
-- 分页
SELECT * FROM users LIMIT 10; -- 前10条
SELECT * FROM users LIMIT 0, 10; -- 从0开始取10条
SELECT * FROM users LIMIT 10 OFFSET 20; -- 跳过20条取10条
-- 聚合函数
SELECT COUNT(*) FROM users; -- 总数
SELECT COUNT(DISTINCT age) FROM users; -- 去重计数
SELECT SUM(amount) FROM orders; -- 求和
SELECT AVG(age) FROM users; -- 平均
SELECT MAX(age) FROM users; -- 最大
SELECT MIN(age) FROM users; -- 最小
-- 分组查询
SELECT age, COUNT(*) as count FROM users GROUP BY age;
SELECT age, COUNT(*) as count FROM users GROUP BY age HAVING count > 1;
-- 别名
SELECT username AS name, email AS mail FROM users;
SELECT u.username, o.order_no FROM users u, orders o WHERE u.id = o.user_id;更新数据(UPDATE)
sql
-- 更新单条
UPDATE users SET age = 26 WHERE id = 1;
-- 更新多条
UPDATE users SET age = age + 1 WHERE age < 30;
-- 更新所有(慎用)
UPDATE users SET status = 1;删除数据(DELETE)
sql
-- 删除单条
DELETE FROM users WHERE id = 1;
-- 删除多条
DELETE FROM users WHERE age < 18;
-- 清空表(保留表结构)
TRUNCATE TABLE users;2.1.5 高级查询
多表连接
sql
-- 内连接(只返回匹配的记录)
SELECT u.username, o.order_no, o.amount
FROM users u
INNER JOIN orders o ON u.id = o.user_id;
-- 左连接(返回左表所有记录)
SELECT u.username, o.order_no, o.amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
-- 右连接(返回右表所有记录)
SELECT u.username, o.order_no, o.amount
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;
-- 自连接
SELECT e.name AS employee, m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;子查询
sql
-- WHERE子查询
SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE amount > 1000);
-- FROM子查询
SELECT * FROM (
SELECT user_id, SUM(amount) as total
FROM orders
GROUP BY user_id
) t WHERE t.total > 1000;
-- EXISTS子查询
SELECT * FROM users u
WHERE EXISTS (
SELECT 1 FROM orders o WHERE o.user_id = u.id
);联合查询
sql
-- UNION(去重)
SELECT username FROM users
UNION
SELECT order_no FROM orders;
-- UNION ALL(不去重)
SELECT username FROM users
UNION ALL
SELECT order_no FROM orders;2.1.6 索引
索引类型
- 主键索引(PRIMARY KEY):唯一标识,不能为空
- 唯一索引(UNIQUE):值唯一,可以为空
- 普通索引(INDEX):最基本的索引
- 全文索引(FULLTEXT):用于全文检索
创建索引
sql
-- 创建表时指定索引
CREATE TABLE users (
id BIGINT PRIMARY KEY,
username VARCHAR(50) UNIQUE,
email VARCHAR(100),
INDEX idx_email (email)
);
-- 添加索引
ALTER TABLE users ADD INDEX idx_age (age);
CREATE INDEX idx_created_at ON users (created_at);
-- 添加唯一索引
ALTER TABLE users ADD UNIQUE idx_username (username);
-- 删除索引
DROP INDEX idx_age ON users;索引使用原则
- 频繁作为查询条件的字段建索引
- 数据量大的表建索引
- 区分度高的字段建索引
- 避免过多索引(一般不超过5个)
- 更新频繁的字段谨慎建索引
2.1.7 事务
事务特性(ACID)
- 原子性(Atomicity):事务是最小执行单位,要么全部成功,要么全部失败
- 一致性(Consistency):事务执行前后,数据库状态保持一致
- 隔离性(Isolation):并发事务之间互不干扰
- 持久性(Durability):事务提交后,数据永久保存
事务操作/
sql
-- 开启事务
START TRANSACTION;
-- 或
BEGIN;
-- 提交事务
COMMIT;
-- 回滚事务
ROLLBACK;
-- 示例
START TRANSACTION;
UPDATE users SET balance = balance - 100 WHERE id = 1;
UPDATE users SET balance = balance + 100 WHERE id = 2;
COMMIT;隔离级别
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED | 可能 | 可能 | 可能 |
| READ COMMITTED | 不会 | 可能 | 可能 |
| REPEATABLE READ(默认) | 不会 | 不会 | 可能 |
| SERIALIZABLE | 不会 | 不会 | 不会 |
sql
-- 查看隔离级别
SELECT @@transaction_isolation;
-- 设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;2.1.8 视图
sql
-- 创建视图
CREATE VIEW user_orders AS
SELECT u.username, o.order_no, o.amount, o.created_at
FROM users u
JOIN orders o ON u.id = o.user_id;
-- 使用视图
SELECT * FROM user_orders WHERE amount > 1000;
-- 删除视图
DROP VIEW user_orders;2.1.9 存储过程
sql
-- 创建存储过程
DELIMITER //
CREATE PROCEDURE GetUserOrders(IN userId BIGINT)
BEGIN
SELECT * FROM orders WHERE user_id = userId;
END //
DELIMITER ;
-- 调用存储过程
CALL GetUserOrders(1);
-- 删除存储过程
DROP PROCEDURE GetUserOrders;2.2 JDBC(第5-6天)
2.2.1 JDBC概述
JDBC(Java Database Connectivity)是Java连接数据库的标准API。
2.2.2 JDBC核心步骤
java
import java.sql.*;
public class JdbcDemo {
public static void main(String[] args) {
// 数据库连接信息
String url = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC&characterEncoding=utf8";
String user = "root";
String password = "123456";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 1. 加载驱动(JDBC 4.0+可省略)
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 获取连接
conn = DriverManager.getConnection(url, user, password);
// 3. 创建SQL
String sql = "SELECT * FROM users WHERE age > ?";
// 4. 创建PreparedStatement
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 25);
// 5. 执行查询
rs = pstmt.executeQuery();
// 6. 处理结果
while (rs.next()) {
Long id = rs.getLong("id");
String username = rs.getString("username");
String email = rs.getString("email");
int age = rs.getInt("age");
System.out.println(id + ", " + username + ", " + email + ", " + age);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 7. 关闭资源(逆序关闭)
try {
if (rs != null) rs.close();
if (pstmt != null) pstmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}2.2.3 CRUD操作
java
public class JdbcCrudDemo {
private static final String URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";
private static final String USER = "root";
private static final String PASSWORD = "123456";
// 插入
public void insert(String username, String password, String email, int age) {
String sql = "INSERT INTO users (username, password, email, age) VALUES (?, ?, ?, ?)";
try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, username);
pstmt.setString(2, password);
pstmt.setString(3, email);
pstmt.setInt(4, age);
int rows = pstmt.executeUpdate();
System.out.println("插入成功,影响行数:" + rows);
} catch (SQLException e) {
e.printStackTrace();
}
}
// 更新
public void update(Long id, String email) {
String sql = "UPDATE users SET email = ? WHERE id = ?";
try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, email);
pstmt.setLong(2, id);
int rows = pstmt.executeUpdate();
System.out.println("更新成功,影响行数:" + rows);
} catch (SQLException e) {
e.printStackTrace();
}
}
// 删除
public void delete(Long id) {
String sql = "DELETE FROM users WHERE id = ?";
try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setLong(1, id);
int rows = pstmt.executeUpdate();
System.out.println("删除成功,影响行数:" + rows);
} catch (SQLException e) {
e.printStackTrace();
}
}
// 查询单个
public User findById(Long id) {
String sql = "SELECT * FROM users WHERE id = ?";
User user = null;
try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setLong(1, id);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
user = new User();
user.setId(rs.getLong("id"));
user.setUsername(rs.getString("username"));
user.setEmail(rs.getString("email"));
user.setAge(rs.getInt("age"));
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return user;
}
// 查询列表
public List<User> findAll() {
String sql = "SELECT * FROM users";
List<User> users = new ArrayList<>();
try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
User user = new User();
user.setId(rs.getLong("id"));
user.setUsername(rs.getString("username"));
user.setEmail(rs.getString("email"));
user.setAge(rs.getInt("age"));
users.add(user);
}
} catch (SQLException e) {
e.printStackTrace();
}
return users;
}
}2.2.4 事务管理
java
public class JdbcTransactionDemo {
public void transfer(Long fromId, Long toId, BigDecimal amount) {
String url = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";
String user = "root";
String password = "123456";
try (Connection conn = DriverManager.getConnection(url, user, password)) {
// 开启事务
conn.setAutoCommit(false);
try {
// 扣款
String sql1 = "UPDATE accounts SET balance = balance - ? WHERE id = ?";
try (PreparedStatement pstmt1 = conn.prepareStatement(sql1)) {
pstmt1.setBigDecimal(1, amount);
pstmt1.setLong(2, fromId);
pstmt1.executeUpdate();
}
// 模拟异常
// int i = 1 / 0;
// 入账
String sql2 = "UPDATE accounts SET balance = balance + ? WHERE id = ?";
try (PreparedStatement pstmt2 = conn.prepareStatement(sql2)) {
pstmt2.setBigDecimal(1, amount);
pstmt2.setLong(2, toId);
pstmt2.executeUpdate();
}
// 提交事务
conn.commit();
System.out.println("转账成功");
} catch (Exception e) {
// 回滚事务
conn.rollback();
System.out.println("转账失败,已回滚");
e.printStackTrace();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}2.2.5 连接池
什么是连接池
连接池预先创建并维护多个数据库连接,避免频繁创建和销毁连接的开销。
HikariCP(Spring Boot默认)
Maven依赖:
xml
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.1</version>
</dependency>使用示例:
java
public class HikariDemo {
private static HikariDataSource dataSource;
static {
// 配置连接池
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC");
config.setUsername("root");
config.setPassword("123456");
config.setMaximumPoolSize(10); // 最大连接数
config.setMinimumIdle(5); // 最小空闲连接
config.setConnectionTimeout(30000); // 连接超时时间
config.setIdleTimeout(600000); // 空闲超时时间
config.setMaxLifetime(1800000); // 连接最大存活时间
dataSource = new HikariDataSource(config);
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public static void main(String[] args) {
try (Connection conn = getConnection()) {
System.out.println("获取连接成功:" + conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
}2.3 MyBatis(第7-12天)
2.3.1 MyBatis概述
MyBatis是一个半自动化的持久层框架,将SQL语句与Java代码分离,支持动态SQL。
2.3.2 快速入门
Maven依赖
xml
<dependencies>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.13</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>核心配置文件 mybatis-config.xml
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- properties:引入外部配置文件 -->
<properties resource="db.properties"/>
<!-- settings:全局配置 -->
<settings>
<!-- 开启驼峰命名自动转换 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启日志 -->
<setting name="logImpl" value="LOG4J"/>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
<!-- typeAliases:类型别名 -->
<typeAliases>
<package name="com.example.entity"/>
</typeAliases>
<!-- environments:环境配置 -->
<environments default="development">
<environment id="development">
<!-- 事务管理 -->
<transactionManager type="JDBC"/>
<!-- 数据源 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- mappers:映射文件 -->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
<!-- 或使用包扫描 -->
<package name="com.example.mapper"/>
</mappers>
</configuration>db.properties
properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456实体类 User.java
java
public class User {
private Long id;
private String username;
private String password;
private String email;
private Integer age;
private Date createdAt;
private Date updatedAt;
// getter和setter省略
}Mapper接口 UserMapper.java
java
public interface UserMapper {
User findById(Long id);
List<User> findAll();
int insert(User user);
int update(User user);
int deleteById(Long id);
}Mapper XML UserMapper.xml
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<!-- 结果映射 -->
<resultMap id="UserResultMap" type="User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="email" column="email"/>
<result property="age" column="age"/>
<result property="createdAt" column="created_at"/>
<result property="updatedAt" column="updated_at"/>
</resultMap>
<!-- 查询单个 -->
<select id="findById" resultMap="UserResultMap">
SELECT * FROM users WHERE id = #{id}
</select>
<!-- 查询全部 -->
<select id="findAll" resultMap="UserResultMap">
SELECT * FROM users
</select>
<!-- 插入 -->
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO users (username, password, email, age)
VALUES (#{username}, #{password}, #{email}, #{age})
</insert>
<!-- 更新 -->
<update id="update">
UPDATE users
SET username = #{username},
email = #{email},
age = #{age}
WHERE id = #{id}
</update>
<!-- 删除 -->
<delete id="deleteById">
DELETE FROM users WHERE id = #{id}
</delete>
</mapper>测试类
java
public class MybatisTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void setup() throws IOException {
// 加载配置
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindById() {
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.findById(1L);
System.out.println(user);
}
}
@Test
public void testInsert() {
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User();
user.setUsername("test");
user.setPassword("123456");
user.setEmail("test@example.com");
user.setAge(25);
int rows = mapper.insert(user);
session.commit();
System.out.println("插入成功,ID:" + user.getId());
}
}
}2.3.3 动态SQL
xml
<!-- 条件查询 -->
<select id="findByCondition" resultMap="UserResultMap">
SELECT * FROM users
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="email != null and email != ''">
AND email = #{email}
</if>
<if test="age != null">
AND age = #{age}
</if>
<if test="minAge != null and maxAge != null">
AND age BETWEEN #{minAge} AND #{maxAge}
</if>
</where>
ORDER BY id DESC
</select>
<!-- 批量更新 -->
<update id="batchUpdate">
UPDATE users
<trim prefix="SET" suffixOverrides=",">
<trim prefix="username = CASE" suffix="END,">
<foreach collection="list" item="user">
WHEN id = #{user.id} THEN #{user.username}
</foreach>
</trim>
<trim prefix="email = CASE" suffix="END,">
<foreach collection="list" item="user">
WHEN id = #{user.id} THEN #{user.email}
</foreach>
</trim>
</trim>
WHERE id IN
<foreach collection="list" item="user" open="(" separator="," close=")">
#{user.id}
</foreach>
</update>
<!-- 批量插入 -->
<insert id="batchInsert">
INSERT INTO users (username, password, email, age) VALUES
<foreach collection="list" item="user" separator=",">
(#{user.username}, #{user.password}, #{user.email}, #{user.age})
</foreach>
</insert>
<!-- choose选择 -->
<select id="findByChoose" resultMap="UserResultMap">
SELECT * FROM users
<where>
<choose>
<when test="id != null">
AND id = #{id}
</when>
<when test="username != null">
AND username = #{username}
</when>
<otherwise>
AND status = 1
</otherwise>
</choose>
</where>
</select>
<!-- set标签 -->
<update id="updateSelective">
UPDATE users
<set>
<if test="username != null">username = #{username},</if>
<if test="email != null">email = #{email},</if>
<if test="age != null">age = #{age},</if>
</set>
WHERE id = #{id}
</update>
<!-- foreach批量删除 -->
<delete id="batchDelete">
DELETE FROM users WHERE id IN
<foreach collection="list" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>2.3.4 缓存机制
一级缓存
- SqlSession级别的缓存
- 默认开启
- 同一个SqlSession中,相同的查询会从缓存获取
java
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
// 第一次查询,从数据库获取
User user1 = mapper.findById(1L);
// 第二次查询,从一级缓存获取
User user2 = mapper.findById(1L);
System.out.println(user1 == user2); // true二级缓存
- Mapper级别的缓存
- 需要手动配置
- 多个SqlSession共享
mybatis-config.xml开启:
xml
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>Mapper.xml配置:
xml
<mapper namespace="com.example.mapper.UserMapper">
<!-- 开启二级缓存 -->
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
<!-- 或使用ref引用其他mapper的缓存 -->
<cache-ref namespace="com.example.mapper.RoleMapper"/>
</mapper>2.3.5 关联查询
一对一
xml
<!-- 用户-身份证 -->
<resultMap id="UserIdCardResultMap" type="User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<!-- 关联身份证 -->
<association property="idCard" javaType="IdCard">
<id property="id" column="id_card_id"/>
<result property="cardNo" column="card_no"/>
</association>
</resultMap>
<select id="findWithIdCard" resultMap="UserIdCardResultMap">
SELECT u.*, i.id as id_card_id, i.card_no
FROM users u
LEFT JOIN id_cards i ON u.id = i.user_id
WHERE u.id = #{id}
</select>一对多
xml
<!-- 用户-订单 -->
<resultMap id="UserOrdersResultMap" type="User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<!-- 关联订单列表 -->
<collection property="orders" ofType="Order">
<id property="id" column="order_id"/>
<result property="orderNo" column="order_no"/>
<result property="amount" column="amount"/>
</collection>
</resultMap>
<select id="findWithOrders" resultMap="UserOrdersResultMap">
SELECT u.*, o.id as order_id, o.order_no, o.amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.id = #{id}
</select>2.3.6 MyBatis插件
java
// 自定义插件:打印SQL执行时间
@Intercepts({
@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class SqlTimePlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
long start = System.currentTimeMillis();
Object result = invocation.proceed();
long time = System.currentTimeMillis() - start;
System.out.println("SQL执行时间:" + time + "ms");
return result;
}
}注册插件:
xml
<plugins>
<plugin interceptor="com.example.plugin.SqlTimePlugin"/>
</plugins>2.4 MyBatis-Plus(第13-14天)
2.4.1 MyBatis-Plus概述
MyBatis-Plus是MyBatis的增强工具,简化CRUD操作。
2.4.2 快速入门
Maven依赖
xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>实体类
java
@Data // Lombok注解
@TableName("users") // 表名
public class User {
@TableId(type = IdType.AUTO) // 主键自增
private Long id;
private String username;
private String password;
private String email;
private Integer age;
@TableField(fill = FieldFill.INSERT) // 自动填充
private Date createdAt;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updatedAt;
@TableLogic // 逻辑删除
private Integer deleted;
}Mapper接口
java
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 继承BaseMapper,自动获得CRUD方法
}基本CRUD
java
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
// 插入
public void insert() {
User user = new User();
user.setUsername("test");
user.setPassword("123456");
user.setEmail("test@example.com");
user.setAge(25);
userMapper.insert(user);
System.out.println("插入ID:" + user.getId());
}
// 根据ID查询
public User findById(Long id) {
return userMapper.selectById(id);
}
// 条件查询
public List<User> findByCondition() {
return userMapper.selectList(
new QueryWrapper<User>()
.eq("age", 25)
.like("username", "test")
.orderByDesc("id")
);
}
// 分页查询
public IPage<User> findByPage(int page, int size) {
return userMapper.selectPage(
new Page<>(page, size),
new QueryWrapper<User>().eq("status", 1)
);
}
// 更新
public void update() {
User user = new User();
user.setId(1L);
user.setEmail("new@example.com");
userMapper.updateById(user);
}
// 删除
public void delete(Long id) {
userMapper.deleteById(id);
}
// 批量删除
public void batchDelete(List<Long> ids) {
userMapper.deleteBatchIds(ids);
}
}2.4.3 条件构造器
java
// QueryWrapper
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username", "test") // 等于
.ne("status", 0) // 不等于
.gt("age", 18) // 大于
.ge("age", 18) // 大于等于
.lt("age", 60) // 小于
.le("age", 60) // 小于等于
.between("age", 18, 60) // BETWEEN
.like("username", "test") // LIKE '%test%'
.likeLeft("username", "test") // LIKE '%test'
.likeRight("username", "test") // LIKE 'test%'
.in("id", Arrays.asList(1, 2, 3)) // IN
.orderByDesc("id") // 降序
.orderByAsc("age") // 升序
.groupBy("age") // 分组
.having("COUNT(*) > 1") // HAVING
.select("id", "username", "age"); // 指定字段
// LambdaQueryWrapper(推荐,避免字段名写错)
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.eq(User::getAge, 25)
.like(User::getUsername, "test")
.orderByDesc(User::getId);
// UpdateWrapper
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("id", 1L)
.set("email", "new@example.com")
.set("age", 26);
userMapper.update(null, updateWrapper);2.4.4 自动填充
java
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createdAt", Date.class, new Date());
this.strictInsertFill(metaObject, "updatedAt", Date.class, new Date());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updatedAt", Date.class, new Date());
}
}2.4.5 逻辑删除
yaml
# application.yml
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 逻辑删除字段
logic-delete-value: 1 # 删除值
logic-not-delete-value: 0 # 未删除值2.4.6 代码生成器
java
public class CodeGenerator {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://localhost:3306/test_db", "root", "123456")
.globalConfig(builder -> {
builder.author("yourname")
.outputDir("D:\\code\\src\\main\\java");
})
.packageConfig(builder -> {
builder.parent("com.example")
.moduleName("system")
.entity("entity")
.mapper("mapper")
.service("service")
.serviceImpl("service.impl")
.xml("mapper.xml")
.controller("controller")
.pathInfo(Collections.singletonMap(OutputFile.xml,
"D:\\code\\src\\main\\resources\\mapper"));
})
.strategyConfig(builder -> {
builder.addInclude("users", "orders")
.addTablePrefix("t_");
})
.templateConfig(builder -> {
builder.disable(TemplateType.ENTITY);
})
.execute();
}
}2.5 JPA/Hibernate(了解)
2.5.1 JPA概述
JPA(Java Persistence API)是Java持久化规范,Hibernate是实现。
2.5.2 基本使用
Maven依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>实体类
java
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 50)
private String username;
@Column(nullable = false)
private String password;
@Column(length = 100)
private String email;
private Integer age;
@CreationTimestamp
private Date createdAt;
@UpdateTimestamp
private Date updatedAt;
}Repository接口
java
public interface UserRepository extends JpaRepository<User, Long> {
// 方法命名查询
List<User> findByAgeGreaterThan(Integer age);
List<User> findByUsernameLike(String username);
User findByEmail(String email);
// JPQL查询
@Query("SELECT u FROM User u WHERE u.age BETWEEN :min AND :max")
List<User> findByAgeBetween(@Param("min") Integer min, @Param("max") Integer max);
// 原生SQL
@Query(value = "SELECT * FROM users WHERE age > ?1", nativeQuery = true)
List<User> findByAgeNative(Integer age);
}2.6 练习任务
练习1:学生管理系统
- 设计学生表、课程表、选课表
- 实现完整的CRUD
- 使用MyBatis实现关联查询
练习2:电商订单系统
- 设计用户表、商品表、订单表、订单详情表
- 实现下单流程(事务处理)
- 使用连接池优化性能
练习3:博客系统
- 设计文章表、分类表、标签表、评论表
- 实现文章分页查询
- 实现标签多对多关联
2.7 学习检查清单
- [ ] 掌握MySQL基本操作
- [ ] 理解事务ACID特性
- [ ] 掌握索引使用原则
- [ ] 熟练使用JDBC
- [ ] 理解连接池原理
- [ ] 掌握MyBatis核心配置
- [ ] 熟练使用动态SQL
- [ ] 理解MyBatis缓存机制
- [ ] 掌握MyBatis-Plus基本使用
完成本阶段后,你应该能够熟练使用MyBatis进行数据库操作,为后续Spring Boot开发打下基础。
评论
正在加载评论…