JDBC定义与本质
概念
什么是JDBC:Java DataBase Connectivity
JDBC本质:SUN公司制定的一套接口(interface),java.sql.*。
面向接口调用,面向接口写实现类,都属于面向接口编程。
接口相当于一种协议,调用者和使用者一般要遵守同一个接口的规则。
面向接口编程好处:解耦合-降低程序耦合度,提高程序扩展力。
多态机制是典型的面向抽象编程,使用时一般用父类引用对象,但调用子类方法。
JDBC接口的意义
为什么需要制定JDBC接口:因为每一个数据库的底层实现原理都不一样,如果没有接口,每连接一个新的数据库,都需要不同的代码实现。
数据库厂家编写JDBC接口的实现类,生成一组class文件,这组class文件称为驱动。所有的数据库驱动都是以jar包的形式存在,不是SUN公司提供,而是由各大数据库厂家提供,需要去各家官网下载。
数据库厂家编写JDBC接口的实现类,程序员调用这些类即可。
模拟JDBC本质
SUN公司
/*
SUN公司
*/
public interface JDBC {
void getConnection(){}
}
数据库厂家
MySQL
/*
MySQL数据库厂家负责实现JDBC实现类
*/
public class MySQL implements JDBC {
public void getConnection() {
// 具体由MySQL实现
System.out.println("连接MySQL数据库成功");
}
}
// 实现类为MySQL驱动
Oracle
/*
Oracle数据库厂家负责实现JDBC实现类
*/
public class Oracle implements JDBC {
public void getConnection() {
// 具体由Oracle实现
System.out.println("连接Oracle数据库成功");
}
}
// 实现类为Oracle驱动
Java程序员
/*
Java程序员
只需要面向JDBC写代码,不用在意数据库是哪个品牌
*/
public class JavaProgrammer {
public void static main(String[] args) throws Exception{
// JDBC jdbc = new MySQL();
// JDBC jdbc = new Oracle();
// 通过反射创建对象
Class c = Class.forName("Oracle");
JDBC jdbc = (JDBC) c.newInstance();
// 以下代码都是面向接口调用,不用修改
jdbc.getConnection();
}
}
用反射机制就可以把数据库信息放在配置文件中,分离数据。
jdbc.properties
className = Oracle
然后调用
/*
Java程序员
只需要面向JDBC写代码,不用在意数据库是哪个品牌
*/
import java.util.*;
public class JavaProgrammer {
public void static main(String[] args) throws Exception{
// JDBC jdbc = new MySQL();
// JDBC jdbc = new Oracle();
// 通过反射创建对象
// Class c = Class.forName("Oracle");
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
String className = bundle.getString("className");
JDBC jdbc = (JDBC) c.newInstance();
// 以下代码都是面向接口调用,不用修改
jdbc.getConnection();
}
}
这样,以后只要改配置文件就可以运行连接。
运行JavaProgrammer
java JavaProgrammer
> 连接Oracle数据库成功
如果没有驱动的class文件直接运行,虽然编译能通过,但会报找不到类的错误,因为底层的实现类不存在。驱动非常重要。
使用JDBC进行开发
准备工作
从官网下载对应的驱动jar包,然后将其配置到环境变量中。
编程六步曲
- 注册驱动:告诉Java程序即将要连接的数据库是哪个
- 获取连接:表示JVM进程与数据库进程的通道打开,属于进程间通信,使用完后要关闭
- 获取数据库操作对象(执行sql语句的对象)
- 执行SQL语句:DQL DML
- 处理查询结果集:执行select语句后处理
- 释放资源:用完资源后一定要关闭资源
参考Java API 文档 - java.sql:Java Platform SE 8
注册驱动
方法一:registerDriver(Driver driver) in class DriverManager
注意点:
- registerDriver是静态方法,可以直接用类名调用
- 调用registerDriver时,其参数类型为Driver,但是在java.sql中,Driver只是一个接口,因此调用时必须使用对应驱动的实现类
- 注意registerDriver可能存在异常,因此需要做异常处理。由于SqlException继承Exception,是受检异常,适合用try catch处理
public static void main(String[] args) {
try {
// 1、注册驱动
Driver driver = new com.mysql.jdbc.Driver();// 多态,父类型引用指向子类型
DriverManager.registerDriver(driver); // 不能直接new Driver,因为Driver是接口,需要new的Driver是实现类中的Driver
// 2、获取数据库连接
// 3、获取数据库操作对象
// 4、执行sql
// 5、处理查询结果集
// 6、释放资源
} catch (SQLException e) {
e.printStackTrace();
}
}
方法二:注意mysql的Driver实现类中,有静态代码块,会在类加载的时候执行一次。
因此可以用反射机制调用该方法,方便将数据库驱动写到配置文件中去,此方法不需要接收返回值,更常用。
// 1、注册驱动
// DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
Class.forName("com.mysql.jdbc.Driver");
获取数据库连接
方法:getConnection(String url, String user, String password) in DriverManager
注意点:
- getConnection为静态方法,可以直接用类名调用
- url是数据库地址,由 协议:// ip地址:端口号/数据库名称 组成
- url拓展:url是统一资源定位符,其中通信协议表示规定好的数据传输格式,ip地址表示计算机地址,端口号表示app代号,资源名表示要访问的资源是什么。
- 如果不是用mysql而是oracle数据库,其格式为jdbc:oracle:thin:@127.0.0.1:1521/数据库名称,相应的,调用时Driver也需要指向oracle的driver实现类。其余也是类似的,都有指定的协议,资源
- 返回的数据库对象com.mysql.cj.jdbc.JDBC4Connection@xxxxx中,@之前的表示类名。实际使用发现com.mysql.jdbc.Driver已经过时,被com.mysql.cj.jdbc.Driver替代,所以得到的返回对象类名是com.mysql.jdbc.ConnectionImpl。
// 2、获取数据库连接
String url = "jdbc:mysql://127.0.0.1:3306";
String user = "root";
String password = "333";
Connection connection = DriverManager.getConnection(url, user, password);// 此时connection指向的是子类对象
System.out.println("数据库连接对象:" + connection); // 返回数据库连接对象:com.mysql.cj.jdbc.ConnectionImpl@xxxxx
获取数据库操作对象
方法:createStatement() in Connection
// 3、获取数据库操作对象
Statement statement= connection.createStatement();
执行sql
如果是增删改语句,使用Statement类中的executeUpdate(String sql)方法
JDBC中的sql语句不用写分号
// 4、执行sql
// String sql = "select * from tableA";
// 专门执行DML语句 - insert, update, delete
// 返回值为影响数据库中的记录条数
String sql = "insert into tableA ('USER_ACCOUNT', 'TEAM') VALUES ('abc@mail.com', 'IT')";
int count = statement.executeUpdate(sql);
如果是查询语句,使用Statement类中的executeQuery(String sql)方法,并把结果存储到ResultSet类中,保存结果集
//4、执行sql
String sql = "select * from tableA";
// int executeUpdate(delete/insert/update)
// ResultSet executeQuery(select)
set = statement.executeQuery(sql); // executeQuery专门用于处理查询语句
处理结果集
ResultSet中存在方法next(),是用于遍历结果集的迭代器,返回bool值。如果存在数据返回true,否则返回false。
用rs.next()取出一行数据后,还需要通过rs中的get方法取出具体字段的value。
get方法有:getString(), getInt(),getDate()等。
getString()方法取出的数据无论在数据库是什么类型,返回的就是String类型。取数据时,可以通过列的下标(下标从1开始),也可以通过列名。推荐用列名,因为下标可能因为取顺序的数据发生改变。如果查询的时候对列名重命名了,必须用新的列名获取数据,否则会返回列不存在的错误。
getInt(),getDate()等方法不能随便使用,必须是数据库的底层数据类型与之相同的时候,才能正确执行,否则会返回数据类型无法转换的错误。
//4、执行sql
String sql = "select id, name a, salary b from tableA";
// int executeUpdate(delete/insert/update)
// ResultSet executeQuery(select)
rs = statement.executeQuery(sql); // executeQuery专门用于处理查询语句
//5、处理结果集
while (rs.next()) {
// getString()方法:不管数据库数据类型是什么,都以String类型返回
// String flow = rs.getString(2); // jdbc中所有下标从1开始, 2表示第二列
// String scenario = rs.getString(3); // 第三列
// 以查询结果的列名获取(因为查询结果可以给列名重命名)
// 除了getString, 还可以用getInt, getDate等特定类型取出数据,但必须与数据库中的数据类型对应
int id = rs.getInt("id");
String name = rs.getString("a");
String salary = rs.getDouble("b");
System.out.println("id: " + (id+200) + " - name is '" + name + "' with salary '" + salry+ "'");
// 返回 id: 1 - name is 'Jack' with salary '5000'
关闭资源
写在finally中,需要释放的资源提前定义,否则finally中无法调用。
如果是查询语句,需要先关闭ResultSet。
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
ResultSet rs = null;
try {
/*
precondition
*/
connection = DriverManager.getConnection(url, user, password);// 此时connection指向的是子类对象
System.out.println("数据库连接对象:" + connection);
// 3、获取数据库操作对象
statement= connection.createStatement();
// 4、执行sql
// do sth
// 5、处理查询结果集
// do sth
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 6、释放资源
// 在finally中释放资源,从小到大关闭,关闭时分别对其try catch
// 为了能关闭资源,定义的时候就要把需要释放的资源放在try外面定义
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (statement != null) {
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
将连接信息放入配置文件中
jdbc.properties
driver = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://127.0.0.1:3306/xxx
user = root
password = 333
用ResourceBundle调用数据
package JDBCTest;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;
// 将连接数据库的所有信息放到配置文件中
/*
实际开发不建议把连接数据库信息写死到程序中
*/
public class JDBCTest04 {
public static void main(String[] args) {
// 使用资源绑定器绑定配置文件
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
String driver = bundle.getString("driver");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");
Connection connection = null;
Statement statement = null;
try {
// 1、注册驱动
Class.forName(driver);
// 2、获取数据库连接
connection = DriverManager.getConnection(url, user, password);
System.out.println("数据库连接对象:" + connection);
// 3、获取数据库操作对象
statement= connection.createStatement();
// 4、执行sql
// 专门执行DML语句 - insert, update, delete
// 返回值为影响数据库中的记录条数
String sql = "delete from users where id = '200'";
// String sql = "update users set TEAM = 'a' where id = '200'";
int count = statement.executeUpdate(sql);
System.out.println(count == 1 ? "删除成功":"删除失败");
// 5、处理查询结果集
} catch (SQLException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
// 6、释放资源
// 在finally中释放资源,从小到大关闭,关闭时分别对其try catch
// 为了能关闭资源,定义的时候就要把需要释放的资源放在try外面定义
try {
if (statement != null) {
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
但实际开发不建议把连接数据库信息写死到程序中,用户体验很差。
注意:properties文件不能放错位置,否则会出现"Can't find bundle for base name jdbc, locale en_US" 的错误。要确保 jdbc.properties 文件位于类路径下。如果是标准的 Java 项目,将.properties 文件放在 src 目录下。对于 Maven 项目,应该放在 src/main/resources 目录下。
使用IDEA进行JDBC配置
新建Project后,File->Project Structure->Project Settings->Libraries
点击加号, 添加Java,选择数据库的jar包
选择要导入的模块,如果有其他新模块新建的话,需要再导入一次。否则运行时会报类找不到错误。
然后就能在External libraries里看到了