1 学习目标
- 了解JDBC的概念
- 重点掌握JDBC的CRUD
- 重点掌握JDBC的各个对象的使用
2 GIT
- 查看安装手册
3 JDBC概述
3.1 数据的持久化
-
持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用 。大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘 上加以"固化",而持久化的实现过程大多通过各种关系数据库来完成。
-
持久化的主要应用是将内存中的数据存储在关系型数据库中,当然也可以存储在磁盘文件、XML数据文件中。
3.2 JAVA中的数据存储技术
-
在Java中,数据库存取技术可分为如下几类:
-
JDBC直接访问数据库
-
JDO (Java Data Object )技术
-
第三方O/R工具,如Hibernate, Mybatis 等
-
-
JDBC是java访问数据库的基石,JDO、Hibernate、MyBatis等只是更好的封装了JDBC。
3.3 JDBC介绍
- JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口 (一组API),定义了用来访问数据库的标准Java类库,(java.sql,javax.sql )使用这些类库可以以一种标准的方法、方便地访问数据库资源。
- JDBC为访问不同的数据库提供了一种统一的途径,为开发者屏蔽了一些细节问题。
- JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统,这样就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。
- 如果没有JDBC,那么Java程序访问数据库时是这样的:
- 有了JDBC,Java程序访问数据库时是这样的:
3.4 JDBC体系结构
- JDBC接口(API)包括两个层次:
- 面向应用的API:Java API,抽象接口,供应用程序开发人员使用(连接数据库,执行SQL语句,获得结果)。
- 面向数据库的API:Java Driver API,供开发商开发数据库驱动程序用。
JDBC是sun公司提供一套用于数据库操作的接口,java程序员只需要面向这套接口编程即可。
不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。 ------------面向接口编程
3.5 JDBC程序编写步骤
4 获取数据库连接
4.1 Driver接口实现类
4.1.1 Driver接口介绍
-
java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口。这个接口是提供给数据库厂商使用的,不同数据库厂商提供不同的实现。
-
在程序中不需要直接去访问实现了 Driver 接口的类,而是由驱动程序管理器类(java.sql.DriverManager)去调用这些Driver实现。
- Oracle的驱动:oracle.jdbc.driver.OracleDriver
- mySql的驱动: com.mysql.jdbc.Driver
4.1.2 导入jar包
①在01-JDBCDemo模块下的lib目录下,添加mysql的驱动jar包
②但是此时这个jar包还不能使用,我们需要将jar包引入到项目,操作很简单,只需要选中jar包,然后右键,点击Add as Library...选项
③在弹出的选框框中点击OK即可
④那么此时的jar包已经是可以展开的模式了,说明已经导入成功
4.1.3 加载与注册JDBC驱动
-
加载驱动:加载 JDBC 驱动需调用 Class 类的静态方法 forName(),向其传递要加载的 JDBC 驱动的类名
- Class.forName("com.mysql.jdbc.Driver");
-
注册驱动:DriverManager 类是驱动程序管理器类,负责管理驱动程序
- 使用DriverManager.registerDriver(com.mysql.jdbc.Driver)来注册驱动
- 通常不用显式调用 DriverManager类的 registerDriver() 方法来注册驱动程序类的实例,因为 Driver 接口的驱动程序类都包含了静态代码块,在这个静态代码块中,会调用 DriverManager.registerDriver() 方法来注册自身的一个实例。下图是MySQL的Driver实现类的源码:
4.1.4 TestConnection代码实现
①在01-JDBCDemo模块下的jdbc包中,找到TestConnection类,在该类中进行代码编写
②在该类中1.加载驱动处书写如下代码:
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
4.2 URL
4.2.1 URL组成结构
- JDBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,从而建立到数据库的连接。
- JDBC URL的标准由三部分组成,各部分间用冒号分隔。
- jdbc:子协议:子名称
- 协议:JDBC URL中的协议总是jdbc
- 子协议:子协议用于标识一个数据库驱动程序
- 子名称 :一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库 提供足够的信息。包含主机名 (对应服务端的ip地址),端口号,数据库名
- 举例:
-
注意:如果JDBC程序与服务器端的字符集不一致,会导致乱码,那么可以通过参数指定服务器端的字符集
javajdbc:mysql://localhost:3306/hr?useUnicode=true&characterEncoding=utf8
4.2.2 TestConnection代码实现
- 在TestConnection中的2.1指定URL,确定要连接哪个数据库处,实现URL的定义
java
//2.1指定URL,确定要连接哪个数据库
String url = "jdbc:mysql://localhost:3306/hr?useUnicode=true&characterEncoding=utf8";
4.3 用户名和密码
- 可以调用 DriverManager 类的 getConnection() 方法建立到数据库的连接
4.4 建立连接
java
package jdbc;
import java.sql.*;
/**
* 用于测试JDBC获取数据库链接是否通畅的案例
*/
public class TestConnection {
public static void main(String[] args) throws Exception {
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取和数据库的连接
//2.1指定URL,确定要连接哪个数据库
String url = "jdbc:mysql://localhost:3306/hr?useUnicode=true&characterEncoding=utf8";
//2.2指定使用的用户名
String user = "root";
//2.3指定使用的密码
String pwd = "root";
//2.4调用DriverManager类的getConnection()方法建立到数据库的连接
Connection conn = DriverManager.getConnection(url, user, pwd);
System.out.println("连接成功~~");
}
}
5 使用PreparedStatement实现操作
5.1 PreparedStatement介绍
- 可以通过调用 Connection 对象的 preparedStatement(String sql) 方法获取 PreparedStatement 对象
- PreparedStatement 接口是 Statement 的子接口,它表示一条预编译过的 SQL 语句
- PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表示,调用 PreparedStatement 对象的 setXxx() 方法来设置这些参数. setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值
- 调用PreparedStatement对象的以下方法可以执行增删改查操作:
- 执行查询SQL时,调用executeQuery()方法,会返回ResultSet对象,将结果集封装在该对象中
- 执行修改SQL(包括删除记录,修改记录,增添记录)时,调用executeUpdate()方法,会返回int类型数据,将修改的记录数返回
5.2 向regions表中添加记录
- 在regions表中插入一条记录: region_id为5,region_name为"神之国度"
sql
INSERT INTO regions(region_id,region_name)
VALUES(null,'神之国度')
- 代码如下:
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
/**
* 用于测试JDBC添加记录
*/
public class TestInsert {
private static Connection conn = null;
private static PreparedStatement ps = null;
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/hr?useUnicode=true&characterEncoding=utf8";
String user = "root";
String pwd = "root";
conn = DriverManager.getConnection(url, user, pwd);
System.out.println("连接成功~~");
//1.获取PreparedStatement的实例
//1.1定义sql语句
String sql = "INSERT INTO regions(region_id,region_name) VALUES(null,'神的国度')";
//1.2创建PreparedStatement实例,并接受sql语句作为参数
ps = conn.prepareStatement(sql);
//1.3通过PreparedStatement调用executeUpdate()方法执行查询操作
int rows = ps.executeUpdate();
System.out.println(rows > 0 ? "添加" + rows + "条记录成功!" : "添加失败!");
//2.释放资源
rs.close();
ps.close();
}
}
5.3 修改regions表中记录
- 将regions表中region_id为5的记录中的region_name值修改为'魔之国度'
UPDATE regions SET region_name = "魔之国度" where region_id =5;
- 代码如下
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
/**
* 用于测试JDBC修改记录
*/
public class TestUpdate {
private static Connection conn = null;
private static PreparedStatement ps = null;
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/hr?useUnicode=true&characterEncoding=utf8";
String user = "root";
String pwd = "root";
conn = DriverManager.getConnection(url, user, pwd);
System.out.println("连接成功~~");
//1.获取PreparedStatement的实例
//1.1定义sql语句
String sql = "UPDATE regions SET region_name = '魔之国度' where region_id =5";
//1.2创建PreparedStatement实例,并接受sql语句作为参数
ps = conn.prepareStatement(sql);
//1.3通过PreparedStatement调用executeUpdate()方法执行修改操作
int rows = ps.executeUpdate();
System.out.println(rows > 0 ? "修改" + rows + "条记录成功!" : "修改失败!");
//2.释放资源
rs.close();
ps.close();
}
}
5.4 删除regions表中记录
- 将regions表中region_id为5的记录删除
DELETE FROM regions WHERE region_id =5;
- 代码如下
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
/**
* 用于测试JDBC删除记录
*/
public class TestDelete {
private static Connection conn = null;
private static PreparedStatement ps = null;
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/hr?useUnicode=true&characterEncoding=utf8";
String user = "root";
String pwd = "root";
conn = DriverManager.getConnection(url, user, pwd);
System.out.println("连接成功~~");
//1.获取PreparedStatement的实例
//1.1定义sql语句
String sql = "DELETE FROM regions WHERE region_id =5;";
//1.2创建PreparedStatement实例,并接受sql语句作为参数
ps = conn.prepareStatement(sql);
//1.3通过PreparedStatement调用executeUpdate()方法执行删除操作
int rows = ps.executeUpdate();
System.out.println(rows > 0 ? "删除" + rows + "条记录成功!" : "删除失败!");
//2.释放资源
rs.close();
ps.close();
}
}
5.5 查询regions表中的所有数据
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
/**
* 用于测试JDBC查询记录
*/
public class TestSelect {
private static Connection conn = null;
private static PreparedStatement ps = null;
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/hr?useUnicode=true&characterEncoding=utf8";
String user = "root";
String pwd = "root";
conn = DriverManager.getConnection(url, user, pwd);
System.out.println("连接成功~~");
//1.定义sql语句
String sql = "SELECT region_id,region_name FROM regions";
//2.创建PreparedStatement实例,并接受sql语句作为参数
ps = conn.prepareStatement(sql);
//3.通过PreparedStatement调用executeQuery()方法执行查询操作
ResultSet rs = ps.executeQuery();
//4.遍历结果集
while (rs.next()){ //while循环一次,迭代一行,遍历一行
int regionId = rs.getInt("region_id");//get一次得到一个单元格的数据
String regionName = rs.getString("region_name");
System.out.println(regionId + "\t" + regionName);
}
//5.释放资源
rs.close();
ps.close();
conn.close();
}
}
5.6 解决SQL注入
5.6.1 案例
- 查询regions表中指定的用户
sql
SELECT region_id,region_name
FROM regions
WHERE region_id = 1 AND region_name = 'Europe'
5.6.2 SQL注入问题演示
java
import java.sql.*;
import java.util.Scanner;
/**
* 用于测试JDBC的SQL注入问题
*/
public class TestSelect2 {
private static Connection conn = null;
private static PreparedStatement ps = null;
private static ResultSet rs = null;
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/hr?useUnicode=true&characterEncoding=utf8";
String user = "root";
String pwd = "root";
conn = DriverManager.getConnection(url, user, pwd);
System.out.println("连接成功~~");
//1.定义sql语句
int regionID = 2;
String regionName = "' or '1'='1";;
String sql = "SELECT region_id,region_name FROM regions where region_id = " + regionID + " AND region_name = '" + regionName + "'";
System.out.println("sql = " + sql);
//2.创建PreparedStatement实例,并接受sql语句作为参数
ps = conn.prepareStatement(sql);
//3.通过PreparedStatement调用executeQuery()方法执行查询操作
rs = ps.executeQuery();
//4.判断结果
System.out.println(rs.next() == true ? "记录存在!" : "记录不存在!");
//5.释放资源
rs.close();
ps.close();
conn.close();
}
}
5.6.3 SQL注入解决
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
/**
* 用于测试JDBC通过PreparedStatement解决SQL注入问题
*/
public class TestSelect3 {
private static Connection conn = null;
private static PreparedStatement ps = null;
private static ResultSet rs = null;
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/hr?useUnicode=true&characterEncoding=utf8";
String user = "root";
String pwd = "root";
conn = DriverManager.getConnection(url, user, pwd);
System.out.println("连接成功~~");
//1.定义sql语句
String sql = "SELECT region_id,region_name FROM regions where region_id = ? AND region_name = ?";
//2.创建PreparedStatement实例,并接受sql语句作为参数
ps = conn.prepareStatement(sql);
//3.为SQL骨架传入参数
ps.setInt(1, 1);
ps.setString(2, "' or '1'='1");
//4.通过PreparedStatement调用executeQuery()方法执行查询操作
rs = ps.executeQuery();
//5.遍历结果集
System.out.println(rs.next() == true ? "记录存在!" : "记录不存在!");
//6.释放资源
rs.close();
ps.close();
conn.close();
}
}
6 使用ResultSet封装结果集
6.1 ResultSet介绍
- 查询需要调用PreparedStatement 的 executeQuery() 方法,查询结果是一个ResultSet 对象
- ResultSet 对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet 接口由数据库厂商提供实现
- ResultSet 返回的实际上就是一张数据表。有一个指针指向数据表的第一条记录的前面。
- ResultSet 对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet 对象的 next() 方法移动到下一行。调用 next()方法检测下一行是否有效。若有效,该方法返回 true,且指针下移。
-
当指针指向一行时, 可以通过调用getXxx(int columnName) 获取每一列的值。
- 例如: getInt("region_id"), getString("region_name")
- 注意:Java与数据库交互涉及到的相关Java API中的索引都从1开始。
- ResultSet 接口的常用方法:
-
boolean next()
-
getString()
-
...
-
7 资源的释放
- 释放ResultSet, PreparedStatement,Connection。
- 数据库连接(Connection)是非常稀有的资源,用完后必须马上释放,如果Connection不能及时正确的关闭将导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。
8 JDBC的事务处理
8.1 JDBC管理事务
-
mysql默认是自动提交事务,每执行一条语句成功后,自动提交。
需要开启手动提交模式。
setAutoCommit(false);
-
提交事务
Connection连接对象.commit();
-
回滚事务
Connection连接对象.rollback();
8.2 代码
java
import java.sql.*;
/**
* 用于测试JDBC的事务处理
*/
public class TestTransaction {
private static Connection conn = null;
private static PreparedStatement ps = null;
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/hr?useUnicode=true&characterEncoding=utf8";
String user = "root";
String pwd = "root";
conn = DriverManager.getConnection(url, user, pwd);
System.out.println("连接成功~~");
//1.开启事务
conn.setAutoCommit(false);//取消自动提交模式,开始手动提交模式
//2.定义多条sql语句
String sql1 = "UPDATE regions SET region_name = '青岛' WHERE region_id = 6";
String sql2 = "UPDATE region SET region_name = '大冶' WHERE region_id = 4";
//3.创建PreparedStatement实例,并执行sql
try {
ps = conn.prepareStatement(sql1);
ps.executeUpdate();
System.out.println("第一条记录更新成功");
ps = conn.prepareStatement(sql2);
ps.executeUpdate();
System.out.println("第二条记录更新成功");
//4.提交事务
conn.commit();
} catch (SQLException e) {
//5.回滚事务
e.printStackTrace();
System.out.println("更新失败!");
conn.rollback();
}
//6.释放资源
ps.close();
conn.close();
}
}
9 总结
① JDBC 是什么?
JDBC 是 Java 数据库连接技术的简称,用于在 Java 程序中连接和操作关系型数据库。
② JDBC 驱动的作用是什么?
JDBC 驱动是一个软件模块,用于在 Java 程序中连接和操作关系型数据库。
③如何防止 SQL 注入攻击?
使用 PreparedStatement 和 SQL 参数化查询可以有效地防止 SQL 注入攻击。
④JDBC 中如何实现事务处理?
使用 JDBC 的事务处理功能可以确保数据库操作的原子性、一致性、隔离性和持久性,可以使用 Connection 对象的 setAutoCommit() 方法来关闭自动提交,并通过 commit() 和 rollback() 方法来实现事务的提交和回滚。