前言
之前一直使用Mybatis,duruid这样的工具,没有专门对原生JDBC进行系统的了解学习,今天来总结以下JDBC的使用和基本的原理。
JDBC是什么
JDBC代表Java数据库连接(Java Database Connectivity),它是用于Java编程语言和数据库之间的数据库无关连接的标准Java API。因为数据库是作为计算机软件平台的基础,程序软件的运行大都离不开数据库,但各个版本的数据库之间的差异又很大,需要有一套公共的API来对底层进行屏蔽,用同一套接口来操作数据库,如:
- 连接到数据库
- 创建SQL或MySQL语句
- 在数据库中执行SQL或MySQL查询
- 查看和修改结果记录
JDBC架构
先来看看原理性的东西,
通常,JDBC体系结构由两层组成:
- JDBC API:提供应用程序到JDBC管理器连接。
- JDBC驱动程序API:支持JDBC管理器到驱动程序连接。
JDBC API使用驱动程序管理器并指定数据库的驱动程序来提供与异构数据库的透明连接。
JDBC驱动程序管理器确保使用正确的驱动程序来访问每个数据源。 驱动程序管理器能够支持连接到多个异构数据库的多个并发驱动程序。
常见的JDBC组件
- DriverManager:此类管理数据库驱动程序列表。 使用通信子协议将来自java应用程序的连接请求与适当的数据库驱动程序进行匹配。在JDBC下识别某个子协议的第一个驱动程序将用于建立数据库连接。
- Driver:此接口处理与数据库服务器的通信。我们很少会直接与Driver对象进行交互。 但会使用DriverManager对象来管理这种类型的对象。 它还提取与使用Driver对象相关的信息。
- Connection:此接口具有用于联系数据库的所有方法。 连接(Connection)对象表示通信上下文,即,与数据库的所有通信仅通过连接对象。
- Statement:使用从此接口创建的对象将SQL语句提交到数据库。 除了执行存储过程之外,一些派生接口还接受参数。
- ResultSet:在使用Statement对象执行SQL查询后,这些对象保存从数据库检索的数据。 它作为一个迭代器并可移动ResultSet对象查询的数据。
- SQLException:此类处理数据库应用程序中发生的任何错误。
创建JDBC应用程序6步
构建JDBC应用程序涉及以下六个步骤
1. 导入包
大多数情况下,使用import java.sql.*
就足够了。
2. 注册JDBC驱动程序
需要初始化驱动程序,以便可以打开与数据库的通信通道。Class.forName("com.mysql.jdbc.Driver");
3. 打开一个连接
需要使用DriverManager.getConnection()方法创建一个Connection对象,它表示与数据库的物理连接。
static final String USER = "root";
static final String PASS = "pwd123456";
conn = DriverManager.getConnection(DB_URL,USER,PASS);
4. 执行查询
需要使用一个类型为Statement或PreparedStatement的对象,并提交一个SQL语句到数据库执行查询。
Statement
System.out.println("Creating statement...");
stmt = conn.createStatement();
String sql;
sql = "SELECT id, first, last, age FROM Employees";
// 返回一个ResultSet
ResultSet rs = stmt.executeQuery(sql);
如果要执行一个SQL语句:UPDATE,INSERT或DELETE语句,那么需要下面的代码片段
System.out.println("Creating statement...");
stmt = conn.createStatement();
String sql;
sql = "DELETE FROM Employees";
// 在获得resultset的地方传入sql
ResultSet rs = stmt.executeUpdate(sql);
PreparedStatement
PreparedStatement接口扩展了Statement接口,它添加了比Statement对象更好一些优点的功能。
此语句可以动态地提供/接受参数。这点应该是非常常用的
PreparedStatement pstmt = null;
try {
String SQL = "Update Employees SET age = ? WHERE id = ?";
// 这里要在获得pstmt的地方传入sql
pstmt = conn.prepareStatement(SQL);
// 给pstmt传参
pstmt.setInt(1, 35); // This would set age
pstmt.setInt(2, 102); // This would set ID
// 执行pstmt update语句
int rows = pstmt.executeUpdate();
System.out.println("Rows impacted : " + rows );
. . .
} catch (SQLException e) {
. . .
} finally {
. . .
}
5. 从结果集中提取数据
需要使用相应的ResultSet.getXXX()方法从结果集中检索数据。
while(rs.next()){
//Retrieve by column name
int id = rs.getInt("id");
int age = rs.getInt("age");
String first = rs.getString("first");
String last = rs.getString("last"); //Display values
System.out.print("ID: " + id);
System.out.print(", Age: " + age);
System.out.print(", First: " + first);
System.out.println(", Last: " + last);
}
6. 清理环境
需要明确地关闭所有数据库资源,而不依赖于JVM的垃圾收集。
rs.close();
stmt.close();
conn.close();
jdbc事务
直接用一个小例子来说明
try{
// 创建connection后,通过setAutoCommit(false)注明每次执行语句后不需要数据库来commit,手动commit
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
String SQL = "INSERT INTO Employees " + "VALUES (106, 20, 'Rita', 'Tez')";
stmt.executeUpdate(SQL);
//Submit a malformed SQL statement that breaks
String SQL = "INSERTED IN Employees " + "VALUES (107, 22, 'Sita', 'Singh')";
stmt.executeUpdate(SQL);
// 上面两个执行语句没有报错的话,手动commit
conn.commit();
} catch(SQLException se) {
// 如果有报错,回滚事务
conn.rollback();
}
上面的例子中,如果事务一旦发生错误,则马上回滚到setAutoCommit(false),相当于事务没有执行(满足原子性),接下来用一招更精确的回滚控制方法——Savepoint,直接在conn.rollback(savePoint)指明回滚的地方,下面是一个例子
setSavepoint(String savepointName): - 定义新的保存点,它还返回一个Savepoint对象。
releaseSavepoint(Savepoint savepointName): - 删除保存点。要注意,它需要一个Savepoint对象作为参数。 该对象通常是由setSavepoint()方法生成的保存点。
try{
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
//设置一个savePoint
Savepoint savepoint1 = conn.setSavepoint("Savepoint1");
String SQL = "INSERT INTO Employees " + "VALUES (106, 24, 'Curry', 'Stephen')"; stmt.executeUpdate(SQL);
String SQL = "INSERTED IN Employees " + "VALUES (107, 32, 'Kobe', 'Bryant')"; stmt.executeUpdate(SQL);
// 正常情况下,提交
conn.commit();
}catch(SQLException se){
// 回滚到savePoint1的地方
conn.rollback(savepoint1);
}