SQL

Prompt:

ddl语句是什么

Response:

DDL 是 Data Definition Language(数据定义语言) 的缩写,它是 SQL(结构化查询语言)的一部分,用于 定义或修改数据库结构,比如:数据库、表、视图、索引、约束等。


🔧 常见的 DDL 语句包括:

语句 功能说明
CREATE 创建数据库对象(如表、数据库、视图等)
ALTER 修改已存在的数据库对象结构
DROP 删除数据库对象(表、数据库等)
TRUNCATE 清空表中的所有数据(但保留表结构)
RENAME 重命名数据库对象

✅ 常用 DDL 示例:

1. 创建数据库

1
CREATE DATABASE school;

2. 创建数据表

1
2
3
4
5
6
CREATE TABLE students (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
age INT,
enrollment_date DATE
);

3. 修改表结构(添加列)

1
ALTER TABLE students ADD email VARCHAR(100);

4. 删除表

1
DROP TABLE students;

5. 清空表数据,但保留表结构

1
TRUNCATE TABLE students;

🆚 DDL vs DML vs DCL 简要对比

类型 全称 用途
DDL Data Definition Language 定义和修改数据库结构(表、列、索引)
DML Data Manipulation Language 操作数据(增删查改)例如 SELECTINSERTUPDATEDELETE
DCL Data Control Language 控制访问权限,例如 GRANTREVOKE

如果你需要某个具体场景下的 DDL 语句,我可以帮你写出来——只需告诉我你希望设计的表结构或功能。

Prompt:

ddl表操作

2/2

Response:

你提到“DDL 表操作”,指的是使用 DDL(数据定义语言) 来对**数据表(Table)**进行的各种结构定义和修改操作。下面我会系统地列出:


DDL 表操作总览

1. 创建表(CREATE TABLE)

1
2
3
4
5
6
7
CREATE TABLE employees (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
position VARCHAR(50),
salary DECIMAL(10, 2),
hire_date DATE
);

2. 修改表结构(ALTER TABLE)

➤ 添加新列:

1
ALTER TABLE employees ADD email VARCHAR(100);

➤ 修改列的数据类型:

1
ALTER TABLE employees MODIFY salary DECIMAL(12, 2);

➤ 重命名列:

(MySQL)

1
ALTER TABLE employees CHANGE name full_name VARCHAR(150);

➤ 删除列:

1
ALTER TABLE employees DROP COLUMN email;

➤ 添加主键 / 外键 / 唯一约束:

1
2
3
4
5
6
7
8
-- 添加主键
ALTER TABLE employees ADD PRIMARY KEY (id);

-- 添加唯一约束
ALTER TABLE employees ADD CONSTRAINT unique_email UNIQUE (email);

-- 添加外键
ALTER TABLE employees ADD CONSTRAINT fk_department FOREIGN KEY (dept_id) REFERENCES departments(id);

3. 删除表(DROP TABLE)

1
DROP TABLE employees;

4. 清空表数据但保留结构(TRUNCATE TABLE)

1
TRUNCATE TABLE employees;

5. 重命名表(RENAME TABLE)

1
RENAME TABLE employees TO staff;

🧠 补充说明

操作 会删除数据? 会删除表结构? 可回滚?
DROP TABLE ✅ 是 ✅ 是 ❌ 不可回滚
TRUNCATE TABLE ✅ 是 ❌ 否 ❌ 不可回滚
DELETE FROM(DML) ✅ 是(可加条件) ❌ 否 ✅ 可回滚(需开启事务)

如果你有某张具体的表结构或想实现的业务(比如学生成绩管理、订单管理、用户管理等),我可以帮你写出完整的 DDL 建表语句,告诉我你的需求即可。

Prompt:

创建表时常用约束有什么

Response:

创建表时,常用的 SQL 约束(Constraints) 用于限定数据的合法性,确保数据库中数据的准确性和完整性。


✅ 常用约束及作用一览

约束名 作用说明
PRIMARY KEY 唯一标识一条记录,不允许为 NULL 且唯一
FOREIGN KEY 保证两表数据之间的引用一致性(外键)
UNIQUE 限定某列(或组合列)值不能重复
NOT NULL 强制该字段不能为 NULL
DEFAULT 指定字段的默认值
CHECK 限定字段的值必须满足某种条件(部分数据库如 MySQL 8.0+ 才支持)
AUTO_INCREMENT 自动增长(通常用于主键,MySQL 专有)

🧱 示例:创建带各种约束的表

1
2
3
4
5
6
7
8
9
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT, -- 主键 + 自增
username VARCHAR(50) NOT NULL UNIQUE, -- 用户名不能为空,且唯一
email VARCHAR(100) DEFAULT 'unknown@example.com', -- 默认值
age INT CHECK (age >= 18), -- 检查约束:年龄不能小于18
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 默认当前时间
role_id INT,
FOREIGN KEY (role_id) REFERENCES roles(id) -- 外键约束:引用角色表的id
);

🔍 每种约束的解释和示例

1. PRIMARY KEY

  • 确保每条记录有唯一标识(主键)
1
id INT PRIMARY KEY

2. NOT NULL

  • 强制该列不能为空
1
username VARCHAR(50) NOT NULL

3. UNIQUE

  • 防止重复值(可用于邮箱、用户名等字段)
1
email VARCHAR(100) UNIQUE

4. DEFAULT

  • 如果插入时未提供值,则使用默认值
1
status VARCHAR(20) DEFAULT 'active'

5. CHECK

  • 限制字段值范围(如年龄必须 ≥ 18)
1
age INT CHECK (age >= 18)

⚠️ 注意:MySQL 在 8.0 之前版本对 CHECK 语法支持不好,可能会被忽略。

6. FOREIGN KEY

  • 创建引用另一个表的字段,确保引用数据存在
1
FOREIGN KEY (role_id) REFERENCES roles(id)

7. AUTO_INCREMENT

  • 整数主键自动增长(MySQL 专用)
1
id INT AUTO_INCREMENT

🧠 小贴士:

  • 每个表只能有一个主键,但可以有多个唯一约束。
  • 外键约束默认会拒绝删除或更新引用表中的数据,可以加 ON DELETE / ON UPDATE 条件控制行为。
    1
    FOREIGN KEY (dept_id) REFERENCES departments(id) ON DELETE CASCADE

如你有特定业务场景(如学生管理、订单系统等),我可以帮你定制建表语句和推荐合适的约束。需要的话直接告诉我。

Prompt:

mysql数据类型

Response:

在 MySQL 中,数据类型决定了表中字段能存储什么类型的数据。它们可以分为以下几类:


📦 一、数值类型

✅ 1. 整数类型

类型 大小(字节) 范围(有符号) 说明
TINYINT 1 -128 ~ 127 适用于很小的整数
SMALLINT 2 -32,768 ~ 32,767
MEDIUMINT 3 -8百万 ~ 8百万
INTINTEGER 4 -21亿 ~ 21亿 常用
BIGINT 8 超过 ±9千万亿 适用于大数,如金额、ID

加上 UNSIGNED 关键字可使范围变为非负(从 0 开始翻倍)
例:INT UNSIGNED → 0 ~ 42亿多


✅ 2. 小数/浮点类型

类型 说明
DECIMAL(M,D)NUMERIC(M,D) 精确小数,适用于金额。M 是总位数,D 是小数位数。例:DECIMAL(10,2)
FLOAT(M,D) 单精度浮点,适用于不要求精度的计算
DOUBLE(M,D)REAL(M,D) 双精度浮点,误差更小

🕒 二、日期和时间类型

类型 格式 说明
DATE YYYY-MM-DD 日期
TIME HH:MM:SS 时间
DATETIME YYYY-MM-DD HH:MM:SS 日期+时间
TIMESTAMP DATETIME 通常用于记录创建或更新时间(可自动更新)
YEAR YYYY 表示年份(如 2025)

🔤 三、字符串类型

✅ 1. 定长与变长字符

类型 说明
CHAR(n) 固定长度,适用于定长字段,如身份证号
VARCHAR(n) 变长,最大长度 65535 字节,适合变长文本,如姓名、邮箱等

推荐使用 VARCHAR,节省空间。CHAR 适合长度一致的场景。


✅ 2. 文本类型(适用于长文本)

类型 最大长度 用途
TINYTEXT 255 字节 短文本
TEXT 65KB 普通文本,如评论
MEDIUMTEXT 16MB 较大文本
LONGTEXT 4GB 超长文本,如文章正文

📁 四、二进制类型(少用)

类型 用途
BINARY(n) 定长二进制
VARBINARY(n) 变长二进制
BLOB 存储二进制大对象,如图片、音频等

🧠 小提示

  • 选择合适的数据类型有利于节省存储、提高性能。
  • 在金额相关场景中推荐使用 DECIMAL 而不是 FLOAT/DOUBLE,因为浮点数有精度问题。
  • VARCHARTEXT 更高效,可以创建索引;TEXT 不能直接作为索引字段(需加前缀)。

如你需要为某张表选择合适的数据类型或做设计,我可以帮你写一个建表语句或推荐字段类型。需要的话请提供表的结构或业务需求。

Prompt:

DQL语句中的聚合函数

Response:

DQL(Data Query Language,数据查询语言)中,聚合函数(Aggregate Functions)用于对一组数据进行计算并返回单一结果,常与 GROUP BY 搭配使用。


📊 常见聚合函数

函数 作用 示例
COUNT() 统计行数(记录数) COUNT(*) 统计总行数,COUNT(column) 统计非 NULL 的行数
SUM() 计算总和 SUM(salary) 计算工资总和
AVG() 计算平均值 AVG(salary)
MAX() 求最大值 MAX(salary)
MIN() 求最小值 MIN(salary)

🔹 基本用法

1
2
3
4
5
6
7
SELECT 
COUNT(*) AS total_employees, -- 总人数
AVG(salary) AS avg_salary, -- 平均工资
MAX(salary) AS highest_salary, -- 最高工资
MIN(salary) AS lowest_salary, -- 最低工资
SUM(salary) AS total_salary -- 工资总额
FROM employees;

🔹 搭配 GROUP BY 使用

1
2
3
4
5
6
7
-- 按部门统计员工数量和平均工资
SELECT
department_id,
COUNT(*) AS emp_count,
AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id;

🔹 搭配 HAVING 过滤分组结果

1
2
3
4
5
6
7
-- 查询平均工资高于 5000 的部门
SELECT
department_id,
AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
HAVING AVG(salary) > 5000;

🧠 小提示

  1. COUNT(*) 会统计所有行,包括 NULL 值;
    COUNT(column) 只统计该列非 NULL 的行。
  2. 聚合函数忽略 NULL 值COUNT(*) 除外)。
  3. 聚合函数通常不能直接与普通列混用,必须配合 GROUP BY

如果你需要的话,我可以帮你整理 DQL 聚合函数 + GROUP BY + HAVING 的速查表,方便你考试或面试用。
你要我帮你做吗?

Prompt:

dql语句中where与having的区别

Response:

DQL(数据查询语言)中,WHEREHAVING 都可以用来过滤数据,但它们作用的阶段对象不同:


📌 核心区别

特性 WHERE HAVING
作用阶段 分组前 过滤 分组后 过滤
能否使用聚合函数 ❌ 不能直接使用(因为此时还没计算聚合值) ✅ 可以使用(已经有了聚合结果)
过滤对象 行(Row) 分组(Group)
是否依赖 GROUP BY 独立使用 一般与 GROUP BY 搭配使用(也可单独用)

🔹 工作流程(执行顺序)

SQL 查询的大致执行顺序是:

1
FROM → WHERE → GROUP BY → 聚合函数计算 → HAVING → SELECT → ORDER BY

这说明:

  • WHERE 是在分组 之前,对原始行过滤。
  • HAVING 是在分组 之后,对分组结果过滤。

🔹 示例对比

1. 使用 WHERE(分组前过滤)

1
2
3
4
5
-- 先找出工资大于 3000 的员工,再按部门分组
SELECT department_id, AVG(salary) AS avg_salary
FROM employees
WHERE salary > 3000
GROUP BY department_id;

作用:在分组前就把工资 ≤ 3000 的员工排除了。


2. 使用 HAVING(分组后过滤)

1
2
3
4
5
-- 按部门分组,并找出平均工资大于 5000 的部门
SELECT department_id, AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
HAVING AVG(salary) > 5000;

作用:先把所有员工分组,计算平均工资,再过滤平均工资 ≤ 5000 的部门。


3. 两者结合

1
2
3
4
5
6
-- 先过滤单个员工工资 > 3000,再找平均工资 > 5000 的部门
SELECT department_id, AVG(salary) AS avg_salary
FROM employees
WHERE salary > 3000
GROUP BY department_id
HAVING AVG(salary) > 5000;

🧠 记忆口诀

WHERE 先筛行,HAVING 后筛组。
WHERE 不懂聚合,HAVING 专挑聚合。


如果你愿意,我可以帮你画一张 SQL 执行顺序流程图,把 WHEREHAVING 的位置直观标出来,方便你快速理解。
你要我画吗?

Prompt:

给我一些MySQL高频面试题

Response:

好嘞,我帮你整理一份 MySQL 高频面试题清单,涵盖基础、优化、事务、锁机制、索引、SQL 编写等核心考点,并附上简要回答思路。


📌 一、基础与数据类型

  1. MySQL 常见数据类型有哪些?各自的适用场景?
    • 整数TINYINTSMALLINTMEDIUMINTINTBIGINT(ID、计数器等)
    • 小数DECIMAL(精确,适合金额)、FLOATDOUBLE(非精确,适合科学计算)
    • 字符串CHAR(定长)、VARCHAR(变长)、TEXT(大文本)
    • 日期时间DATEDATETIMETIMESTAMPYEAR
  2. CHARVARCHAR 的区别?
    • CHAR(n):定长,存储速度快,浪费空间;适合固定长度数据。
    • VARCHAR(n):变长,节省空间,查询速度略慢,需额外存储长度信息。
  3. DATETIMETIMESTAMP 的区别?
    • DATETIME:存储范围大(1000-9999 年),不受时区影响。
    • TIMESTAMP:存储 1970-2038 年的时间,受时区影响,常用于记录创建/更新时间。

📌 二、索引与优化

  1. 索引的类型有哪些?
    • 主键索引(PRIMARY KEY
    • 唯一索引(UNIQUE
    • 普通索引(INDEX
    • 组合索引(多列索引)
    • 全文索引(FULLTEXT
    • 空间索引(SPATIAL
  2. B+ 树索引的原理?为什么用 B+ 树而不是 B 树?
    • B+ 树所有数据存储在叶子节点,叶子节点有链表,范围查询效率高;
    • B 树数据可能存储在非叶节点,范围查询不如 B+ 树高效。
  3. 覆盖索引是什么?
    • 查询结果的字段全部在索引中,不用回表。
    • 优点:减少磁盘 IO,提高性能。
  4. 最左前缀原则
    • 组合索引会按最左列优先匹配,比如 (a,b,c) 索引,可用 aa,ba,b,c 但不能直接用 b,c
  5. EXPLAIN 的作用?
    • 查看 SQL 执行计划,包括索引使用情况、扫描行数、连接方式等。

📌 三、事务与锁

  1. MySQL 事务的四大特性(ACID)
    • 原子性(Atomicity)
    • 一致性(Consistency)
    • 隔离性(Isolation)
    • 持久性(Durability)
  2. 事务隔离级别及问题
    * 读未提交(可能脏读)
    * 读已提交(可能不可重复读)
    * 可重复读(可能幻读,MySQL InnoDB 通过间隙锁解决)
    * 串行化(性能最差)
  3. MySQL 有哪些锁?
    * 行锁、表锁、意向锁、间隙锁(Gap Lock)
  4. InnoDB 与 MyISAM 的区别?
    * InnoDB:支持事务、行级锁、外键;适合高并发 OLTP
    * MyISAM:不支持事务、表级锁、查询速度快;适合读多写少的场景

📌 四、SQL 编写与优化

  1. WHEREHAVING 的区别?
    * WHERE:分组前过滤,不能用聚合函数。
    * HAVING:分组后过滤,可以用聚合函数。
  2. INNER JOINLEFT JOINRIGHT JOIN 区别?
    * INNER JOIN:两表匹配的记录
    * LEFT JOIN:左表全部 + 匹配到的右表
    * RIGHT JOIN:右表全部 + 匹配到的左表
  3. 如何优化 SQL 查询性能?
    * 建立合适的索引(避免索引失效)
    * 避免 SELECT *
    * 分页优化(LIMIT + 覆盖索引)
    * 使用 EXPLAIN 分析执行计划
    * 大表分区/分库分表
  4. 索引失效的常见情况
    * 使用 OR 条件(部分情况)
    * 列类型隐式转换
    * 在索引列上使用函数
    * 模糊查询 %abc(前导 % 会失效)

📌 五、高级

  1. MySQL 的存储引擎有哪些?
    * 常用:InnoDB、MyISAM、Memory
    * 特殊:Archive(归档)、Federated(远程数据)
  2. 主从复制原理
    * 主库记录 binlog → 从库 I/O 线程读取 binlog → SQL 线程重放数据。
  3. 分库分表策略
    * 垂直拆分(按功能模块拆表)
    * 水平拆分(按 ID 范围/哈希分片)
  4. 慢查询优化步骤
    * 开启 slow_query_log
    * 用 EXPLAIN 分析
    * 添加索引 / 重写 SQL / 分表

我可以帮你把这些 MySQL 高频面试题整理成 题目 + 参考答案 + 关键知识点总结 的 PDF,方便你在面试前速记。
如果你要的话,我还能帮你额外加上 SQL 执行顺序图索引原理图
你需要我帮你整理成那种面试速记版吗?

1/2

Prompt:

jdbc是什么

Response:

JDBC 全称是 Java Database Connectivity,即 Java 数据库连接,是 Java 提供的一套 操作数据库的标准 API

它的作用就是:

让 Java 程序用统一的方式(同一套代码)去连接和操作各种不同的数据库(MySQL、Oracle、PostgreSQL、SQL Server…),而不用关心具体数据库的底层实现细节。


📌 JDBC 的核心作用

  1. 建立数据库连接(Java ↔ 数据库)
  2. 发送 SQL 语句(增删改查)
  3. 处理 SQL 执行结果
  4. 关闭数据库资源

🔹 JDBC 的工作原理

JDBC 本身是一个 接口规范,具体的数据库厂商(MySQL、Oracle 等)会提供 JDBC 驱动程序(Driver) 来实现这些接口。
执行流程是:

1
Java 程序 → JDBC API → JDBC 驱动 → 数据库

🧱 常用的 JDBC 相关类

类/接口 作用
DriverManager 管理数据库驱动,创建数据库连接
Connection 表示与数据库的连接
Statement 用于执行 SQL(简单语句)
PreparedStatement 执行预编译 SQL(防 SQL 注入)
ResultSet 封装查询结果集

🔹 JDBC 示例(连接 MySQL)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.sql.*;

public class JDBCDemo {
public static void main(String[] args) throws Exception {
// 1. 加载驱动(MySQL 8+ 可省略)
Class.forName("com.mysql.cj.jdbc.Driver");

// 2. 获取连接
String url = "jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC";
String user = "root";
String password = "123456";
Connection conn = DriverManager.getConnection(url, user, password);

// 3. 创建 PreparedStatement(防止 SQL 注入)
String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 1);

// 4. 执行查询
ResultSet rs = pstmt.executeQuery();

// 5. 处理结果
while (rs.next()) {
System.out.println(rs.getInt("id") + " - " + rs.getString("username"));
}

// 6. 关闭资源
rs.close();
pstmt.close();
conn.close();
}
}

📍 小结

  • JDBC 是 Java 操作数据库的标准接口规范
  • 需要配合 数据库厂商提供的 JDBC 驱动 才能工作。
  • 常用类:DriverManagerConnectionPreparedStatementResultSet
  • 生产环境中,通常结合 连接池(如 Druid、HikariCP)来管理 JDBC 连接,提高性能。

如果你需要的话,我可以帮你画一个 JDBC 工作原理流程图,让你更直观地理解 Java 是怎么一步步和数据库通信的。
你想让我画吗?

Prompt:

预编译sql是什么

Response:

预编译 SQL(Prepared SQL)指的是:
在执行 SQL 时,先让数据库服务器解析、编译、优化 SQL 语句的结构(即 SQL 模板),然后在多次执行时只需要 传递参数,而不需要重复编译 SQL。

它的常见实现就是 **JDBC 的 PreparedStatement**。


📌 原理

  1. 编写 SQL 模板(带占位符 ?
  2. 第一次执行时,数据库会 编译 SQL 模板 并缓存执行计划
  3. 后续执行时,只需要 绑定参数,直接使用缓存的执行计划,跳过重复解析
  4. 这样既能 提高性能,又能 防止 SQL 注入

🔹 示例

普通 SQL(Statement

1
2
3
String sql = "SELECT * FROM users WHERE username = '" + name + "'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);

⚠ 缺点:

  • 每次执行都要解析 SQL
  • 拼接字符串容易被 SQL 注入 攻击

预编译 SQL(PreparedStatement

1
2
3
4
String sql = "SELECT * FROM users WHERE username = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, name); // 绑定参数
ResultSet rs = pstmt.executeQuery();

✅ 优点:

  1. 性能更高(编译一次,多次执行)
  2. 防止 SQL 注入(参数不会直接拼接到 SQL 中,而是单独传给数据库)
  3. 代码可读性更好

📊 预编译执行流程(JDBC)

1
2
3
4
5
6
7
8
客户端(Java程序):
1. 编写 SQL 模板(含 ? 占位符)
2. 绑定参数
3. 发送到数据库

数据库服务器:
1. 第一次执行:解析 SQL、生成执行计划、缓存
2. 后续执行:直接使用缓存的执行计划,替换参数

📍 总结

  • 预编译 SQL 就是 先编译 SQL 结构,再绑定参数执行 的方式。
  • 在 JDBC 中用 PreparedStatement 实现。
  • 优点:性能更高 + 防 SQL 注入。
  • 适用场景:频繁执行同一类 SQL(比如批量插入、重复查询)时效果最佳。

如果你愿意,我可以帮你画一张 “预编译 SQL vs 普通 SQL” 执行流程对比图,让你秒懂它们的区别。
你要我画吗?

Prompt:

什么是mybatis

Response:

MyBatis 是一个 Java 的持久层框架,它的作用是 帮助开发者更方便地在 Java 程序中执行 SQL 语句,并且可以 自动把数据库查询结果映射成 Java 对象

简单说:

MyBatis 让你写 SQL 更自由,同时又能自动把 SQL 和 Java 对象关联起来。


📌 MyBatis 的特点

  1. 半自动化 ORM 框架
    • ORM(Object Relational Mapping)= 对象关系映射
    • Hibernate 是全自动 ORM(帮你自动生成 SQL)
    • MyBatis 是半自动 ORM(SQL 你自己写,框架帮你映射结果)
  2. 支持自定义 SQL
    • 不像 Hibernate 那样生成 SQL,而是由你写 SQL(灵活、可控)
  3. SQL 与 Java 分离
    • SQL 语句写在 XML 文件中或注解中,方便维护
  4. 自动结果映射
    • 查询结果自动映射到 Java 对象(支持复杂对象映射)
  5. 支持动态 SQL
    • 可以在 SQL 中用 <if><where><foreach> 等标签实现动态拼接 SQL

🔹 MyBatis 核心组件

组件 作用
SqlSessionFactory 创建 SqlSession 的工厂
SqlSession 执行 SQL、获取映射器、管理事务
Mapper(映射器) Java 接口与 SQL 的映射(XML/注解)
XML 配置文件 定义数据库连接、映射关系、SQL 语句等

🔹 MyBatis 基本使用流程

  1. 定义实体类
1
2
3
4
5
6
public class User {
private int id;
private String username;
private String email;
// getter / setter
}
  1. 定义 Mapper 接口
1
2
3
public interface UserMapper {
User selectUserById(int id);
}
  1. 编写 XML 映射文件
1
2
3
4
5
<mapper namespace="com.example.mapper.UserMapper">
<select id="selectUserById" resultType="com.example.User">
SELECT * FROM users WHERE id = #{id}
</select>
</mapper>
  1. 加载 MyBatis 配置文件 & 执行 SQL
1
2
3
4
5
6
7
8
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectUserById(1);
System.out.println(user.getUsername());
}

📍 MyBatis 的优缺点

优点

  • SQL 灵活可控,性能可优化
  • 映射关系清晰,支持复杂 SQL
  • 易于和 Spring/Spring Boot 集成

缺点

  • 需要自己写 SQL(SQL 复杂度高的项目更适合)
  • SQL 与 Java 分离,调试时可能要在多个文件间切换

如果你需要的话,我可以帮你画一个 MyBatis 工作原理流程图,让你直观地理解它是怎么把 SQL 和 Java 对象关联起来的。
你要我帮你画吗?

Prompt:

在springboot项目中使用注解方式的流程呢

2/2

Response:

Spring Boot 项目中,使用 注解方式 来集成 MyBatis 的流程,其实就是不用 XML 写 SQL,而是直接在 Mapper 接口里用注解定义 SQL。这样代码和 SQL 写在一起,更简洁。

我给你分步骤讲清楚:


📌 Spring Boot + MyBatis 注解方式流程

1. 引入依赖

pom.xml 中添加 MyBatis Spring Boot Starter 和数据库驱动(以 MySQL 为例):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependencies>
<!-- MyBatis Spring Boot 启动器 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>

<!-- MySQL 驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.3.0</version>
</dependency>
</dependencies>

2. 配置数据库连接

application.ymlapplication.properties 中配置数据源:

1
2
3
4
5
6
spring:
datasource:
url: jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver

3. 创建实体类

1
2
3
4
5
6
public class User {
private Integer id;
private String username;
private String email;
// getter / setter
}

4. 创建 Mapper 接口(用注解写 SQL)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.apache.ibatis.annotations.*;

@Mapper // 让 Spring Boot 识别为 MyBatis Mapper
public interface UserMapper {

@Select("SELECT * FROM users WHERE id = #{id}")
User selectUserById(Integer id);

@Insert("INSERT INTO users(username, email) VALUES(#{username}, #{email})")
@Options(useGeneratedKeys = true, keyProperty = "id") // 获取自增主键
int insertUser(User user);

@Update("UPDATE users SET email = #{email} WHERE id = #{id}")
int updateUser(User user);

@Delete("DELETE FROM users WHERE id = #{id}")
int deleteUser(Integer id);
}

5. 编写 Service 层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class UserService {
private final UserMapper userMapper;

public UserService(UserMapper userMapper) {
this.userMapper = userMapper;
}

public User getUserById(Integer id) {
return userMapper.selectUserById(id);
}

public int addUser(User user) {
return userMapper.insertUser(user);
}
}

6. 编写 Controller 层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;

public UserController(UserService userService) {
this.userService = userService;
}

@GetMapping("/{id}")
public User getUser(@PathVariable Integer id) {
return userService.getUserById(id);
}

@PostMapping
public String createUser(@RequestBody User user) {
userService.addUser(user);
return "新增成功,ID = " + user.getId();
}
}

7. 启动类开启 Mapper 扫描

如果 Mapper 接口都加了 @Mapper,Spring Boot 会自动识别;
如果没加 @Mapper,可以用 @MapperScan

1
2
3
4
5
6
7
@SpringBootApplication
@MapperScan("com.example.demo.mapper") // 扫描 Mapper 接口所在包
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

📍 总结流程

  1. 引入 MyBatis + 数据库依赖
  2. 配置数据源
  3. 创建实体类
  4. @Mapper + 注解 SQL 定义 Mapper 接口
  5. 编写 Service 层调用 Mapper
  6. 编写 Controller 层提供 API
  7. 启动类加 @MapperScan(或在每个 Mapper 上加 @Mapper

如果你愿意,我可以帮你画一张 Spring Boot + MyBatis 注解方式执行流程图,把从 Controller 到 SQL 执行的过程一步步画出来,方便你面试讲解。
你要我画吗?

Prompt:

跟mybatis-plus有什么区别

Response:

Spring Boot 项目中使用 MyBatis(普通版)和 MyBatis-Plus(MP) 的区别,主要体现在 开发效率、自动化程度和功能扩展 上。下面我帮你梳理清楚:


1️⃣ MyBatis(普通版)

  • 特点
    • SQL 需要自己手写(XML 或 注解)
    • 映射结果需要自己配置
    • 灵活、可控,但重复工作多
  • 优点
    • SQL 完全由开发者控制,适合复杂查询
    • 可以自定义映射关系
  • 缺点
    • CRUD SQL 需要手写
    • 项目中重复性高(insert、update、delete、select)
    • 动态 SQL 复杂

2️⃣ MyBatis-Plus

  • 特点
    • 在 MyBatis 基础上提供 增强功能
    • 内置 CRUD 方法(几乎无需写 SQL)
    • 支持 条件构造器(Wrapper)、分页、逻辑删除、乐观锁、自动填充字段等
    • 支持 代码生成器,可自动生成 Entity、Mapper、Service、Controller
  • 优点
    • 开发效率大幅提升(基本 CRUD 免写 SQL)
    • 支持 Lambda 表达式查询,代码更安全、更简洁
    • 提供丰富插件(分页、性能分析、乐观锁)
  • 缺点
    • 对 SQL 的灵活性略低(复杂 SQL 仍需手写)
    • 对新手理解 Wrapper API 可能需要适应

3️⃣ CRUD 对比示例

假设有 User 表:

功能 MyBatis MyBatis-Plus
查询所有用户 @Select("SELECT * FROM users") List<User> selectAll(); userMapper.selectList(null);
根据 id 查询 @Select("SELECT * FROM users WHERE id = #{id}") User selectById(int id); userMapper.selectById(id);
插入 @Insert("INSERT INTO users(...) VALUES(...)") int insert(User user); userMapper.insert(user);
更新 @Update(...) int update(User user); userMapper.updateById(user);
删除 @Delete(...) int delete(int id); userMapper.deleteById(id);
分页查询 手写 SQL + limit userMapper.selectPage(page, wrapper);

4️⃣ 总结

方面 MyBatis MyBatis-Plus
SQL 控制 完全自定义 内置 CRUD,可自定义 SQL
开发效率 较低 高(大量自动化功能)
学习成本 SQL 基础即可 需要学习 MP 特有 API
适用场景 SQL 复杂、灵活性要求高 常规 CRUD,快速开发,企业项目中常用

💡 总结一句话:

MyBatis 灵活可控,MyBatis-Plus 高效自动化,MP 是 MyBatis 的增强版。


如果你愿意,我可以帮你画一张 MyBatis 与 MyBatis-Plus 流程对比图,直观展示 CRUD 的差异和执行流程。
你想让我画吗?

Prompt:

什么是数据库连接池

Response:

数据库连接池(Database Connection Pool)是 管理和复用数据库连接的一种技术,目的是提升数据库操作效率,减少频繁创建和关闭连接的开销。下面我帮你详细梳理:


1️⃣ 概念

  • 数据库连接:应用程序与数据库之间的通信通道,每次操作数据库都需要建立连接。
  • 问题
    • 每次数据库操作都建立连接 → 性能消耗大
    • 数据库同时连接数有限 → 容易资源耗尽
  • 解决方案连接池
    • 事先创建一定数量的数据库连接放入池中
    • 应用程序需要使用时从池中获取连接,用完再归还池中,而不是关闭
    • 避免频繁创建和销毁连接,提高性能

2️⃣ 工作原理

  1. 初始化连接池:启动时创建固定数量的数据库连接(可配置最小、最大连接数)。
  2. 获取连接:应用程序向连接池申请数据库连接。
  3. 使用连接:应用程序执行 SQL。
  4. 归还连接:执行完毕将连接归还池中,不关闭连接。
  5. 连接池管理
    • 超时回收空闲连接
    • 异常连接检测与重建
    • 动态扩展或缩减连接数量

3️⃣ 优点

  • 性能提升:避免频繁创建和销毁连接的开销
  • 资源复用:充分利用有限的数据库连接数
  • 管理方便:支持最大连接数、最小连接数、空闲检测等配置
  • 提高系统稳定性:防止数据库连接耗尽

4️⃣ 常用连接池

连接池 特点
HikariCP 高性能,Spring Boot 默认推荐
Druid 阿里开源,监控和统计功能丰富
C3P0 老牌连接池,功能全面,但性能一般
Tomcat JDBC Pool 与 Tomcat 集成好,配置简单

5️⃣ Spring Boot 中使用数据库连接池

Spring Boot 默认支持 HikariCP:

1
2
3
4
5
6
7
8
9
10
spring:
datasource:
url: jdbc:mysql://localhost:3306/testdb
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 10
minimum-idle: 5
idle-timeout: 30000

使用连接池后,开发者只需要通过 DataSource 获取连接,不必手动管理连接生命周期,Spring Boot 会自动管理。


💡 总结一句话:

数据库连接池就是“连接的缓存”,提高数据库操作效率并节约资源。


如果你需要,我可以画一张 数据库连接池工作原理图,让你一眼看懂连接获取、使用、归还的流程。
你想让我画吗?

Prompt:

基于mybatis的增删改查操作

Response:

好的,我给你整理一份 基于 MyBatis 的增删改查(CRUD)操作示例,用最常见的方式(XML 映射或注解方式),假设有一张 User 表:

1
2
3
4
5
6
CREATE TABLE user (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(100),
age INT
);

1️⃣ 实体类 User.java

1
2
3
4
5
6
7
8
public class User {
private Integer id;
private String username;
private String email;
private Integer age;

// getter & setter
}

2️⃣ Mapper 接口 UserMapper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import org.apache.ibatis.annotations.*;
import java.util.List;

public interface UserMapper {

// 插入用户(返回影响行数)
@Insert("INSERT INTO user(username, email, age) VALUES(#{username}, #{email}, #{age})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insertUser(User user);

// 根据 id 删除用户
@Delete("DELETE FROM user WHERE id = #{id}")
int deleteUserById(Integer id);

// 根据 id 更新用户
@Update("UPDATE user SET username=#{username}, email=#{email}, age=#{age} WHERE id=#{id}")
int updateUser(User user);

// 根据 id 查询用户
@Select("SELECT * FROM user WHERE id = #{id}")
User getUserById(Integer id);

// 查询所有用户
@Select("SELECT * FROM user")
List<User> getAllUsers();
}

注解方式:方便快速开发;
XML 方式:适合复杂 SQL 和动态 SQL。


3️⃣ Mapper XML 示例(可选)

UserMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<mapper namespace="com.example.mapper.UserMapper">

<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user(username, email, age) VALUES(#{username}, #{email}, #{age})
</insert>

<delete id="deleteUserById" parameterType="int">
DELETE FROM user WHERE id = #{id}
</delete>

<update id="updateUser" parameterType="User">
UPDATE user SET username=#{username}, email=#{email}, age=#{age} WHERE id=#{id}
</update>

<select id="getUserById" parameterType="int" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>

<select id="getAllUsers" resultType="User">
SELECT * FROM user
</select>

</mapper>

4️⃣ 使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

@Autowired
private UserMapper userMapper;

public void testCRUD() {
// 插入
User user = new User();
user.setUsername("Alice");
user.setEmail("alice@example.com");
user.setAge(25);
userMapper.insertUser(user);
System.out.println("Inserted user ID: " + user.getId());

// 查询
User u = userMapper.getUserById(user.getId());
System.out.println(u.getUsername());

// 更新
u.setAge(26);
userMapper.updateUser(u);

// 删除
userMapper.deleteUserById(u.getId());
}
}

✅ 总结:

  • 插入insert / @Insert
  • 删除delete / @Delete
  • 更新update / @Update
  • 查询select / @Select

如果你愿意,我可以帮你再写一份 MyBatis-Plus 版本 CRUD 对比示例,让你一眼看出 MyBatis 和 MP 的差异。
你想让我写吗?

Prompt:

Mybatis#{…},${…}区别

Response:

MyBatis 中,#{...}${...} 经常被用在 SQL 映射里,但它们的本质和用途完全不同,理解好差异非常重要,尤其在面试中。下面我帮你详细梳理:


1️⃣ #{...} —— 预编译参数占位符

  • 作用:用于 预编译 SQL,防止 SQL 注入。
  • 执行原理
    1. MyBatis 会将 #{参数} 替换成 ? 占位符。
    2. 然后通过 PreparedStatement 设置参数值。
  • 特点
    • 自动处理 类型转换
    • 自动防止 SQL 注入
    • 适合 值类型传参
  • 示例
1
2
@Select("SELECT * FROM user WHERE username = #{username} AND age = #{age}")
User getUser(@Param("username") String name, @Param("age") int age);

生成的 SQL 大概是:

1
SELECT * FROM user WHERE username = ? AND age = ?

然后通过 PreparedStatement 设置参数值。


2️⃣ ${...} —— 字符串替换占位符

  • 作用:直接将 ${参数} 替换成字符串 拼接到 SQL 中
  • 执行原理
    • MyBatis 在生成 SQL 时 直接做文本替换
  • 特点
    • 不会预编译,存在 SQL 注入风险
    • 常用于 动态表名、列名、排序字段
    • 不会自动处理类型。
  • 示例
1
2
@Select("SELECT * FROM user ORDER BY ${orderBy} LIMIT ${limit}")
List<User> getUsers(@Param("orderBy") String orderBy, @Param("limit") int limit);

如果传入 orderBy = "age"limit = 10,生成 SQL 为:

1
SELECT * FROM user ORDER BY age LIMIT 10

3️⃣ 总结对比

特性 #{...} ${...}
参数替换方式 预编译,占位符 ? 字符串拼接
安全性 高,防 SQL 注入 低,容易注入
适用场景 值类型传参 表名、列名、排序等 SQL 片段
类型处理 自动处理 不处理
示例 WHERE age = #{age} ORDER BY ${columnName}

💡 面试小技巧:

  • 遇到用户输入的值一定用 #{}
  • 只在 动态 SQL无法用预编译处理的情况 才用 ${}

如果你想,我可以画一张 #{} vs ${} 参数传递流程图,让区别更直观。
你想让我画吗?

Prompt:

@Insert(“INSERT INTO user(username, email, age) VALUES(#{username}, #{email}, #{age})”)
@Options(useGeneratedKeys = true, keyProperty = “id”)
int insertUser(User user);这里#{username}, #{email}, #{age})是属性名还是表的字段名啊

Response:

在你给的这段 MyBatis 注解 SQL 中:

1
2
3
@Insert("INSERT INTO user(username, email, age) VALUES(#{username}, #{email}, #{age})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insertUser(User user);
  • usernameemailageVALUES(#{...}) 里指的是 实体类 User 的属性名,不是数据库表的字段名。
  • MyBatis 会根据 Java Bean 的 getter/setter 获取属性值,然后把值填充到 SQL 里的占位符中(通过预编译 ?)。
  • SQL 左边的 user(username, email, age) 才是数据库表的 字段名

📌 对比总结:

位置 含义
user(username, email, age) 数据库表 user 的字段名
#{username}, #{email}, #{age} Java 实体类 User 的属性名

所以,MyBatis 允许字段名和属性名不一致,只要在实体类属性上用 @TableField("表字段名")<resultMap> 映射即可。

如果你想,我可以画一张 MyBatis 注解 SQL 属性-字段映射示意图,让这个概念更清楚。
你想让我画吗?

Prompt:

@param注解

Response:

MyBatis 中,@Param 注解用于 给 SQL 参数起别名,特别是在接口方法有 多个参数 时,方便在 SQL 映射中引用。下面详细讲解:


1️⃣ 基本用法

1
2
@Select("SELECT * FROM user WHERE username = #{name} AND age = #{age}")
User getUser(@Param("name") String username, @Param("age") int age);
  • @Param("name"):给方法参数 username 起别名 name
  • SQL 中使用 #{name} 绑定这个参数的值。
  • 必须给多个参数的接口方法使用 @Param,否则 MyBatis 默认只能用 #{param1}, #{param2}…引用。

2️⃣ 为什么需要 @Param

场景 1:多个参数

1
User getUserByUsernameAndEmail(String username, String email);
  • 如果不加 @Param,SQL 中需要写:
1
SELECT * FROM user WHERE username = #{param1} AND email = #{param2}
  • 可读性差,容易出错。

场景 2:增加可读性

1
User getUserByUsernameAndEmail(@Param("username") String username, @Param("email") String email);

SQL 中就可以写:

1
SELECT * FROM user WHERE username = #{username} AND email = #{email}
  • 可读性好,直观。

3️⃣ 单参数方法

  • 如果接口方法 只有一个参数,通常不需要 @Param
1
2
@Select("SELECT * FROM user WHERE id = #{id}")
User getUserById(Integer id);

4️⃣ 总结

特性 说明
用途 给接口方法参数起别名
适用 多参数方法,或者为了 SQL 可读性
SQL 中引用 #{别名}
不用 单参数方法可以省略

💡 面试提示:

  • 多参数方法 必须用 @Param 或者使用 实体对象封装参数
  • 避免直接用 #{param1}#{param2},可读性差。