持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情
1. JDBC 是什么?
JDBC,全称 Java DataBase Connectivity, 是 Java 程序访问数据库的标准接口。
-
使用 ava 码访问数据库时,并不会直接访问数据库,而是通过 JDBC 接口来进行访问,JDBC 接口则通过 JDBC 驱动来实现真正对数据库的连接和访问。
-
JDBC 接口是 Java JDK 标准库中自带的,可以直接编译,而 JDBC 驱动是由数据厂商提供的。比如在 Java 代码中访问 MySQL 时,除了引入 MySQL 的依赖信息外,还需要引入 MySQL 连接驱动。
-
Java 标准库自带的 JDBC 接口其实就是定义了一组接口,而某个具体的JDBC驱动就是实现了这些接口的类。
-
引入驱动后就可以通过编写代码操作 JDBC 接口,这样保证了 Java 程序只需要编写一套代码,而通过引入不同的驱动,实现访问各种不同的数据库。
-
总的来讲,在使用它 JDBC 时,本地编写的代码只需要对 Java 中自带的 JDBC 接口进行操作,在 MySQL 对应的 JDBC 驱动中会对接口进行具体的实现,代码在调用接口时就会实现对 MySQL 服务器进行连接操作。
使用 JDBC 带来的好处
-
JDBC 接口是 Java 标准库中定义的,提供了标准的规范,Java 程序开发和编译期只需要考虑接口定义,不需要了解具体数据库是如何实现的
-
各个数据厂商使用相同的接口,Java 代码中可随时替换底层的数据库,访问数据库的 Java 代码基本不变
2. JDBC 怎么使用?
实际使用时 JDBC 并不知道最终使用哪种数据库,需要使用哪个数据库,就去使用那个数据库的实现类,即对应的 JDBC 驱动。
厂商提供的 JDBC 驱动通常也是 Java 开发的程序并打包为 Jar 类型,可以通过 Maven 引入。
本文通过使用 MySQL 数据库来演示 JDBC 使用流程。
2.1 引入依赖信息
- MySQL 依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
- MySQL 连接驱动依赖 Jar,即 JDBC 驱动包 可以使用下载 JDBC 的 Jar 包到本地进行引用的方式,而使用 Maven 进行统一管理更方便。
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
2.2 建立JDBC连接
JDBC 使用之前需要先创建一个连接,之后所有 Java 代码对数据库的操作都可以通过该连接进行。
建立一个 Connection 连接需要准备的内容有:
- URL,即连接数据库的地址,是数据库厂商指定的格式
- user/password,即访问数据库所需要的用户名和密码
URL 作为数据连接地址,需要指定目标数据库类型标识、服务器地址、端口号、对应数据库名等信息,不同数据库定义了不同的 URL
- MySql:
dbc:mysql://localhost:3306/[mysql-db]?key1=value1&key2=value2 - SqlServer:
dbc:microsoft:sqlserver://localhost:1433;DatabaseName=[sqlserver-db] - Oracle:
dbc:oracle:thin:@localhost:1521:[oracle-db] - useSSL=false 代表不使用 SSL 加密;
- characterEncoding=utf8 即使用 UTF-8 编码(MySQL 默认 utf8)
完整的数据库连接代码如下:
@Test
void testJdbcConnection(){
String url = "jdbc:mysql://localhost:3306/jdbc_test?useSSL=false";
String username = "username";
String password = "password";
Connection conn = null;
try {
//1.加载驱动(开发推荐的方式,可以省略)
//Class.forName("com.mysql.jdbc.Driver");
//2.获取与数据库的链接
conn = DriverManager.getConnection(url, username, password);
...
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
-
Class.forName("com.mysql.jdbc.Driver");用于将驱动类进行加载使用,而在 JDK1.5 之后引入的驱动 Jar 包会默认加载驱动类,因此可以省略。 -
DriverManager.getConnection(),该静态方法调用时会自动扫描 classpath,找到所有的 JDBC 驱动,然后根据传入的 URL 挑选一个合适的驱动。
-
JDBC 连接是一个昂贵的资源,使用完成后要及时释放,还可以自动释放连接的方法创建
try(conn=DriverManager.getConnection(url, username, password)){ ... }
连接时可能遇到的问题
-
Establishing SSL connection without server's identity verification is not recommended.
- 这是因为在连接数据库时 URL 中没有指定 useSSL 属性,提示需要指定
-
Path does not chain with any of the trust anchors
- 如果数据库连接字符串中设置了属性 useSSL=true 则会报上述问题,需要设置为 useSSL=false 才可以正常连接使用
3. JDBC 操作数据库
3.1 相关操作对象
获取到数据库连接 Connection 后,就可以在连接的基础上进行数据库操作了。
Connection 中提供了方法来创建一个 Statement 对象,该对象用来执行一条 SQL 语句实现对数据库的操作,执行的结果使用 ResultSet 对象接收。
Statement
命令 Statement stmt = conn.createStatement() 可以创建 Statement 对象,该对象中提供了多个执行 SQL 的方法
executeQuery(String sql),用于执行 select 查询语句executeUpdate(String sql),用于执行 insert、update、delete 语句execute(String sql),用于执行任意操作语句addBatch(String sql),用于将多条 SQL 语句加入批处理中executeBatch(),用于批量执行 SQL 语句
ResultSet
ResultSet 对象用来接收 Statement 中方法执行返回的结果内容,该对象中封装一系列方法来读取结果内容。
-
获取指定行的数据
next(),指针移动到下一行,用于获取下一行数据Previous(),指针移动到上一行absolute(int row),指针移动到指定行beforeFirst(),指针移动到最前面afterLast(),指针移动到最后面
-
通过索引或者列名来获取指定列的数据,列索引从 1 开始
getObject(int index),获取结果集中指定下标的数据getObject(string columnName),获取结果集中指定列名的数据结果getString(int index),获取指定下标的指定类型(String)数据getString(string columnName),获取指定列名指定类型(String)数据
3.1 查询数据
查询操作的完整实现流程代码如下
@Test
void testJdbcConnection(){
String url = "jdbc:mysql://localhost:3306/jdbc_test?useSSL=false";
String username = "root";
String password = "root";
Connection conn = null;
try {
//1.加载驱动(开发推荐的方式,可以省略)
//Class.forName("com.mysql.jdbc.Driver");
//2.获取与数据库的链接
conn = DriverManager.getConnection(url, username, password);
try(Statement stmt = conn.createStatement()){
try(ResultSet rs = stmt.executeQuery("select * from user_account")){
while(rs.next()){
long id = rs.getLong(1); //索引列从1开始,数据类型要一致,否则报错
String name = rs.getString("name");
String pwd = rs.getString(3);
System.out.println("id: " + id + "name: " + name + "pwd: " + pwd);
}
}
}
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
得到输出结果如下
id: 1name: tompwd: 123456
id: 2name: lilypwd: 13579
- Statement 和 ResultSet 都是需要关闭的资源,使 try 块确保及时关闭
- ResultSet 索引列从 1 开始而不是 0
3.2 增删改操作
增加、修改、删除数据的操作均通过 stmt.executeUpdate(String sql) 方法实现,只是对应的 SQL 语句不同而已。
4. Statement 和 PrepaerStatement
使用 Statement 的方法传入 SQL 语句执行时,如 SQL 语句中使用变量传入值,则需要使用字符串拼接的方式,而字符串拼接容易引发 SQL 注入的问题。
Java 的 sql 库中还提供了一个 PerparedStatement 接口,该接口继承了 Statement 并可以避免 SQL 注入问题。
PreparedStatement 执行的 SQL 语句中可以使用 ? 作为占位符,然后将 SQL 和对应的数据传给数据库,不仅可以避免 SQL 注入,还能高效利用数据库本身对查询的缓存。
使用 PreparedStatement 替换 Statement 可写为:
//将sql语句提前,并且固定话,只是传入的参数值不同,避免了sql注入
try(PreparedStatement ps = conn.prepareStatement("select * from <tablename> where id = ? and name = ?")){
//对于占位符进行赋值,同样是从1开始
ps.setObject(1,10);
ps.setObject(2,"M");
try(ResultSet rs = ps.executeQuery){
while (rs.next()) {
long id = rs.getLong("id");
long grade = rs.getLong("grade");
String name = rs.getString("name");
String gender = rs.getString("gender");
}
}
}
- 对于动态的 SQL 语句,尽量避免使用 Statement,而是使用 PreparedStatement 对象