JDBC 各组件调用关系
[img]http://dl.iteye.com/upload/attachment/372278/b2ccc9c6-6f2b-326a-b513-bc48852028bc.png[/img]
JDBC 中常用的、关键的几个类:
DriverManager dm = null;
Connection conn = null;
Statement stmt = null;
PreparedStatement pstmt = null;
CallableStatement cstmt = null;
ResultSet rs = null;
DriverManager
管理所有注册的数据驱动。JDBC 规定,当数据驱动(Driver)被加载时,它必须向 DriverManager 注册。所以,当用 Class.forName(driver) 加载数据库驱动时,driver 就已经注册到 DriverManager 中了。如 mysql 的 Driver 类:
package com.mysql.jdbc;
import java.sql.SQLException;
/**
* The Java SQL framework allows for multiple database drivers. Each driver
* should supply a class that implements the Driver interface
*
* <p>
* The DriverManager will try to load as many drivers as it can find and then
* for any given connection request, it will ask each driver in turn to try to
* connect to the target URL.
*
* <p>
* It is strongly recommended that each Driver class should be small and
* standalone so that the Driver class can be loaded and queried without
* bringing in vast quantities of supporting code.
*
* <p>
* When a Driver class is loaded, it should create an instance of itself and
* register it with the DriverManager. This means that a user can load and
* register a driver by doing Class.forName("foo.bah.Driver")
*
* @see org.gjt.mm.mysql.Connection
* @see java.sql.Driver
* @author Mark Matthews
* @version $Id$
*/
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
// ~ Static fields/initializers
// ---------------------------------------------
//
// Register ourselves with the DriverManager
//
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
// ~ Constructors
// -----------------------------------------------------------
/**
* Construct a new driver and register it with DriverManager
*
* @throws SQLException
* if a database error occurs.
*/
public Driver() throws SQLException {
// Required for Class.forName().newInstance()
}
}
Connection
一个 Connection 实例代表一个到数据的连接。可以从 DriverManager 或 DataSource 中获得 Connection 实例。
对数据库的所有操作都是通过 Connection 提交到数据库的,Connection 的一次提交就是一次事务。在默认情况下,每执行一次 sql 语句,Connection 就自动提交,这也是最小的事务单位,所以就算本次操作失败了,也无需做 rollback,因为失败而数据没被持久化(或者说自动提交则自动回滚)。
当需要把多个 sql 操作作为一个原子操作室,就把它们放到同一个事务中,此时需要把 Connection 的自动提交关掉,当所有 sql 操作完后再进行提交,如果其中某个操作失败了,则 rollback 所有操作,如:
try {
conn.setAutoCommit(false);
stmt = conn.createStatement();
stmt.executeUpdate("sql1");
stmt.executeUpdate("sql2");
stmt.executeUpdate("sql3");
conn.commit();
} catch (Exception e) {
e.printStackTrace();
conn.rollback();
} finally {
stmt.close();
conn.close();
}
注意:Connection 可以设置 savepoint,当某个操作失败时,可以回滚到某个 savepoint,而不用回滚所有的 sql 操作,我想这就是嵌套事务实现的原理。如
Savepoint sp = null;
try {
conn.setAutoCommit(false);
stmt = conn.createStatement();
stmt.executeUpdate("sql1");
stmt.executeUpdate("sql2");
sp = conn.setSavepoint();
stmt.executeUpdate("sql3");
stmt.executeUpdate("sql4");
conn.commit();
} catch (Exception e) {
e.printStackTrace();
conn.rollback(sp);
} finally {
stmt.close();
conn.close();
}
Connection 还可以获取数据库的元信息:DatabaseMetaData,包含表、视图等信息。
Statement,PreparedStatement,CallableStatement
Statement 和 PreparedStatement 的主要区别是:PreparedStatement 对 sql 语句做了优化,以在后面的执行过程中达到更快的速度。
但是注意:PreparedStatement 适用于某个 sql 语句被多次执行只是参数不同的情况,如果 PreparedStatement 只是被执行一两次,那么此时它的性能可能比不上 Statement,因为它在 sql 语句优化上付出了开销。
CallableStatement 用于调用数据库的存储过程。
ResultSet
ResultSet 实例表示某次 sql 执行返回的结果集,在数据库中反映为结果表(在数据库中,每次查询返回的结果都放在一个临时表中?)。
注意:对于不同的数据库驱动厂商,返回结果的方式有所不同。如 mysql 将所有结果一次性返回,而有些厂商则只返回一部分的数据,等到调用 rs.next() 的时候再去数据拿下一次的数据。
我们一般默认使用不可滚动、不可更新的结果集,在有需要的情况下,可以用以下方式创建 stmt以返回可滚动、可更新的结果集:
stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
于是就可以对返回的结果集进行更新行、删除行、插入行等操作。
对于 ResultSet 的两个参数,有如下说明:
When creating a Statement object, you need to specify two arguments to the method
createStatement(). The first argument indicates the type of a ResultSet object and can be one of three constants:
• ResultSet.TYPE_FORWARD_ONLY: A constant indicating the type for a ResultSet object whose cursor may move only forward (creates a nonscrollable ResultSet object)
• ResultSet.TYPE_SCROLL_INSENSITIVE: A constant indicating the type for a ResultSet object that is scrollable but generally not sensitive to changes made by others
• ResultSet.TYPE_SCROLL_SENSITIVE: A constant indicating the type for a ResultSet object that is scrollable and generally sensitive to changes made by others
The second argument is one of two ResultSet constants for specifying whether a result set is read-only or writable/updatable:
• ResultSet.CONCUR_READ_ONLY: A constant indicating the concurrency mode for a ResultSet object that may not be updated
• ResultSet.CONCUR_UPDATABLE: A constant indicating the concurrency mode for a ResultSet object that may be updated
The important point to remember is that if you specify a ResultSet type, you must also specify whether it is read-only or writable/updatable.
一个事务之和唯一一个 Connection 实例关联。当事务 tr1 更新某表数据时,它会锁住该表,直到本次事务提交。在此期间,如果有事务 tr2 欲对同一个表做更新操作,那么 tr2 会阻塞,知道 tr1 提交完毕后,tr2 才能往下继续操作该表。
数据库支持表级、行级锁定,可用语句 sql … for update 进行行级锁定。
Date、Time、Timestamp
在 SQL 中,Date 只能存储日期,如:1999-9-9,Time 只能存储时间,如:23:59:59。如在 mysql 中,如果往 date 列存入值 1999-9-9 23:59:59,则 “23:59:59” 被自动截去,只存入“1999-9-9”,往 time 列存入值 1999-9-9 23:59:59,则 “1999-9-9” 被自动截去,只存入 “23:59:59”。在 mysql 中,可以用 datetime 存储日期和时间。
BLOB、CLOB
都是大字段,这两中类型的本质区别是,BLOB 大小写敏感,CLOB 大小写不敏感。如在 msql 中有以下 case:
[img]http://dl.iteye.com/upload/attachment/372280/bb4574c1-73d1-378d-9354-6d86d9a50ffa.png[/img]
可以看到在表 t_blob 中,field_blob 为 blob 类型,当我们把 t_blob 中的记录按field_blob 排序时,可以看到 “BBB”排在 “aaa”的前面,这是正确的,因为在 ascii 中 B 的值本来就小于 a ;但是再 t_clob 中,我们把 t_clob 中的记录按 field_clob 排序时,“aaa”是排在 “BBB” 前面的,因为 CLOB 类型大小写不敏感,在大小写不敏感的情况下,a 当然小于 b(B)。