我们在学习完了数据库的基本操作后,希望和我们的Java程序建立连接,那么我们今天就来一探究竟JDBC是如何让Java程序与数据库建立连接的
1. 什么是JDBC
JDBC(Java Data Base Connectivity, Java数据库连接) 是Java程序和数据库之间的桥梁,包含了⼀套Java定义的用于执⾏SQL语句的接口,使开发者能够编写数据库的程序。JDBC 的主要作⽤是:与数据库建⽴连接、发送SQL语句和处理数据库执行结果。
2. 为什么要使用JDBC
⾸先回顾⼀下使⽤客户端操作数据库的过程,主要分为以下几步:
a. 确定数据库服务器的地址,端口号,指定用户名密码
b.连接到数据库服务
c. 发送SQL语句
d. 得到返回结果并显示
e. 断开连接
-
同样如果使用程序操作数据库也会经历以上几步,⼤家应该可以想到,为实现上述步骤,可以编写相应的代码实现数据库连接,发送SQL语句,处理结果并显示,最后关闭连接。
-
但是不同的数据库服务器对于同⼀个操作不论是协议还是参数都各有不同,如果让程序员自己去实现,那就必须针对不同的数据库进行编码实现,这个⼯作量和维护成本显然太大。
-
Java采取的做法是把以上操作步骤定义了相应的接⼝,具体的实现交给数据库厂商去做,Java程序员只需要按照需要调用接⼝中定义的方法即可,这样不论使用什么数据库,都对于Java程序没有任何影响,即便是换⼀个数据库,也只需要换⼀下相应厂商的实现依赖即可。
-
JDBC使⽤过程可以概括为:加载数据库厂商的驱动包、建立连接、创建Statement、执行SQL、 处理结果释放资源和关闭连接。
3. 使用JDBC
3.1 获取MySQL厂商的驱动包(依赖,JAR包)
由于数据库厂商的驱动包过多,不好查询,我们推荐一个网址,里面有所有的数据库厂商提供的驱动包:MAVEN
3.2 创建Maven⼯程
3.3 配置国内镜像
java
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>
阿⾥云公共仓库
</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
<mirror>
<id>central</id>
<mirrorOf>*</mirrorOf>
<name>aliyun central</name>
<url>https://maven.aliyun.com/repository/central</url>
</mirror>
3.4 加载数据库厂商的驱动包
java
//固定写法,其中Driver是一个Java实现的类
Class.forName("com.mysql.cj.jdbc.Driver");
3.5 建立连接
- 使用驱动管理类
DriverManager
的静态方法获取数据库连接
java
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/yu?"
"characterEncoding=utf8" +
"&allowPublicKeyRetrieval=true&useSSL=false",
"root",
"123456");
MySQL数据库连接URL格式:jdbc:mysql://127.0.0.1:3306/数据库名?characterEncoding=utf8&allowPublicKeyRetrieval=true&useSSL=false
其中:
jdbc:mysql://
-> 固定写法
127.0.0.1:3306/
-> IP+端口号,直接使用即可,无需修改
yu为我本地数据库
-> 根据自己要连接的数据库写即可
characterEncoding=utf8
-> 字符集编码
&allowPublicKeyRetrieval=true&useSSL=false
-> 默认即可
root
-> 本地数据库名
123456
-> 我的数据库连接密码
- 通过数据源
DataSource
对象获取,推荐在实际开发中应用这种方式
3.6 创建Statement
Statement是⽤于执⾏静态SQL语句并返回执行结果的对象
java
// 通过connection获取statement对象
Statement statement = connection.createStatement();
QQ20241125-232419
3.7 定义SQL语句
java
//可根据相关需求定义,此处仅是示例
String sql = "select * from student";
3.8 执行SQL
- 执行select查询时返回的是⼀个结果集,⽤ResultSet接收
java
//接受查询结果
ResultSet resultSet = statement.executeQuery(sql);
接受完后,我们可以对接受的结果集进行打印
java
//执行返回结果
while (resultSet.next()) {
long id = resultSet.getLong("id");
String name = resultSet.getString("name");
String sno = resultSet.getString("sno");
int age = resultSet.getInt("age");
short gender = resultSet.getShort("gender");
Date enrollDate = resultSet.getDate("enroll_date");
long classId = resultSet.getLong("class_id");
System.out.println(MessageFormat.format("学生编号={0}, 姓名={1}, 学号={2}, 年龄={3}, 性别={4}, 入学时间={5}, " +
"班级编号={6}", id, name, sno, age, gender, enrollDate, classId));
}
- 执行insert,update,delete操作时,返回的是受影响的⾏数,⽤int类型接收(int接受的值用于判断更新操作是否成功,1表示成功,0表示失败)
3.9 处理结果释放资源和关闭连接
在整个数据库访问过程中创建的对象都需要释放,包括:ResultSet,Statement和Connection,后创建的先释放,建议在try
catch 语句后的finally代码块中写入,以便程序无论最后是否执行成功,都能保证释放资源和关闭连接
java
if(resultSet!=null){
resultSet=null;
}
if(statement!=null){
statement =null;
}
if(connection!=null){
connection =null;
}
4. JDBC常用接口和类
4.1 DriverManager 和DataSource
- DataSource驱动管理类,⽤于管理JDBC驱动程序,可以从驱动程序中获取数据库连接,始于JDK1.1。
- DataSource数据源是DriverManager的替代⽅案,始于JDK1.4,是获取数据库连接的首选方法,推荐使用。
4.2 DriverManager 与DataSource的区别
- DriverManager和DataSource都可以获取到数据库连接,但它们之间存着着⼀些区别,主要在于连接的管理方式和资源利用效率
- 连接管理方式不同:
①DriverManager每次调用getConnection方法都会初始化⼀个新的连接(上文Driver类中,我们注意到过源码中就有new Driver()),使用完成后会关闭真实连接,导致资源浪费
②DataSource使用了连接池的技术,会在初始化时创建⼀定数量的数据库连接,这些连接可以重复使用,关闭时并不是真正关闭连接,而是将连接归还给连接池,以供后续使用,有效地提高资源利用率和和性能
4.3 Connection 数据库连接
数据库连接(会话)对象,在连接的上下文中执行SQL语句并返回结果
4.3.1 Statement对象
- ⽤于执⾏静态SQL语句并返回执⾏结果,由于只能执⾏静态语句,所以这⾥会有⼀个问题,假设⼀个语句中需要动态的参数,⽐如where⼦句中的条件,那么只能通过字符串拼接的⽅式组装完成的SQL语句,比如:
java
String sql = "select * from student where name = '" + name + "' and class_id =
" + classId;
- 字符串拼接形式构造SQL语句时,如果不处理参数中的特殊字符就会造成SQL注⼊,这是⼀个非常严重的安全性问题。
4.3.2 SQL注入
4.3.3 PreparedStatement
- 预编译SQL语句对象,SQL语句被预编译并存储在PreparedStatement对象中,可以使用该对象多次执行SQL语句,同时可以解决SQL注入问题。
示例:通过Connection对象获取到PreparedStatement对象,需要传⼊SQL模板,动态参数用占位符?
表示
java
// 获取了个处理SQL的PrepareStatement对象
PreparedStatement preparedStatement = connection.prepareStatement("select id,
name, sno, age, gender, enroll_date, class_id from student where name = ? and
class_id = ?");
- 为动态参数设置真实值,下标从1开始
java
// ⽤真实值去替换占位符
preparedStatement.setString(1, "宋江");
preparedStatement.setLong(2, 2);
- 执行SQL语句
java
// select 操作
ResultSet resultSet = statement.executeQuery();
// insert, update, delete操作
//増 删 改 返回的是受影响的行数
int result = statement.executeUpdate();
//1 表示插入成功,0表示错误
4.3.4 CallableStatement
用于执行SQL存储过程的接⼝。
4.3.5 executeQuery()
执行结果返回的是⼀个结果集,通常⽤于select操作
4.3.6 executeUpdate()
执行结果返回的是⼀个整形,通常用于insert,update,delete操作
4.3.7 ResultSet结果集
- 是⼀个查询结果集的数据表,通常由执行查询操作的语句生成。
- ResultSet对象维护了⼀个指向当前数据行的游标,最初游标位于第⼀行之前,调用next方法将游标移动到下⼀行,当ResultSet中没有更多的数据行时返回false,所以可以在while循环中使用它来遍历结果集。
- ResultSet接⼝提供了getter方法(getBoolean、getLong等),用于从当前行检索列值,可以使用列的索引号或列的名称来检索值。⼀般来说,使用列索引会更有效率,索引编号从1开始,按照从左到右的顺序读取。
5. 示例
查询名字为唐三藏的信息
java
import com.mysql.cj.jdbc.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.*;
import java.text.MessageFormat;
import java.util.Scanner;
public class TestDataSource {
public static void main(String[] args) {
// 定义MYSQL数据源对象
MysqlDataSource mysqlDataSource = new MysqlDataSource();
// 设置数据库连接串
mysqlDataSource.setURL("jdbc:mysql://127.0.0.1:3306/yu?characterEncoding=utf8&allowPublicKeyRetrieval=true&useSSL=false");
// 用户名
mysqlDataSource.setUser("root");
// 密码
mysqlDataSource.setPassword("123456");
// 定义JDBC的数据源对象
DataSource dataSource = mysqlDataSource;
// 定义数据库连接对象
Connection connection = null;
// 定义预处理SQL执行对象
PreparedStatement statement = null;
// 定义结果集对象
ResultSet resultSet = null;
try {
// 1. 通过数据源对象获取数据库连接
connection = dataSource.getConnection();
// 定义SQL语
String sql = "select id, name, sno, age, gender, enroll_date, class_id from student where name = ?";
// 2. 预处理SQL执行对象
statement = connection.prepareStatement(sql);
// 接收用户的输入
System.out.println("请输入学生姓名:");
Scanner scanner = new Scanner(System.in);
String inName = scanner.next();
// 3. 用真实的值替换占位符
statement.setString(1, inName);
// 4. 执行SQL 获取结果
resultSet = statement.executeQuery();
// 5. 遍历结果集
// 6. 遍历结果集,获取数据行
while (resultSet.next()) {
// 获取ID列的值
long id = resultSet.getLong(1);
// 获取name列的值
String name = resultSet.getString(2);
String sno = resultSet.getString(3);
int age = resultSet.getInt(4);
byte gender = resultSet.getByte(5);
Date enrollDate = resultSet.getDate(6);
long classId = resultSet.getLong(7);
System.out.println(MessageFormat.format("学生编号={0}, 姓名={1}, 学号={2}, 年龄={3}, 性别={4}, 入学时间={5}, " +
"班级编号={6}", id, name, sno, age, gender, enrollDate, classId));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 释放结果集对象
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
// 释放Statement
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
// 关闭数据库连接
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}