JDBC
1. 概念
Java DataBase Connectivity Java数据库连接(用Java操作数据库)
定义了操作所有关系型数据库的规范(接口),实现类(数据库驱动jar包)由各数据库厂商实现用来操作数据库
2. 快速入门
- 步骤
- 导入驱动jar包
mysql-connector-java-8.0.19.jar
- 注册驱动
- 获取数据库连接对象 Connection
- 定义 sql
- 获取执行 sql 语句的对象 Statement
- 执行 sql,接收返回的结果
- 处理结果
- 释放资源
- 导入驱动jar包
1 | public class Main { |
3. 详解各个对象
DriverManager:驱动管理对象
功能:
注册驱动
static void registerDriver (Driver driver) 使用
DriverManager
注册给定的驱动程序写代码时使用
Class.forName("com.mysql.cj.jdbc.Driver");
另外,发现不注册驱动也能运行成功,这是因为在mysql5之后,在jar包中存在文件
mysql-connector-java-8.0.19.jar\META-INF\services\java.sql.Driver
,里面写的有默认驱动获取数据库连接
方法:
static Connection getConnection(String url, String user, String password)
参数:
url:指定连接的路径
语法:jdbc:mysql://ip地址(域名): 端口号/数据库名称?serverTimezone=所在时区
mysql-jdbc 6.0以上版本需要在连接数据库url后面指定所在时区。
中国用的是GMT+8的时区,在jdbc连接的url后面加上东八区的serverTimezone=Asia/Shanghai
或
serverTimezone=Hongkong
或
serverTimezone=GMT%2B8
(%2B是“+”号)即可解决问题
例子:
jdbc:mysql://localhost:3306/learn?serverTimezone=Hongkong
细节:如果连接的是本机mysql服务器,并且mysql服务的默认端口是3306,则url可以简写为:
jdbc:mysql:///数据库名称?serverTimezone=时区名称
user:用户名
password:密码
Connection:数据库连接对象
- 功能:
- 获取执行 sql 的对象
Statement createStatement()
PreparedStatement prepareStatement(String sql)
- 管理事务
- 开启事务:
setAutoCommit(boolean autoCommit)
:调用该方法设置参数为false
即开启事务 - 提交事务:
commit()
- 回滚事务:
rollback()
- 开启事务:
- 获取执行 sql 的对象
- 功能:
Statement:执行sql对象
- 执行 sql
boolean execute(String sql)
:可以执行任意的 sql 了解int executeUpdate(String sql)
:执行 DML(insert、update、delete)语句、DDL语句(create、alter、drop)语句- 返回值:影响的行数,可以通过这个影响的行数判断 DML 语句是否执行成功,返回值 > 0执行成功
ResultSet executeQuery(String sql)
:执行 DQL(select)语句- (其返回值和 2. 不同)
- 执行 sql
ResultSet:结果集对象,封装查询结果
boolean next()
:光标向下移动一行getXxx(参数)
:获取数据Xxx
代表数据类型,如int getInt()
,getString()
- 参数:
- int:代表列的编号,从 1 开始 ,如:
getString(1)
- String:代表列名称,如:
getDouble("balance")
- int:代表列的编号,从 1 开始 ,如:
注意:
使用步骤:
- 游标向下移动一行
- 判断是否有数据
- 获取数据
1
2
3
4
5
6
7
8
9
10
11
12String sql = "select * from user";
//5. 获取执行 sql 语句的对象 Statement
Statement stmt = connection.createStatement();
//6. 执行 sql,接收返回的结果
ResultSet rs = stmt.executeQuery(sql);
//7. 处理结果
// 循环判断游标是否是最后一行末尾
while (rs.next()){
int id = rs.getInt(1);
String name = rs.getString("username");
System.out.println("id = " + id + " username = " + name);
}
PreparedStatement:执行sql对象
SQL 注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全问题
- 输入用户名随便,输入密码:
a' or ‘a' = ‘a
- sql:
select * from user where username = ‘asdad‘ and password = ‘a' or ‘a‘ = ‘a'
- 输入用户名随便,输入密码:
解决sql注入问题:使用PreparedStatement对象来解决
预编译的sql:参数使用
?
作为占位符,用户的输入只是给占位符赋值步骤:
- 导入驱动jar包
mysql-connector-java-8.0.19.jar
- 注册驱动
- 获取数据库连接对象 Connection
- 定义 sql
- 注意:sql的参数使用
?
作为占位符。如:select * from user where username = ? and password = ?;
- 注意:sql的参数使用
- 获取执行 sql 语句的对象 PrepareStatement
Connection.prepareStatement(String sql)
- 给
?
赋值:- 方法:
setXxx(参数1, 参数2)
- 参数1:
?
的位置编号,从 1 开始 - 参数2:
?
的值
- 参数1:
- 方法:
- 执行 sql,接收返回的结果
- 处理结果
- 释放资源
- 导入驱动jar包
后期都会使用PreparedStatement来完成增删改查的所有操作
- 可以防止SQL注入
- 效率更高
代码示例:(可以防止 sql 注入)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public static void main(String[] args) {
ResultSet rs = null;
PreparedStatement pstmt = null;
Connection connection = null;
try {
connection = JDBCUtils.getConnection();
//4. 定义 sql
String sql = "select * from user where username = ? and sex = ?";
//5. 获取执行 sql 语句的对象 PreparedStatement
pstmt = connection.prepareStatement(sql);
//6. 给 ? 赋值
pstmt.setString(1,"ascasc");
pstmt.setString(2,"a' or ‘a‘ = ‘a");
//7. 执行查询,不需要传参
rs = pstmt.executeQuery();
//8. 判断,输出为 false
System.out.println(rs.next());
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(rs,pstmt,connection);
}
}
抽取JDBC工具类:JDBCUtils
目的:简化书写
分析:
抽取注册驱动
抽取一个方法获取连接对象
需求:不传递参数,同时保证工具类的通用性
解决:配置文件(
src/jdbc.properties
)1
2
3
4url=jdbc:mysql:///learn?serverTimezone=Hongkong
user=root
password=4869
driver=com.mysql.cj.jdbc.Driver
抽取一个方法释放资源
代码示例:
JDBCUtils.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105package com.conv.util;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;
/**
* JDBC工具类
*/
public class JDBCUtils {
private static String url;
private static String user;
private static String password;
private static String driver;
/**
* 文件的读取,只需要读取一次即可拿到这些值,使用静态代码块
*/
static {
try {
//1. 读取资源文件,获取值
Properties pro = new Properties();
//2. 加载文件
//方法 1(实测不可行)
// 获取src路径下的文件的方式----->ClassLoader 类加载器
// ClassLoader classLoader = JDBCUtils.class.getClassLoader();
// // 根路径是 src 目录,所以只用写相对路径就行
// URL res = classLoader.getResource("jdbc.properties");
// String path = res != null ? res.getPath() : null;
// System.out.println(path);
// if (path != null) {
// pro.load(new FileReader(path));
// }
//方法 2
// 该行代码实测是可行的(可能java8不行?)若不行的话可采用上面的方法
pro.load(new FileReader("src/jdbc.properties"));
//3. 获取数据,赋值
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
driver = pro.getProperty("driver");
//4. 注册驱动
Class.forName(driver);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取连接
*
* @return 连接对象
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
/**
* 关闭资源
*
* @param statement
* @param connection
*/
public static void close(Statement statement, Connection connection) {
try {
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 关闭资源
*
* @param resultSet
* @param statement
* @param connection
*/
public static void close(ResultSet resultSet, Statement statement, Connection connection) {
try {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}main.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33package com.conv.jdbc;
import com.conv.util.JDBCUtils;
import java.sql.*;
public class Main {
public static void main(String[] args) {
ResultSet rs = null;
Statement stmt = null;
Connection connection = null;
try {
connection = JDBCUtils.getConnection();
//4. 定义 sql
//String sql = "update user set sex = '男' where id = 42";
String sql = "select * from user";
//5. 获取执行 sql 语句的对象 Statement
stmt = connection.createStatement();
//6. 执行 sql,接收返回的结果
rs = stmt.executeQuery(sql);
//7. 处理结果
while (rs.next()) {
int id = rs.getInt(1);
String name = rs.getString("username");
System.out.println("id = " + id + " username = " + name);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(rs,stmt,connection);
}
}
}
sql 语句拼接Java中的变量
java中的字符串只能双引号(js可以单引号也可以双引号)引上,如果字符串中需要拼接变量,该变量用单引号括起来,然后加两个双引号再加两个加号,中间就是变量
例:
1
2String username, password; //username 和 password 已经定义
String sql = “select * from user where username = '"+username+"' and password = '"+password+"'”
JDBC 控制事务
事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败
操作:
- 开启事务:
- 提交事务
- 回滚事务
使用Connection对象来管理事务
- 开启事务:
setAutoCommit(boolean autoCommit)
:调用该方法设置参数为false
即开启事务- 在执行sql之前开启事务
- 提交事务:
commit()
- 当所有sql都执行完提交事务
- 回滚事务:
rollback()
- 在catch中回滚事务
- 开启事务:
代码示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50public static void main(String[] args) {
ResultSet rs = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;
Connection connection = null;
try {
//1. 获取连接
connection = JDBCUtils.getConnection();
//开启事务
connection.setAutoCommit(false);
//2.定义 sql
//2.1 张三 - 300
String sql1 = "update account set money = money - ? where id = ?";
//2.2 李四 + 300
String sql2 = "update account set money = money + ? where id = ?";
//3. 获取执行sql对象
pstmt1 = connection.prepareStatement(sql1);
pstmt2 = connection.prepareStatement(sql2);
//4. 设置参数
pstmt1.setInt(1, 300);
pstmt1.setInt(2, 1);
pstmt2.setInt(1, 300);
pstmt2.setInt(2, 2);
//5. 执行sql
pstmt1.executeUpdate();
//手动制造异常
int i = 1 / 0;
pstmt2.executeUpdate();
connection.commit();
} catch (Exception e) {
//事务回滚(回滚到事务开启之前的状态)
try {
if (connection != null) {
connection.rollback();
}
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
} finally {
JDBCUtils.close(pstmt1, connection);
JDBCUtils.close(pstmt2, null);
}
}