JDBC简介
- JDBC:Java Database Connectivity,Java数据库连接
- JDBC为开发者使用数据库提供了统一的编程接口,它由一组java类和接口组成,是java程序与数据库系统通信的标准API。JDBC API使得开发人员可以使用纯java的方式来连接数据库,并执行操作。
- 不同厂商的数据库程序代码不同,无法使用一套代码来使用所有数据库,所以sun公司提出了一套API,凡是数据库想与java进行连接的,数据库厂商自己必须实现JDBC这套接口。对应的JDBC实现被称为此数据库的数据库驱动。
- 使用不同的数据库需要下载不同的数据库驱动包。
访问数据库流程
- 加载JDBC驱动程序
- 连接数据库
- 为数据库传递查询和更新指令
- 处理数据库 响应并返回 的结果
JDBC下载
- MySQL数据库驱动包下载地址:http://dev.mysql.com/downloads/connector/j/(虽然只有4.5M,但直接下载速度非常慢)
- 解压后得到 jar 文件,然后在对应的项目中导入该文件。
环境
- win10、eclipse、JDK11、Mysql8.0.21、JDBC8.0.21
- 注意:Mysql8以上版本的数据库连接方式与以下版本有所不同。
编程中的基本使用
建立数据库连接
代码
//加载JDBC驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//建立与数据库的连接(连接对象内部包含了Socket对象,是一个远程连接;比较耗时,)
//真正开发中使用连接池来管理连接对象
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/school"
+ "?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC","root","password");
注意:
(1)加载数据库驱动语句 适用于MySql8以上版本。
(2)MySQL 8.0 以上版本不需要建立 SSL 连接的,需要显示关闭。
(3)allowPublicKeyRetrieval=true 允许客户端从服务器获取公钥。
(4)最后还需要设置 中央标准时间。
(5)最后两个字符串应分别设置为登入数据库的 用户名 和 密码。
执行sql语句
- Statement接口
(1)用于执行静态SQL语句并返回它所生成结果的对象。
(2)三种Statement类:
[1] Statement:
实例对象通过createStatement创建,用于发送简单的SQL语句(不带参数的SQL语句,如果带参数可能会发生SQL注入)。
[2] PreparedStatement:
继承自Statement接口,实例对象通过prepareStatement创建,用于发送含有一个或多个输入参数的sql语句。PreparedStatement对象比Statement对象的效率更高,并且可以防止sql注入。一般都是用PrepareStatement。
[3] CallableStatement:
继承自PreparedStatement。由方法prePareCall创建,用于调用存储过程。
(3)常用的Statement方法:
execute(): 运行语句,返回 是否有结果集。
executeQuery(): 运行select语句,返回ResultSet结果集。
executeUpdate(): 运行insert/update/delete操作,返回 更新的行数。 - Statement执行对象
(1)使用场景:普通的不带参数的简单SQL语句。
(2)特点:每次的执行都需要编译SQL,效率低。
(3)代码示例:
//2. 使用Statement对象 执行sql语句,可能会发生sql注入
Statement stmt = conn.createStatement();
String sql = "insert into student values (103, \"王五\",2);"; //拼接sql语句
stmt.execute(sql); //执行
- PreparedStatement执行对象
(1)使用场景:执行具有一个或多个可变参数的SQL。
(2)特点:PreparedStatement会预编译,会被缓冲,在缓存区中可以发现预编译的命令,虽然会被再次解析,但不会被再次编译,能够有效提高系统性能。
(3)代码(两种方式)
示例1:
//2. 使用PrepareStatement执行sql语句,可以避免sql注入
String sql = "insert into student values (?, ?, ?)"; //?号是占位符
PreparedStatement ps = conn.prepareStatement(sql); //创建PreparedStatement对象
ps.setInt(1, 104); //第一个参数是占位符的索引(从1开始),第二个参数是占位符对应的实际值
ps.setString(2, "赵六");
ps.setInt(3, 2);
ps.execute(); //执行
示例2:
//2. 使用PrepareStatement执行sql语句,可以避免sql注入
String sql = "insert into student values (?, ?, ?)"; //?号是占位符
PreparedStatement ps = conn.prepareStatement(sql); //创建PreparedStatement对象
ps.setObject(1, 105);
ps.setObject(2, "小明");
ps.setObject(3, 3);
ps.execute(); //执行
- 关闭连接
后创建的先关闭;如果使用try…catch…处理异常,则要分开关闭。 - 以PreparedStatement执行对象为例整个sql语句执行过程
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* 测试PreparedStatement的基本用法
* @author dxt
*
*/
public class Demo03 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
try {
//1、连接数据库
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/school"
+ "?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC", "root", "root");
//2. 使用PrepareStatement执行sql语句,可以避免sql注入
String sql = "insert into student values (?, ?, ?)"; //?号是占位符
ps = conn.prepareStatement(sql); //创建PreparedStatement对象
ps.setObject(1, 105); //第一个参数是占位符的索引(从1开始),第二个参数是占位符对应的实际值
ps.setObject(2, "小明");
ps.setObject(3, 3);
ps.execute(); //执行
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(ps != null) {
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
- sql注入问题
示例代码
Statement stmt = conn.createStatement();
//测试SQL注入
String id = "5 or 1=1"; //传入的参数中含有恶意代码
String sql = "delete from student where id="+id;
stmt.execute(sql);
解释:id可能是传入程序中的参数,此参数可能会包含恶意代码,从而改变sql语句的执行逻辑,从而会对数据库造成不好的影响。如上述代码:会删除student表中所有数据,因为sql语句的逻辑变为了delete from student where true
。
获取处理结果
- execute()
如果sql语句第一个结果是结果集对象则返回true;如果sql语句返回结果是一个 更新操作的计数 或 没有结果集 则返回false。 - executeQuery()
(1)运行select语句,返回 ResultSet 结果集。
(2)示例代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 测试ResultSet的基本使用方法
* @author dxt
*
*/
public class Demo04 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1. 连接数据库
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/school"
+ "?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC","root","root");
//2.执行sql语句
String sql = "select id,name,class from student where id>?";
ps = conn.prepareStatement(sql);
ps.setInt(1, 102); //把id大于102的用户信息取出来
rs = ps.executeQuery();
while(rs.next()) { //向后移动游标,如果此位置的下一位置有内容则为true;游标从1之前开始
//getInt():参数为列在结果集中对应的序号
System.out.println(rs.getInt(1)+"--"+rs.getString(2)+"--"+rs.getInt(3));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(ps != null) {
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
- executeUpdate()
返回insert/update/delete操作处理的数据的 计数。插入1行数据则返回1,删除2行数据则返回2。