Web后端数据库
JDBC
-
介绍
JDBC,就是使用Java语言操作关系型数据库的一套API
![image-20251126195830697]()
-
查询数据
-
需求
觊觎JDBC实现用户登录功能
本直接就是觊觎JDBC程序,执行如下select语句,并将查询的结果输出到控制台
select * from user form where username = 'linchong' and password ='123456'; -
准备工作
-
创建一个Maven项目
![image-20251126210859405]()
-
创建一个数据库web,并在数据库里面创建User表
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);
-
-
代码实现
-
在pom.xml文件中引入依赖
<dependencies> <!-- MySQL JDBC driver --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.9.3</version> <scope>test</scope> </dependency> </dependencies> -
在src/main/test/java目录下编写测试类,定义测试方法
public class JDBCTest { /** * 编写JDBC程序, 查询数据 */ @Test public void testJdbc() throws Exception { // 获取连接 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/web", "root", "1234"); // 创建预编译的PreparedStatement对象 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()) { int id = rs.getInt("id"); String uName = rs.getString("username"); String pwd = rs.getString("password"); String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println("ID: " + id + ", Username: " + uName + ", Password: " + pwd + ", Name: " + name + ", Age: " + age); } // 关闭资源 rs.close(); pstmt.close(); conn.close(); } }上述的单元测试中,我们将用户名和密码都写死了,而这两个值应该是动态的,是通过页面来传递到服务端的.现在我们根据JUnit中的参数还测试进行单元测试
public class JDBCTest { /** * 编写JDBC程序, 查询数据 */ @ParameterizedTest @CsvSource({"daqiao,123456"}) public void testJdbc(String _username, String _password) throws Exception { // 获取连接 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/web", "root", "1234"); // 创建预编译的PreparedStatement对象 PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM user WHERE username = ? AND password = ?"); // 设置参数 pstmt.setString(1, _username); // 第一个问号对应的参数 pstmt.setString(2, _password); // 第二个问号对应的参数 // 执行查询 ResultSet rs = pstmt.executeQuery(); // 处理结果集 while (rs.next()) { int id = rs.getInt("id"); String uName = rs.getString("username"); String pwd = rs.getString("password"); String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println("ID: " + id + ", Username: " + uName + ", Password: " + pwd + ", Name: " + name + ", Age: " + age); } // 关闭资源 rs.close(); pstmt.close(); conn.close(); } }如果在测试时,需要传递一组参数,可以使用@CsvSource注解
-
-
代码剖析
-
ResultSet
ResultSet(结果集对象) : 封装了DQL查询语句查询的结果
- next(): 将光标从当前位置向前移动一行,并判断当前行是否为有效行,返回值为Boolean
- true:有效行,当前行有数据
- false: 无效行,当前行没有数据
- getXxx(...): 获取书库,可以根据列的编号获取,也可以根据列名获取(推荐)
while(resultSet.next()){ int id = resultSet.getInt("id"); //...省略 } - next(): 将光标从当前位置向前移动一行,并判断当前行是否为有效行,返回值为Boolean
-
预编译SQL
-
静态SQL(参数硬编码)
conn.prepareStatement("SELECT * FROM user WHERE username = 'daqiao' AND password = '123456'"); ResultSet resultSet = pstmt.executeQuery();这种就是讲参数值直接拼接在SQL语句中,参数值是写死的
-
预编译SQL(参数动态传递)
-
conn.prepareStatement("SELECT * FROM user WHERE username = ? AND password = ?"); pstmt.setString(1, "daqiao"); pstmt.setString(2, "123456"); ResultSet resultSet = pstmt.executeQuery();他没有将参数值写死,而是使用?占位,然后在指定占位符对应的值是多少
-
这种预编译的SQL,主要作用有两个
- 防止SQL注入
- 性能更高
-
-
-
-
增删改数据
-
需求
需求: 基于JDBC程序,执行如下update语句
SQL:
update user set password = '123456', gender = 2 where id = 1; -
代码实现
@ParameterizedTest @CsvSource({"1,123456,25"}) public void testUpdate(int userId, String newPassword, int newAge) throws Exception { // 建立数据库连接 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/web", "root", "1234"); // SQL 更新语句 String sql = "UPDATE user SET password = ?, age = ? WHERE id = ?"; // 创建预编译的PreparedStatement对象 PreparedStatement pstmt = conn.prepareStatement(sql); // 设置参数 pstmt.setString(1, newPassword); // 第一个问号对应的参数 pstmt.setInt(2, newAge); // 第二个问号对应的参数 pstmt.setInt(3, userId); // 第三个问号对应的参数 // 执行更新 int rowsUpdated = pstmt.executeUpdate(); // 输出结果 System.out.println(rowsUpdated + " row(s) updated."); // 关闭资源 pstmt.close(); conn.close(); }
-
Mybatis
-
介绍
MyBatis是一款优秀的 持久层 框架,用于简化JDBC的开发。
MyBatis本是 Apache的一个开源项目iBatis,2010年这个项目由apache迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
官网:https://mybatis.org/mybatis-3/zh/index.html
在上面我们提到了两个词:一个是持久层,另一个是框架。
- 持久层:指的是就是数据访问层(dao),是用来操作数据库的。
![image-20251127095217508]()
- 框架:是一个半成品软件,是一套可重用的、通用的、软件基础代码模型。在框架的基础上进行软件开发更加高效、规范、通用、可拓展。
通过Mybatis就可以大大简化原生的JDBC程序的代码编写,比如 通过
select * from user查询所有的用户数据,通过JDBC程序操作呢,需要大量的代码实现,而如果通过Mybatis实现相同的功能,只需要简单的三四行就可以搞定。![image-20251127095235803]()
-
快速入门
使用Mybatis查询所有用户数据
-
创建springboot,并导入Mybatis的起步依赖,MySQL的驱动,lombok
![image-20251127100528301]()
![]()
-
数据准备:创建用户表user,并创建对应的实体类User
-
用户表user创建
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); -
实体类:实体类的属性包与表中的字段名一一对应.实体类放在com.itheima.pojo包下
@Data @NoArgsConstructor @AllArgsConstructor public class User { private Integer id; //ID private String username; //用户名 private String password; //密码 private String name; //姓名 private Integer age; //年龄 }最好在这里重写toStrng方法
-
-
配置Mybatis
在application.properties中配置数据库的连接信息
#数据库访问的url地址 spring.datasource.url=jdbc:mysql://localhost:3306/web #数据库驱动类类名 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver #访问数据库-用户名 spring.datasource.username=root #访问数据库-密码 spring.datasource.password=root@1234记得修改自己的数据库信息
-
编写Mybatis程序:编写Mybatis的持久层接口,定义SQL语句(注解)
在创建出来的springboot工程中,在引导类所在包下,在创建一个包mapper.在mapper包下创建一个接口UserMapper, 这是一个持久层接口(Mybaits的持久层接口规范一般都叫做XxxMapper)
UserMapper接口的内容如下:
import com.itheima.pojo.User; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import java.util.List; @Mapper public interface UserMapper { /** * 查询全部 */ @Select("select * from user") public List<User> findAll(); }注解说明:
- @Mapper注解:表示是mybatis中的Mapper接口
程序运行时,框架会自动生成接口的实现类对象(代理对象),并给交Spring的IOC容器管理
- @Select注解:代表的就是select查询,用于书写select查询语句
-
单元测试
再创建出来的Springboot工程中,在src下的test目录下,已经自动帮我们创建好了测试类,并且在测试类上已经添加了注解@SpringBootTest , 代表该测试类已经与Spring Boot整合。
该测试类在运行的时候会自动通过引导类加载SPring的环境(IOC容器),我们要测试那个bean对象,就可以直接通过@Auto wired注解直接将其注入进行,然后就可以测试了。
@SpringBootTest class SpringbootMybatisQuickstartApplicationTests { @Autowired private UserMapper userMapper; @Test public void testFindAll(){ List<User> userList = userMapper.findAll(); for (User user : userList) { System.out.println(user); } } }测试类所在包,需要与引导类所在包相同
-
-
辅助配置
-
配置SQL提示
默认我们在UserMapper接口中加的
@Select注解中所编写的SQL语句是没有提示的.如果想让IDEA给我们提示对应的SQL语句,就需要在IDEA中配置与MYSQL数据库的链接.如下配置![image-20251127104915464]()
现在有提示了,可能IDEA咩有和数据库建立连接,不识别表信息
![image-20251127105118385]()
![image-20251127105140211]()
-
配置Mybatis日志输出
默认情况下,在Mybatis中,SQL语句执行的时候,我们看不见SQL的执行日志(在application.properties)加入如下配置,即可查看日志:
#mybatis的配置 mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
-
-
JDBC VS Mybatis
-
JDBC 缺点:
- url,username,password等相关的参数全部都硬编码在java代码中
- 查询结果的解析,封装比较繁琐
- 每一次操作数据库之前, 先获取连接, 操作完成后,关闭连接.频繁的获取连接,释放连接造成的资源浪费.
-
Mybaits解决方法:
- 数据库连接四要素(驱动,连接,用户名,密码), 都配置在springboot默认的配置文件application.properties中
- 查询结果的解析及其封装,由Mybatis自动完成映射封装,我们无需关注
- 在Mabatis中使用数据库连接池技术, 从而避免了频繁的创建连接,销毁连接而带来的资源浪费
-
对于Mybatis来说, 我们在开发持久层程序操作数据库时, 需要重点关注一下两个方面
-
application.properties
#驱动类名称 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver #数据库连接的url spring.datasource.url=jdbc:mysql://localhost:3306/web01 #连接数据库的用户名 spring.datasource.username=root #连接数据库的密码 spring.datasource.password=1234 -
Mapper接口(编写SQL语句)
@Mapper public interface UserMapper { @Select("select * from user") public List<User> list(); }
-
-
-
数据库的连接池
前面的Mybatis中,使用了数据库连接池技术,避免频繁的创建连接, 销毁连接而来带的资源浪费
-
介绍
-
数据库连接池的情况
![image-20251127110949448]()
客户端执行SQL语句:要先创建一个新的连接对象,然后执行SQL语句,SQL语句执行后又需要关闭连接对象从而释放资源,每次执行SQL时都需要创建连接、销毁链接,这种频繁的重复创建销毁的过程是比较耗费计算机的性能。
-
有数据库连接池的情况
数据库连接池是个容器,负责分配、管理数据库连接(Connection)- 程序在启动时,会在数据库连接池(容器)中,创建一定数量的Connection对象
允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
- 客户端在执行SQL时,先从连接池中获取一个Connection对象,然后在执行SQL语句,SQL语句执行完之后,释放Connection时就会把Connection对象归还给连接池(Connection对象可以复用)
释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接遗漏
- 客户端获取到Connection对象了,但是Connection对象并没有去访问数据库(处于空闲),数据库连接池发现Connection对象的空闲时间 > 连接池中预设的最大空闲时间,此时数据库连接池就会自动释放掉这个连接对象
数据库连接池的好处:
- 资源重用
- 提升系统响应速度
- 避免数据库连接遗漏
-
-
产品
如何实现数据库连接池
-
官方(sun)提供了数据库连接池标准(java.sql.DataSource接口)
-
功能: 获取连接
public Connection getConnection() throws SQLExceprtion; -
第三方组织必须按照DataSource接口实现
现在常见的数据库连接池:C3P0、DBCP、Druid、Hikari (springboot默认)
现在使用更多的是:Hikari、Druid (性能更优越)
Springboot默认使用HiKari(追光者)
Druid(德鲁伊)
- Druid连接池是阿里巴巴开源的数据库连接池项目
- 功能强大,性能优秀,是Java语言最好的数据库连接池之一
如果我们想把默认的数据库连接池切换为Druid数据库连接池,只需要完成以下两步操作即可:
参考官方地址:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
①. 在
pom.xml文件中引入依赖<dependency> <!-- Druid连接池依赖 --> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.19</version> </dependency>②. 在
application.properties中引入数据库连接配置spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.druid.url=jdbc:mysql://localhost:3306/web spring.datasource.druid.username=root spring.datasource.druid.password=1234配置完毕之后,我们再次运行单元测试,大家会看到控制台输出的日志中,已经将连接池切换为了 Druid连接池。
-
-
-
增删改查操作
-
删除
-
需求: 根据ID删除用户信息
-
SQL: delete from user where id = 5
-
Mapper接口方法
-
方法一:
-
/** - 根据id删除 / @Delete("delete from user where id = 5") public void deleteById();这种方法执行删除操作,调用deleteById方法只能删除id为5的用户信息,因为将id直接写死在代码中了,不可取
-
-
方法二
-
/** - 根据id删除 / @Delete("delete from user where id = #{id}") public void deleteById(Integer id);在Mybatis中,我们可以通过参数占位符#{...}来占位,在调用deleteById方法时,传递的参数值,最终会替换占位符
-
-
-
编写单元测试方法进行测试
在单元测试类中,增加如下的测试方法
@Test
public void testDeleteById(){
userMapper.deleteById(36);
} -
Mybatis中提供的符号有两个, 一个是${...},另一个是#{...}, 区别如下:
-
符号 说明 场景 优缺点 # 占位符。执行时,会将#{…}替换为?,生成预编译SQL 参数值传递 安全、性能高 (推荐) $ 拼接符。直接将参数拼接在SQL语句中,存在SQL注入问题 表名、字段名动态设置时使用 不安全、性能低
-
-
新增
-
需求:添加一个用户
-
SQL: insert into user(username,password,name,age) values('zhouyu','123456',周瑜,20);
-
Mapper接口:
/** * 添加用户 */ @Insert("insert into user(username,password,name,age) values(#{username},#{password},#{name},#{age})") public void insert(User user);如果在SQL中,我们需要传递多个参数,可以将多个参数封装到一个对象中,然后在SQL语句中, 我们可以通过#{对象属性名}的方式,获取到对象中封装的属性值
-
单元测试
在测试类中加入测试方法, 代码如下:@Test public void testInsert(){ User user = new User(); user.setUsername("admin"); user.setPassword("123456"); user.setName("管理员"); user.setAge(30); userMapper.insert(user); }
-
-
修改
-
需求:根据ID更新用户信息
-
SQL: update user set username = 'zhouyu', password = '123456', name = '周瑜', age = 20 where id = 1;
-
Mapper接口方法:
/** * 根据id更新用户信息 */ @Update("update user set username = #{username},password = #{password},name = #{name},age = #{age} where id = #{id}") public void update(User user); -
单元测试
在测试类中添加测试方法, 代码如下@Test public void testUpdate(){ User user = new User(); user.setId(6); user.setUsername("admin666"); user.setPassword("123456"); user.setName("管理员"); user.setAge(30); userMapper.update(user); }
-
-
查询
-
需求:根据用户名和密码查询用户信息
-
SQL:select* fromuser whereusername = 'zhouyu' and password = '123456'
-
Mapper接口方法:
- /** * 根据用户名和密码查询用户信息 */ @Select("select * from user where username = #{username} and password = #{password}") public User findByUsernameAndPassword(@Param("username") String username, @Param("password") String password); -单元测试:
@Test public void testFindByUsernameAndPassword(){ User user = userMapper.findByUsernameAndPassword("admin666", "123456"); System.out.println(user); }说明:基于官方骨架创建的springboot项目中,接口编译时会保留方法形参名,@Param注解可以省略 (#{形参名})。
-
-
-
XML映射配置
Mybatis的开发方式有两种方式:1.注解 2.XML
-
XML配置文件规范
使用Mybatis的注解方法,主要就是来完成一些简单的增删改查功能. 如果需要实现复杂的SQL功能,建议使用XML来配置映射语句,也就是将SQL语句写在XML配置文件中.
在Mybatis中使用XML硬核文件方式开发,需要符合一定的规范:
-
XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同的包下,(同名同包)
-
XML映射文件的namespace属性为Mapper接口全限定名一致
-
XML映射文件中SQL语句的id与Mapper接口中的方法名一致, 并且保持返回类型一致
![image-20251128111827239]()
select标签:就是用于编写select查询语句的
resultType属性,指的是查询返回的单条记录所封装的类型
-
-
XML配置文件实现
-
第一步:创建XML映射
![image-20251128112959580]()
![image-20251128113024094]()
-
第二步:编写XML映射文件
XML映射文件中的dtd约束, 直接从Mybatis官网中复制即可;或者直接AI生成
<?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=""> </mapper> -
第三步:配置
-
XML映射文件的namespace属性为Mapper接口全限定名
<?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.itheima.mapper.UserMapper"> </mapper> -
XML映射文件中sql语句的id与Mapper接口中的方法名一致, 并保持返回类型一致
<?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.itheima.mapper.UserMapper"> </mapper>一个接口方法对应的SQL语句,要么使用注解配置,要么使用XML配置,切不可同时配置。
-
-
-
MybatisX的使用
MybatisX是一款基于IDEA的快速开发Mybatis的插件,为效率而生。
MybatisX的安装:
![image-20251128114846146]()
-
SpringBoot配置文件
-
介绍
前面我们一直使用springboot项目创建完毕后自带的
application.properties进行属性的配置,而如果在项目中,我们需要配置大量的属性,采用properties配置文件这种key=value的配置形式,就会显得配置文件的层级结构不清晰,也比较臃肿。![image-20251128135252368]()
-
语法
yml 配置文件的基本语法:
- 大小写敏感
- 数值前面必须有空格, 作为分隔符
- 使用你缩进表示层级关系, 不允许使用tab键, 只能用空格
- 缩进保证元素左对齐
-
表示注解
yml常见的数据格式:
-
定义对象或Map集合
user: name: zhangsan age: 18 password: 123456 -
定义数组,list或Set集合
hobby: - java - game - sport在yml格式的配置文件中,如果配置项的值是以 0 开头的,值需要使用 '' 引起来,因为以0开头在yml中表示8进制的数据。










数据库连接池是个容器,负责分配、管理数据库连接(Connection)




浙公网安备 33010602011771号