day10 Java操作数据库

计划

  1. JDBC基础入门
  2. JDBC查询与增删改
  3. SQL注入与预编译SQL
  4. MyBatis快速入门
  5. MyBatis注解写法与XML映射
  6. SpringBoot配置文件与数据库连接池

笔记

1.JDBC概述

JDBC是什么
  • JDBC:Java DataBase Connectivity,使用Java语言操作关系型数据库的一套API
  • 本质:Sun公司定义的一套接口规范,数据库厂商提供驱动jar包实现接口
  • 执行原理:Java程序调用JDBC接口,真正执行的是数据库驱动中的实现类
Java操作数据库技术
技术 说明
JDBC 最底层、最基础的数据库操作技术
MyBatis 基于JDBC封装的持久层框架,简化数据库开发
MyBatisPlus 在MyBatis基础上进一步增强
Hibernate / SpringDataJPA ORM思想更强的持久层框架

2.JDBC查询数据

查询需求

基于JDBC实现用户登录,本质是执行一条带用户名和密码条件的查询语句:

1
select * from user where username = 'daqiao' and password = '123456';
准备数据表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
create table user(
id int unsigned primary key auto_increment comment 'ID,主键',
username varchar(20) comment '用户名',
password varchar(32) comment '密码',
name varchar(10) comment '姓名',
age tinyint unsigned comment '年龄'
) comment '用户表';

insert into user(id, username, password, name, age) values
(1, 'daqiao', '123456', '大乔', 22),
(2, 'xiaoqiao', '123456', '小乔', 18),
(3, 'diaochan', '123456', '貂蝉', 24),
(4, 'lvbu', '123456', '吕布', 28),
(5, 'zhaoyun', '12345678', '赵云', 27);
JDBC查询步骤
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/web", "root", "1234");

PreparedStatement pstmt = conn.prepareStatement(
"select * from user where username = ? and password = ?");
pstmt.setString(1, "daqiao");
pstmt.setString(2, "123456");

ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
Integer id = rs.getInt("id");
String username = rs.getString("username");
String password = rs.getString("password");
String name = rs.getString("name");
Integer age = rs.getInt("age");
System.out.println(id + "," + username + "," + password + "," + name + "," + age);
}

rs.close();
pstmt.close();
conn.close();
ResultSet结果集
  • ResultSet:封装DQL查询语句返回的结果
  • next():移动光标到下一行,有数据返回true,没有数据返回false
  • getXxx(…):获取当前行指定列的数据,推荐使用列名获取

3.预编译SQL与SQL注入

静态SQL与预编译SQL
写法 示例 问题
静态SQL username = 'daqiao' 参数写死,灵活性差
字符串拼接SQL 拼接用户输入 容易产生SQL注入
预编译SQL username = ? 安全、性能更高,推荐使用
PreparedStatement
1
2
3
4
5
PreparedStatement pstmt = conn.prepareStatement(
"select * from user where username = ? and password = ?");
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
SQL注入
  • SQL注入:通过控制输入内容,修改原本SQL语句含义,达到绕过校验或攻击服务器的目的
  • 典型场景:登录功能中,如果使用字符串拼接SQL,输入特殊内容可能使条件恒成立
  • 解决方式:使用预编译SQL,用户输入会被当成普通参数值处理,不再改变SQL结构

4.JDBC增删改数据

执行DML语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/web", "root", "1234");

String sql = "update user set password = ?, age = ? where id = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "123456");
pstmt.setInt(2, 25);
pstmt.setInt(3, 1);

int rows = pstmt.executeUpdate();
System.out.println(rows);

pstmt.close();
conn.close();
executeQuery与executeUpdate
方法 执行语句 返回值
executeQuery() DQL查询语句 ResultSet结果集
executeUpdate() DML增删改语句 影响的记录数

5.MyBatis概述

MyBatis是什么
  • MyBatis:一款优秀的持久层框架,用于简化JDBC开发
  • 持久层:数据访问层,负责操作数据库
  • 框架:半成品软件,提供通用基础代码,提高开发效率
JDBC的不足
  1. 数据库连接信息硬编码在Java代码中
  2. 查询结果解析和对象封装繁琐
  3. 每次操作都要创建和关闭连接,资源浪费明显
MyBatis的改进
  1. 数据库连接信息放到SpringBoot配置文件中
  2. 查询结果由MyBatis自动映射为Java对象
  3. 底层使用数据库连接池复用连接

6.MyBatis快速入门

使用步骤
  1. 创建SpringBoot工程,引入mybatis-spring-boot-startermysql-connector-j
  2. 创建数据库表,并创建与表字段对应的实体类
  3. 在配置文件中配置数据库连接信息
  4. 编写Mapper接口,并在接口上添加@Mapper
  5. 选择注解方式或XML方式编写SQL
  6. 在业务代码或测试代码中注入Mapper接口,调用方法操作数据库
核心关系
组成 作用 复习重点
实体类 封装表中一行数据 属性名尽量与字段名一致
Mapper接口 定义数据库操作方法 方法名表达清楚要执行的操作
SQL语句 真正操作数据库 可写在注解中,也可写在XML中
配置文件 配置数据源和MyBatis行为 数据库连接四要素、日志输出
Mapper接口基本模板
1
2
3
4
5
6
7
8
9
10
11
12
13
@Mapper
public interface UserMapper {
List<User> findAll();

int deleteById(Integer id);

int insert(User user);

int update(User user);

User findByUsernameAndPassword(@Param("username") String username,
@Param("password") String password);
}

细节

  • Mapper接口不需要自己写实现类,MyBatis运行时会生成代理对象
  • @Mapper的作用是让MyBatis识别该接口,并把代理对象交给Spring IOC容器管理
  • DML语句可以用int作为返回值,表示影响的记录数
  • 查询多条数据用List<实体类>,查询单条数据用实体类

7.MyBatis注解写法

适用场景

注解写法适合简单SQL,例如单表的基础增删改查。优点是直观、文件少;缺点是复杂SQL写在Java注解中可读性差。

查询
1
2
3
4
5
6
@Select("select * from user")
List<User> findAll();

@Select("select * from user where username = #{username} and password = #{password}")
User findByUsernameAndPassword(@Param("username") String username,
@Param("password") String password);

细节

  • @Select用于写查询语句
  • 多条件查询时,建议使用@Param给参数命名,SQL中通过#{参数名}取值
  • 查询结果字段名与实体类属性名一致时,MyBatis可以自动封装
删除
1
2
@Delete("delete from user where id = #{id}")
int deleteById(Integer id);

细节

  • #{id}不是字符串拼接,而是预编译占位符
  • 返回值int表示删除了几条记录
新增
1
2
@Insert("insert into user(username, password, name, age) values(#{username}, #{password}, #{name}, #{age})")
int insert(User user);

细节

  • 参数是对象时,#{username}表示调用对象的getUsername()取值
  • 自增主键字段通常不需要手动插入
  • 字段顺序要与values中的参数顺序保持一致
修改
1
2
@Update("update user set username = #{username}, password = #{password}, name = #{name}, age = #{age} where id = #{id}")
int update(User user);

细节

  • 修改语句必须注意where条件,缺少条件会更新整张表
  • 修改对象必须包含用于定位数据的主键值

8.MyBatis XML映射写法

适用场景

XML写法适合复杂SQL,例如多条件动态查询、多表查询、复杂字段映射。优点是SQL集中、可读性更好;缺点是接口和XML之间要严格对应。

XML开发规范
规范 要求
文件位置 XML映射文件与Mapper接口包路径保持一致
文件名称 XML文件名与Mapper接口名一致,如UserMapper.xml
namespace 必须写Mapper接口的全限定名
SQL的id 必须与Mapper接口方法名一致
resultType 查询返回单条记录要封装成的实体类类型
XML基本模板
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhang.mapper.UserMapper">

</mapper>
查询写法
1
2
3
4
5
6
7
<select id="findAll" resultType="com.zhang.pojo.User">
select * from user
</select>

<select id="findByUsernameAndPassword" resultType="com.zhang.pojo.User">
select * from user where username = #{username} and password = #{password}
</select>

细节

  • resultType写的是单条记录封装成什么类型,不是List<User>
  • 如果Mapper方法返回List<User>,XML中仍然写resultType="com.zhang.pojo.User"
  • id必须和接口方法名一致,否则MyBatis找不到对应SQL
增删改写法
1
2
3
4
5
6
7
8
9
10
11
12
13
<insert id="insert">
insert into user(username, password, name, age)
values (#{username}, #{password}, #{name}, #{age})
</insert>

<update id="update">
update user set username = #{username}, password = #{password}, name = #{name}, age = #{age}
where id = #{id}
</update>

<delete id="deleteById">
delete from user where id = #{id}
</delete>

细节

  • <insert><update><delete>标签一般不需要写resultType
  • DML执行后返回影响行数,Mapper接口方法可以写int
  • XML和注解不要同时给同一个方法配置SQL,否则会冲突或造成理解混乱

9.注解写法与XML写法对比

对比点 注解写法 XML写法
SQL位置 Mapper接口方法上 resources下的Mapper.xml中
适合场景 简单增删改查 复杂SQL、动态SQL、多表查询
优点 写法直接,文件少 SQL更清晰,便于维护复杂语句
缺点 SQL复杂时可读性差 配置规则多,需要接口和XML对应
选择建议 入门和简单SQL优先使用 复杂业务SQL优先使用

10.MyBatis参数传递细节

#{...}${...}
符号 本质 结果 使用建议
#{...} 参数占位符 会替换为?,走预编译SQL 传递普通参数时强烈推荐
${...} 字符串拼接 直接拼接到SQL语句中 只有动态表名、字段名等特殊场景才考虑
为什么推荐#{...}
  1. 可以防止SQL注入
  2. 可以使用预编译SQL,提高性能
  3. 参数值会被当成普通数据处理,不会改变SQL结构
参数来源
Mapper方法参数 SQL中取值方式
单个简单参数 #{参数名}#{任意名}通常都能取到
多个简单参数 推荐使用@Param("名称")后通过#{名称}取值
对象参数 通过#{属性名}取对象属性值

11.数据库连接池

连接池作用
  • 数据库连接池是一个容器,负责分配和管理数据库连接
  • 程序启动时,连接池中会提前创建一定数量的Connection对象
  • 执行SQL时从连接池获取连接,执行完毕后归还连接池
  • 空闲过久的连接可以被自动释放,减少连接泄露风险
连接池优点
  1. 资源重用
  2. 提升系统响应速度
  3. 避免频繁创建和销毁连接
  4. 降低数据库连接遗漏风险
常见连接池
连接池 说明
Hikari SpringBoot默认连接池,性能好
Druid 阿里巴巴开源连接池,功能强大
C3P0 早期常见连接池
DBCP Apache提供的连接池

12.SpringBoot配置文件

properties与yml
配置文件 格式 特点
application.properties key=value 简单直接,配置多时层级不够清晰
application.yml / application.yaml 缩进表示层级 简洁清晰,以数据为中心,推荐使用
yml语法规则
  1. 大小写敏感
  2. 数值前必须有空格作为分隔符
  3. 使用缩进表示层级关系
  4. 缩进不能使用Tab,只能使用空格
  5. 相同层级左侧对齐
  6. #表示注释
  7. 以0开头的值建议使用引号包裹,避免被识别为八进制
数据源配置模板
1
2
3
4
5
6
7
8
9
10
11
12
spring:
application:
name: springboot-mybatis-quickstart1
datasource:
url: jdbc:mysql://localhost:3306/db02
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 数据库密码

mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
MyBatis日志配置
1
2
3
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

开启后,控制台可以看到MyBatis执行的SQL语句、参数和结果,方便排查数据库操作问题。

13.复习时容易忽略的细节

  1. Mapper接口只定义方法,不写实现类,实现类由MyBatis动态代理生成
  2. @Mapper不能忘,否则Mapper接口不会被MyBatis识别并交给Spring管理
  3. 注解和XML是两种SQL配置方式,同一个方法不要两边都写SQL
  4. XML中的namespace必须等于Mapper接口全限定名
  5. XML中SQL标签的id必须等于Mapper接口的方法名
  6. 查询集合时,XML的resultType仍写集合中单个元素的类型
  7. DML语句返回int可以知道影响了几条记录
  8. 修改和删除一定要注意where条件,避免影响整张表
  9. 普通参数传递优先使用#{...},不要为了省事使用${...}
  10. 开启MyBatis日志后,可以在控制台观察最终执行的SQL和参数

14.核心知识点总结

知识点 说明
JDBC Java操作关系型数据库的底层API
DriverManager 获取数据库连接
Connection 数据库连接对象
PreparedStatement 预编译SQL执行对象
ResultSet 查询结果集对象
executeQuery() 执行DQL查询语句
executeUpdate() 执行DML增删改语句
SQL注入 通过输入改变SQL语义的攻击方式
MyBatis 简化JDBC开发的持久层框架
@Mapper 声明Mapper接口,交给MyBatis和Spring管理
注解写法 通过@Select、@Insert、@Update、@Delete在接口方法上写SQL
#{…} 参数占位符,生成预编译SQL
${…} 字符串拼接符,存在注入风险
XML映射 将SQL写在XML中,适合复杂SQL
Hikari SpringBoot默认数据库连接池
application.yml SpringBoot推荐配置文件格式