JDBC详解
一、相关概念
1.什么是JDBC
DBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。
在程序中包含数据库编程所需的JDBC类。大多数情况下,使用import java.sql.*
二、常用接口
1.Driver接口
Driver接口由数据库厂家提供,作为java开发人员,只需要使用Driver接口就可以了。在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序,不同的数据库有不同的装载方法。如:
// Oracle (thin)数据库
Class.forName("oracle.jdbc.driver.OracleDriver");
// MySQL数据库
Class.forName("com.mysql.jdbc.Driver");
// SQLServer2000数据库
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
2.Connection接口
Connection与特定数据库的连接(会话),在连接上下文中执行sql语句并返回结果。DriverManager.getConnection(url, user, password)方法建立在JDBC URL中定义的数据库Connection连接上。
// Oracle (thin)数据库
String URL="jdbc:oracle:thin:@localhost:1521:myDB";
// MySQL数据库
String URL = "jdbc:mysql://localhost:3306/myDB";
// SQLServer2000数据库
String URL ="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=myDB";
//创建连接
Connection con = DriverManager.getConnection(URL,UserName,Password);
常用方法:
方法名 | 说明 |
---|---|
Statement createStatement() | 创建向数据库发送sql的statement对象 |
PreparedStatement prepareStatement(String sql) | 创建向数据库发送预编译sql的PrepareSatement对象 |
prepareCall(sql) | 创建执行存储过程的callableStatement对象 |
setAutoCommit(boolean autoCommit) | 设置事务是否自动提交 |
boolean isClosed() | 查询此Connection对象是否已经被关闭 |
commit() | 在链接上提交事务 |
rollback() | 在此链接上回滚事务 |
3.Statement接口
用于执行静态SQL语句并返回它所生成结果的对象。
三种Statement类:
类名 | 说明 |
---|---|
Statement | 由createStatement创建,用于发送简单的SQL语句(不带参数) |
PreparedStatement | 继承自Statement接口,由preparedStatement创建,用于发送含有一个或多个参数的SQL语句。PreparedStatement对象比Statement对象的效率更高,并且可以防止SQL注入,所以我们一般都使用PreparedStatement |
CallableStatement | 继承自PreparedStatement接口,由方法prepareCall创建,用于调用存储过程 |
常用Statement方法:
方法 | 说明 |
---|---|
boolean execute(String SQL) | 运行语句,返回是否有结果集 |
ResultSet executeQuery(String SQL) | 运行select语句,返回ResultSet结果集 |
int executeUpdate(String SQL) | 运行insert/update/delete操作,返回更新的行数 |
addBatch(String sql) | 把多条sql语句放到一个批处理中 |
executeBatch() | 向数据库发送一批sql语句执行 |
4.ResultSet接口
ResultSet提供检索不同类型字段的方法,常用的有:
方法 | 说明 |
---|---|
getString(int index)、getString(String columnName) | 获得在数据库里是varchar、char等类型的数据对象 |
getFloat(int index)、getFloat(String columnName) | 获得在数据库里是Float类型的数据对象 |
getDate(int index)、getDate(String columnName) | 获得在数据库里是Date类型的数据 |
getBoolean(int index)、getBoolean(String columnName) | 获得在数据库里是Boolean类型的数据 |
getObject(int index)、getObject(String columnName) | 获取在数据库里任意类型的数据 |
JDBC数据类型:
下表列出了默认的JDBC数据类型与Java数据类型转换,当使用PreparedStatement或CallableStatement对象时可调用setXXX()方法或ResultSet.updateXXX()方法。
SQL | JDBC/Java | setXXX | getXXX | updateXXX |
---|---|---|---|---|
VARCHAR | java.lang.String | setString | getString | updateString |
CHAR | java.lang.String | setString | getString | updateString |
LONGVARCHAR | java.lang.String | setString | getString | updateString |
BIT | boolean | setBoolean | getBoolean | updateBoolean |
NUMERIC | java.math.BigDecimal | setBigDecimal | getBigDecimal | updateBigDecimal |
TINYINT | byte | setByte | getByte | updateByte |
SMALLINT | short | setShort | getShort | updateShort |
INTEGER | int | setInt | getInt | updateInt |
BIGINT | long | setLong | getLong | updateLong |
REAL | float | setFloat | getFloat | updateFloat |
FLOAT | float | setFloat | getFloat | updateFloat |
DOUBLE | double | setDouble | getDouble | updateDouble |
VARBINARY | byte[ ] | setBytes | getBytes | updateBytes |
BINARY | byte[ ] | setBytes | getBytes | updateBytes |
DATE | java.sql.Date | setDate | getDate | updateDate |
TIME | java.sql.Time | setTime | getTime | updateTime |
TIMESTAMP | java.sql.Timestamp | setTimestamp | getTimestamp | updateTimestamp |
CLOB | java.sql.Clob | setClob | getClob | updateClob |
BLOB | java.sql.Blob | setBlob | getBlob | updateBlob |
ARRAY | java.sql.Array | setARRAY | getARRAY | updateARRAY |
REF | java.sql.Ref | SetRef | getRef | updateRef |
STRUCT | java.sql.Struct | SetStruct | getStruct | updateStruct |
JDBC 3.0 增强了对 BLOB,CLOB,ARRAY 和 REF 数据类型的支持。 ResultSet 对象现在有 UPDATEBLOB(),updateCLOB(), updateArray(),和 updateRef()方法,通过这些方法可以直接操作服务器上的相应数据。
java.sql.Date 类映射 SQL DATE 类型,java.sql.Time 类和 java.sql.Timestamp 类也分别映射 SQL TIME 数据类型和 SQL TIMESTAMP 数据类型。
ResultSet还提供了对结果集进行滚动的方法:
方法 | 说明 |
---|---|
boolean next() | 将光标移动到下一行,如果是结果集的最后一行则返回 false |
boolean previous() | 将光标移动到上一行,如果超过结果集的范围则返回 false |
void close() | 关闭 ResultSet 对象 |
int getRow() | 返回当前光标指向的行数的值 |
void beforeFirst() | 将光标移动到第一行之前 |
void afterLast() | 将光标移动到最后一行之后 |
boolean first() | 将光标移动到第一行 |
void last() | 将光标移动到最后一行 |
boolean relative(int row) | 将光标移动到当前指向的位置往前或往后第 row 行的位置 |
boolean absolute(int row) | 将光标移动到指定的第 row 行 |
void moveToInsertRow() | 将光标移动到结果集中指定的行,可以在数据库中插入新的一行。当前光标位置将被记住 |
void moveToCurrentRow() | 如果光标处于插入行,则将光标返回到当前行,其他情况下,这个方法不执行任何操作 |
使用后依次关闭对象及连接:ResultSet → Statement → Connection
处理 NULL 值
SQL 使用 NULL 值和 Java 使用 null 是不同的概念。可以使用三种策略来处理 Java 中的 SQL NULL 值-
- 避免使用返回原始数据类型的 getXXX()方法。
- 使用包装类的基本数据类型,并使用 ResultSet 对象的 wasNull()方法来测试收到 getXXX()方法返回的值是否为 null,如果是 null,该包装类变量则被设置为 null。
- 使用原始数据类型和 ResultSet 对象的 wasNull()方法来测试通过 getXXX()方法返回的值,如果是 null,则原始变量应设置为可接受的值来代表 NULL。
Statement stmt = conn.createStatement( );
String sql = "SELECT id, first, last, age FROM Employees";
ResultSet rs = stmt.executeQuery(sql);
int id = rs.getInt(1);
if( rs.wasNull( ) ) {
id = 0;
}
三、使用JDBC的步骤
加载JDBC驱动程序 → 建立数据库连接Connection → 创建执行SQL的语句Statement → 处理执行结果ResultSet → 释放资源
(贾琏欲执事)
1.加载JDBC驱动
使用Class.forName()方法将给定的JDBC驱动类加载到Java虚拟机中。若系统中不存在给定的类,则会引发异常,异常类型为ClassNotFoundException。
方式一:推荐这种方式,不会对具体的驱动类产生依赖。
Class.forName(“com.MySQL.jdbc.Driver”);
方式二:会造成DriverManager中产生两个一样的驱动,并会对具体的驱动类产生依赖。
DriverManager.registerDriver(com.mysql.jdbc.Driver);
2.建立连接
DriverManager类是JDBC的管理层,作用于用户和驱动程序之间。 DriverManager类跟踪可用的驱动程序,并在数据库和相应的驱动程序之间建立连接。当调用getConnection()方法时, DriverManager类首先从已加载的驱动程序列表中找到一个可以接收该数据库URL的驱动程序,然后请求该驱动程序使用相关的URL、用户名和密码连接到数据库中,于是就建立了与数据库的连接,创建连接对象并返回引用。
Connection conn = DriverManager.getConnection(url, user, password);
URL用于标识数据库的位置,通过URL地址告诉JDBC程序连接哪个数据库,URL的写法为:
其他参数如:useUnicode=true&characterEncoding=utf8
3.创建执行SQL语句的statement
//Statement
String id = "5";
String sql = "delete from table where id=" + id;
Statement st = conn.createStatement();
st.executeQuery(sql);
//存在sql注入的危险
//如果用户传入的id为“5 or 1=1”,那么将删除表中的所有记录
//PreparedStatement 有效的防止sql注入(SQL语句在程序运行前已经进行了预编译,当运行时动态地把参数传给PreprareStatement时,即使参数里有敏感字符如 or '1=1'也数据库会作为一个参数一个字段的属性值来处理而不会作为一个SQL指令)
String sql = “insert into user (name,pwd) values(?,?)”;
PreparedStatement ps = conn.preparedStatement(sql);
ps.setString(1, “col_value”); //占位符顺序从1开始
ps.setString(2, “123456”); //也可以使用setObject
ps.executeQuery();
4.处理执行结果(ResultSet)
ResultSet rs = ps.executeQuery();
While(rs.next()){
rs.getString(“col_name”);
rs.getInt(1);
//…
}
5.释放资源
//数据库连接(Connection)非常耗资源,尽量晚创建,尽量早的释放
//都要加try catch 以防前面关闭出错,后面的就不执行了
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (st != null) {
st.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
四、事务
- 为了提高性能
- 为了保持业务流程的完整性
- 使用分布式事务
可以通过事务在任意时间来控制以及更改应用到数据库。它把单个 SQL 语句或一组 SQL 语句作为一个逻辑单元,如果其中任一语句失败,则整个事务失败。
若要启用手动事务模式来代替 JDBC 驱动程序默认使用的自动提交模式的话,使用 Connection 对象的的 setAutoCommit()方法。如果传递一个布尔值 false 到 setAutoCommit()方法,你就关闭自动提交模式。你也可以传递一个布尔值 true 将其再次打开。
conn.setAutoCommit(false);
提交和回滚
当完成了的修改,并且要提交修改,可以在 connection 对象里调用 commit()方法,如下所示-
conn.commit( );
另外,用名为 conn 的连接回滚数据到数据库,使用如下所示的代码-
conn.rollback( );
try{
//Assume a valid connection object conn
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);
// If there is no error.
conn.commit();
}catch(SQLException se){
// If there is any error.
conn.rollback();
}
在这种情况下,之前的 INSERT 语句不会成功,一切都将被回滚到最初状态。
使用还原点
新的 JDBC 3.0 还原点接口提供了额外的事务控制。大部分现代的数据库管理系统的环境都支持设定还原点,例如 Oracle 的 PL/SQL。
当在事务中设置一个还原点来定义一个逻辑回滚点。如果在一个还原点之后发生错误,那么可以使用 rollback 方法来撤消所有的修改或在该还原点之后所做的修改。
Connection 对象有两个新的方法来管理还原点-
- setSavepoint(String savepointName): 定义了一个新的还原点。它也返回一个 Savepoint 对象。
- releaseSavepoint(Savepoint savepointName): 删除一个还原点。请注意,它需要一个作为参数的 Savepoint 对象。这个对象通常是由 setSavepoint() 方法生成的一个还原点。
有一个 rollback (String savepointName) 方法,该方法可以回滚到指定的还原点。
try{
//Assume a valid connection object conn
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
//set a Savepoint
Savepoint savepoint1 = conn.setSavepoint("Savepoint1");
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', 'Tez')";
stmt.executeUpdate(SQL);
// If there is no error, commit the changes.
conn.commit();
}catch(SQLException se){
// If there is any error.
conn.rollback(savepoint1);
}
五、异常
异常处理可以允许处理一个异常情况,例如可控方式的程序定义错误。
当异常情况发生时,将抛出一个异常。抛出这个词意味着当前执行的程序停止,控制器被重定向到最近的适用的 catch 子句。如果没有适用的 catch 子句存在,那么程序执行被终止。
JDBC 的异常处理是非常类似于 Java 的异常处理,但对于 JDBC,最常见的异常是 java.sql.SQLException。
SQLException 异常在驱动程序和数据库中都可能出现。当出现这个异常时,SQLException 类型的对象将被传递到 catch 子句。
传递的 SQLException 对象具有以下的方法,以下的方法可用于检索该异常的额外信息
方法 | 说明 |
---|---|
getErrorCode( ) | 获取与异常关联的错误号。 |
getMessage( ) | 获取 JDBC 驱动程序的错误信息,该错误是由驱动程序处理的,或者在数据库错误中获取 Oracl 错误号和错误信息。 |
getSQLState( ) | 获取 XOPEN SQLstate 字符串。对于 JDBC 驱动程序错误,使用该方法不能返回有用的信息。对于数据库错误,返回第五位的 XOPEN SQLstate 代码。该方法可以返回 null。 |
getNextException( ) | 获取异常链的下一个 Exception 对象。 |
printStackTrace( ) | 打印当前异常或者抛出,其回溯到标准的流错误。 |
printStackTrace(PrintStream s) | 打印该抛出,其回溯到你指定的打印流。 |
printStackTrace(PrintWriter w) | 打印该抛出,其回溯到你指定的打印写入。 |
//STEP 1. Import required packages
import java.sql.*;
public class JDBCExample {
// JDBC driver name and database URL
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost/EMP";
// Database credentials
static final String USER = "username";
static final String PASS = "password";
public static void main(String[] args) {
Connection conn = null;
try{
//STEP 2: Register JDBC driver
Class.forName("com.mysql.jdbc.Driver");
//STEP 3: Open a connection
System.out.println("Connecting to database...");
conn = DriverManager.getConnection(DB_URL,USER,PASS);
//STEP 4: Execute a query
System.out.println("Creating statement...");
Statement stmt = conn.createStatement();
String sql;
sql = "SELECT id, first, last, age FROM Employees";
ResultSet rs = stmt.executeQuery(sql);
//STEP 5: Extract data from result set
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);
}
//STEP 6: Clean-up environment
rs.close();
stmt.close();
conn.close();
}catch(SQLException se){
//Handle errors for JDBC
se.printStackTrace();
}catch(Exception e){
//Handle errors for Class.forName
e.printStackTrace();
}finally{
//finally block used to close resources
try{
if(conn!=null)
conn.close();
}catch(SQLException se){
se.printStackTrace();
}//end finally try
}//end try
System.out.println("Goodbye!");
}//end main
}//end JDBCExample
六、批处理
批处理是指将关联的 SQL 语句组合成一个批处理,并将他们当成一个调用提交给数据库。
当一次发送多个 SQL 语句到数据库时,可以减少通信的资源消耗,从而提高了性能。
- JDBC 驱动程序不一定支持该功能。可以使用 DatabaseMetaData.supportsBatchUpdates() 方法来确定目标数据库是否支持批处理更新。如果你的JDBC驱动程序支持此功能,则该方法返回值为 true。
- Statement,PreparedStatement 和 CallableStatement 的 addBatch() 方法用于添加单个语句到批处理。
- executeBatch() 方法用于启动执行所有组合在一起的语句。
- executeBatch() 方法返回一个整数数组,数组中的每个元素代表了各自的更新语句的更新数目。
- clearBatch() 此方法删除所有用 addBatch() 方法添加的语句。但是,不能有选择性地选择要删除的语句。
批处理和 Statement 对象
使用 Statement 对象来使用批处理所需要的典型步骤如下所示-
- 使用 createStatement() 方法创建一个 Statement 对象。
- 使用 setAutoCommit() 方法将自动提交设为 false。
- 被创建的 Statement 对象可以使用 addBatch() 方法来添加你想要的所有SQL语句。
- 被创建的 Statement 对象可以用 executeBatch() 将所有的 SQL 语句执行。
- 最后,使用 commit() 方法提交所有的更改。
// Create statement object
Statement stmt = conn.createStatement();
// Set auto-commit to false
conn.setAutoCommit(false);
// Create SQL statement
String SQL = "INSERT INTO Employees (id, first, last, age) " +
"VALUES(200,'Zia', 'Ali', 30)";
// Add above SQL statement in the batch.
stmt.addBatch(SQL);
// Create one more SQL statement
String SQL = "INSERT INTO Employees (id, first, last, age) " +
"VALUES(201,'Raj', 'Kumar', 35)";
// Add above SQL statement in the batch.
stmt.addBatch(SQL);
// Create one more SQL statement
String SQL = "UPDATE Employees SET age = 35 " +
"WHERE id = 100";
// Add above SQL statement in the batch.
stmt.addBatch(SQL);
// Create an int[] to hold returned values
int[] count = stmt.executeBatch();
//Explicitly commit statements to apply changes
conn.commit();
批处理和 PrepareStatement 对象
使用 prepareStatement 对象来使用批处理需要的典型步骤如下所示-
- 使用占位符创建 SQL 语句。
- 使用任一 prepareStatement() 方法创建 prepareStatement 对象。
- 使用 setAutoCommit() 方法将自动提交设为 false。
- 被创建的 Statement 对象可以使用 addBatch() 方法来添加你想要的所有 SQL 语句。
- 被创建的 Statement 对象可以用 executeBatch() 将所有的 SQL 语句执行。
- 最后,使用 commit() 方法提交所有的更改。
// Create SQL statement
String SQL = "INSERT INTO Employees (id, first, last, age) " +
"VALUES(?, ?, ?, ?)";
// Create PrepareStatement object
PreparedStatemen pstmt = conn.prepareStatement(SQL);
//Set auto-commit to false
conn.setAutoCommit(false);
// Set the variables
pstmt.setInt( 1, 400 );
pstmt.setString( 2, "Pappu" );
pstmt.setString( 3, "Singh" );
pstmt.setInt( 4, 33 );
// Add it to the batch
pstmt.addBatch();
// Set the variables
pstmt.setInt( 1, 401 );
pstmt.setString( 2, "Pawan" );
pstmt.setString( 3, "Singh" );
pstmt.setInt( 4, 31 );
// Add it to the batch
pstmt.addBatch();
//add more batches
.
.
.
.
//Create an int[] to hold returned values
int[] count = stmt.executeBatch();
//Explicitly commit statements to apply changes
conn.commit();
七、存储过程
创建 CallableStatement 对象
假设,需要执行下面的 Oracle 存储过程-
CREATE OR REPLACE PROCEDURE getEmpName
(EMP_ID IN NUMBER, EMP_FIRST OUT VARCHAR) AS
BEGIN
SELECT first INTO EMP_FIRST
FROM Employees
WHERE ID = EMP_ID;
END;
注意:上面的存储过程是在 Oracle 使用的,但使用的是 MySQL 数据库,所以在 MySQL 的环境下需要重新写出相同功能的代码,下面的代码是在 EMP 数据库中创建相同功能的代码-
DELIMITER $$
DROP PROCEDURE IF EXISTS `EMP`.`getEmpName` $$
CREATE PROCEDURE `EMP`.`getEmpName`
(IN EMP_ID INT, OUT EMP_FIRST VARCHAR(255))
BEGIN
SELECT first INTO EMP_FIRST
FROM Employees
WHERE ID = EMP_ID;
END $$
DELIMITER ;
当前有三种类型的参数:IN,OUT 和 INOUT。PreparedStatement 对象只能使用 IN 参数。CallableStatement 对象可以使用所有的三种类型。
下面是三种类型参数的定义
参数 | 描述 |
---|---|
IN | 当 SQL 语句创建的时候,该参数的值是未知的。你可以用 setXXX() 方法将值绑定到 IN 参数里。 |
OUT | 该参数的值是由 SQL 语句的返回值。你可以用 getXXX() 方法从 OUT 参数中检索值。 |
INOUT | 该参数同时提供输入和输出值。你可以用 setXXX() 方法将值绑定到 IN 参数里,并且也可以用 getXXX() 方法从 OUT 参数中检索值。 |
下面的代码片段展示了如何使用 Connection.prepareCall() 方法实现一个基于上述存储过程的 CallableStatement 对象
CallableStatement cstmt = null;
try {
String SQL = "{call getEmpName (?, ?)}";
cstmt = conn.prepareCall (SQL);
. . .
}
catch (SQLException e) {
. . .
}
finally {
. . .
}
字符串变量 SQL 使用参数占位符来表示存储过程。
使用 CallableStatement 对象就像使用 PreparedStatement 对象。在执行该语句前,你必须将值绑定到所有的参数,否则你将收到一个 SQL 异常。
如果你有 IN 参数,只要按照适用于 PreparedStatement 对象相同的规则和技巧;用 setXXX()方法来绑定对应的 Java 数据类型。
当你使用 OUT 和 INOUT 参数就必须采用额外的 CallableStatement 方法:registerOutParameter()。registerOutParameter() 方法将 JDBC 数据类型绑定到存储过程返回的数据类型。
一旦你调用了存储过程,你可以用适当的 getXXX()方法从 OUT 参数参数中检索数值。这种方法将检索出来的 SQL 类型的值映射到 Java 数据类型。
关闭 CallableStatement 对象
正如关闭其它的 Statement 对象,出于同样的原因,也应该关闭 CallableStatement 对象。
close()方法简单的调用就可以完成这项工作。如果你先关闭了 Connection 对象,那么它也会关闭 CallableStatement 对象。然而,你应该始终明确关闭 CallableStatement 对象,以确保该对象被彻底关闭。
CallableStatement cstmt = null;
try {
String SQL = "{call getEmpName (?, ?)}";
cstmt = conn.prepareCall (SQL);
. . .
}
catch (SQLException e) {
. . .
}
finally {
cstmt.close();
}
八、流数据
PreparedStatement 对象必须具备使用输入和输出流来提供参数数据的能力。能够将整个文件存储到数据库列中,这样数据库就能存储大型数据,例如 CLOB 和 BLOB 数据类型。
用于流数据有下列几种方法-
- setAsciiStream(): 该方法是用来提供较大的 ASCII 值。
- setCharacterStream(): 该方法是用来提供较大的 UNICODE 值。
- setBinaryStream(): 该方法是用来提供较大的二进制值。
setXXXStream()方法需要一个额外的参数,该参数是除了参数占位符的文件大小。这个参数通知驱动程序通过使用流有多少数据被发送到数据库中。
假如我们到要上传一个名为 XML_Data.xml 的 XML 文件到数据库的表中。下面是该 XML 文件的内容
<?xml version="1.0"?>
<Employee>
<id>100</id>
<first>Zara</first>
<last>Ali</last>
<Salary>10000</Salary>
<Dob>18-08-1978</Dob>
<Employee>
将该 XML 文件和要运行的示例保存在相同的目录的。
这个示例将创建一个数据库表 XML_Data ,然后 XML_Data.xml 将被上传到该表中。
将下面的示例拷贝并粘帖到 JDBCExample.java 中,编译并运行它,如下所示
// Import required packages
import java.sql.*;
import java.io.*;
import java.util.*;
public class JDBCExample {
// JDBC driver name and database URL
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost/EMP";
// Database credentials
static final String USER = "username";
static final String PASS = "password";
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
Statement stmt = null;
ResultSet rs = null;
try{
// Register JDBC driver
Class.forName("com.mysql.jdbc.Driver");
// Open a connection
System.out.println("Connecting to database...");
conn = DriverManager.getConnection(DB_URL,USER,PASS);
//Create a Statement object and build table
stmt = conn.createStatement();
createXMLTable(stmt);
//Open a FileInputStream
File f = new File("XML_Data.xml");
long fileLength = f.length();
FileInputStream fis = new FileInputStream(f);
//Create PreparedStatement and stream data
String SQL = "INSERT INTO XML_Data VALUES (?,?)";
pstmt = conn.prepareStatement(SQL);
pstmt.setInt(1,100);
pstmt.setAsciiStream(2,fis,(int)fileLength);
pstmt.execute();
//Close input stream
fis.close();
// Do a query to get the row
SQL = "SELECT Data FROM XML_Data WHERE id=100";
rs = stmt.executeQuery (SQL);
// Get the first row
if (rs.next ()){
//Retrieve data from input stream
InputStream xmlInputStream = rs.getAsciiStream (1);
int c;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while (( c = xmlInputStream.read ()) != -1)
bos.write(c);
//Print results
System.out.println(bos.toString());
}
// Clean-up environment
rs.close();
stmt.close();
pstmt.close();
conn.close();
}catch(SQLException se){
//Handle errors for JDBC
se.printStackTrace();
}catch(Exception e){
//Handle errors for Class.forName
e.printStackTrace();
}finally{
//finally block used to close resources
try{
if(stmt!=null)
stmt.close();
}catch(SQLException se2){
}// nothing we can do
try{
if(pstmt!=null)
pstmt.close();
}catch(SQLException se2){
}// nothing we can do
try{
if(conn!=null)
conn.close();
}catch(SQLException se){
se.printStackTrace();
}//end finally try
}//end try
System.out.println("Goodbye!");
}//end main
public static void createXMLTable(Statement stmt)
throws SQLException{
System.out.println("Creating XML_Data table..." );
//Create SQL Statement
String streamingDataSql = "CREATE TABLE XML_Data " +
"(id INTEGER, Data LONG)";
//Drop table first if it exists.
try{
stmt.executeUpdate("DROP TABLE XML_Data");
}catch(SQLException se){
}// do nothing
//Build table.
stmt.executeUpdate(streamingDataSql);
}//end createXMLTable
}//end JDBCExample
九、数据库连接池
对于共享资源,有一个很著名的设计模式:资源池(Resource Pool)。
该模式正是为了解决资源的频繁分配﹑释放所造成的问题。
连接池技术的核心思想是连接复用,通过建立一个数据库连接池以及一套连接使用、分配和管理策略,使得该连接池中的连接可以得到高效、安全的复用,避免了数据库连接频繁建立、关闭的开销。
连接池的工作原理
主要由三部分组成,分别为连接池的建立、连接池中连接的使用管理、连接池的关闭。
1.连接池的建立
一般在系统初始化时,连接池会根据系统配置建立,并在池中创建了几个连接对象,以便使用时能从连接池中获取。连接池中的连接不能随意创建和关闭,这样避免了连接随意建立和关闭造成的系统开销。Java中提供了很多容器类可以方便的构建连接池,例如Vector、Stack等。
2.连接池的管理
连接池管理策略是连接池机制的核心,连接池内连接的分配和释放对系统的性能有很大的影响。其管理策略是:
当客户请求数据库连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给客户使用;如果没有空闲连接,则查看当前所开的连接数是否已经达到最大连接数,如果没达到就重新创建一个连接给请求的客户;如果达到就按设定的最大等待时间进行等待,如果超出最大等待时间,则抛出异常给客户。
当客户释放数据库连接时,先判断该连接的引用次数是否超过了规定值,如果超过就从连接池中删除该连接,否则保留为其他客户服务。
该策略保证了数据库连接的有效复用,避免频繁的建立、释放连接所带来的系统资源开销。
3.连接池的关闭
当应用程序退出时,关闭连接池中所有的连接,释放连接池相关的资源,该过程正好与创建相反。
连接池的主要优点
1.减少连接创建时间。连接池中的连接是已准备好的、可重复使用的,获取后可以直接访问数据库,因此减少了连接创建的次数和时间。
2.简化的编程模式。当使用连接池时,每一个单独的线程能够像创建一个自己的JDBC连接一样操作,允许用户直接使用JDBC编程技术。
3.控制资源的使用。
DBCP 数据库连接池
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
public class JdbcUtils_DBCP {
private static DataSource ds = null;
static{
try{
InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties prop = new Properties();
prop.load(in);
BasicDataSourceFactory factory = new BasicDataSourceFactory();
ds = factory.createDataSource(prop);
System.out.println(ds);
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static Connection getConnection() throws SQLException{
return ds.getConnection();
}
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
st.close();
}catch (Exception e) {
e.printStackTrace();
}
}
if(conn!=null){
try{
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
C3P0 数据库连接池
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class JdbcUtils_C3P0 {
private static ComboPooledDataSource ds = null;
static{
try{
ds = new ComboPooledDataSource();
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://localhost:3306/day16");
ds.setUser("root");
ds.setPassword("root");
ds.setInitialPoolSize(10);//最初连接数
ds.setMinPoolSize(5);//最小连接数
ds.setMaxPoolSize(20);//最大连接数
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static Connection getConnection() throws SQLException{
return ds.getConnection();
}
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
st.close();
}catch (Exception e) {
e.printStackTrace();
}
}
if(conn!=null){
try{
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
也可以将配置信息放在xml文件中(src下):c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
<named-config name="mysql">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</named-config>
<named-config name="oracle">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</named-config>
</c3p0-config>
这样JdbcUtils_C3p0可以改为:
/*
ds = new ComboPooledDataSource();
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://localhost:3306/day16");
ds.setUser("root");
ds.setPassword("root");
ds.setInitialPoolSize(10);//最初连接数
ds.setMinPoolSize(5);//最小连接数
ds.setMaxPoolSize(20);//最大连接数
*/
ds = new ComboPooledDataSource(""mysql"");//如果默认()
十、示例
JDBC工具类
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/store28
user=root
password=root1234
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;
public final class JDBCUtilsPlus {
private static final String DRIVER;
private static final String URL;
private static final String USER;
private static final String PASSWORD;
private JDBCUtilsPlus(){}
static {
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
DRIVER = bundle.getString("driver");
URL = bundle.getString("url");
USER = bundle.getString("user");
PASSWORD = bundle.getString("password");
/**
* 驱动注册
*/
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e);
}
}
/**
* 获取 Connetion
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection(URL, USER, PASSWORD);
}
/**
* 释放资源
* @param conn
* @param st
* @param rs
*/
public static void colseResource(Connection conn,Statement st,ResultSet rs) {
closeResultSet(rs);
closeStatement(st);
closeConnection(conn);
}
/**
* 释放连接 Connection
* @param conn
*/
public static void closeConnection(Connection conn) {
if(conn !=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//等待垃圾回收
conn = null;
}
/**
* 释放语句执行者 Statement
* @param st
*/
public static void closeStatement(Statement st) {
if(st !=null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//等待垃圾回收
st = null;
}
/**
* 释放结果集 ResultSet
* @param rs
*/
public static void closeResultSet(ResultSet rs) {
if(rs !=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//等待垃圾回收
rs = null;
}
/**
* insert方法
* @param conn
* @param sql
* @param parems
* @return
* @throws SQLException
*/
public static int insert(Connection conn,String sql,Object[] parems) throws SQLException{
PreparedStatement stmt = null;
try{
stmt = conn.prepareStatement(sql);
if(null != parems && parems.length>0){//判断是否有信息增加
for(int i = 0;i<parems.length;i++){
stmt.setObject(i+1, parems[i]);//使用给定对象设置指定参数的值
}
}
int count = stmt.executeUpdate();
return count;
}finally{
close(stmt);
}
}
/**
* 泛型查询方法
* @param conn
* @param sql
* @param paramsList
* @param rm
* @return
* @throws SQLException
*/
public static <T> List<T> executeQuery(Connection conn,String sql,Object[] paramsList,RowsMapper<T> rm) throws SQLException{
PreparedStatement stmt = null;
ResultSet rs = null;
List<T> list = new ArrayList<T>();
try{
stmt = conn.prepareStatement(sql);
if(null != paramsList && paramsList.length>0){//判断是否有信息增加
for(int i = 0;i<paramsList.length;i++){
stmt.setObject(i+1, paramsList[i]);
}
}
rs = stmt.executeQuery();
while(rs.next()){
T t = rm.getEntity(rs);
list.add(t);
}
return list;
}finally{
close(rs);
close(stmt);
}
}
}
时间处理(Date和Time以及Timestamp区别、随机日期生成)
java.util.Date
- 子类:java.sql.Date
- 子类:java.sql.Time
- 子类:java.sql.Timestamp
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Random;
/**
* 测试时间处理(java.sql.Date,Time,Timestamp)
*/
public class Demo07 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","mysql");
for (int i = 0; i < 1000; i++) {
ps = conn.prepareStatement("insert into t_user(userName,pwd,regTime,lastLoginTime)values(?,?,?,?)");
ps.setObject(1, "小高" + i);
ps.setObject(2, "123");
//
int random = 1000000000 + new Random().nextInt(1000000000); //随机时间
java.sql.Date date = new java.sql.Date(System.currentTimeMillis() - random); //插入随机时间
java.sql.Timestamp stamp = new Timestamp(System.currentTimeMillis()); //如果需要插入指定时间,可以使用Calendar、DateFormat
ps.setDate(3, date);
ps.setTimestamp(4, stamp);
//
ps.execute();
}
System.out.println("插入");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally{
try {
if (ps!=null) {
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn!=null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
/**
* 测试时间处理(java.sql.Date,Time,Timestamp),取出指定时间段的数据
*/
public class Demo08 {
/**
* 将字符串代表的时间转为long数字(格式:yyyy-MM-dd hh:mm:ss)
* @param dateStr
* @return
*/
public static long str2DateTime(String dateStr){
DateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
try {
return format.parse(dateStr).getTime();
} catch (ParseException e) {
e.printStackTrace();
return 0;
}
}
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","mysql");
//
ps = conn.prepareStatement("select * from t_user where regTime > ? and regTime < ?");
java.sql.Date start = new java.sql.Date(str2DateTime("2016-06-20 00:00:00"));
java.sql.Date end = new java.sql.Date(str2DateTime("2016-06-24 00:00:00"));
ps.setObject(1, start);
ps.setObject(2, end);
rs = ps.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("id") + "--" + rs.getString("userName")+"--"+rs.getDate("regTime"));
}
//
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally{
try {
if (ps!=null) {
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn!=null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
CLOB文本大对象操作
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 测试CLOB 文本大对象的使用
* 包含:将字符串、文件内容插入数据库中的CLOB字段和将CLOB字段值取出来的操作。
*/
public class Demo09 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
PreparedStatement ps2 = null;
ResultSet rs = null;
Reader r = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","mysql");
//插入//
ps = conn.prepareStatement("insert into t_user(userName,myInfo)values(?,?)");
ps.setString(1, "小高");
//将文本文件内容直接输入到数据库中
// ps.setClob(2, new FileReader(new File("G:/JAVA/test/a.txt")));
//将程序中的字符串输入到数据库中的CLOB字段中
ps.setClob(2, new BufferedReader(new InputStreamReader(new ByteArrayInputStream("aaaa".getBytes()))));
ps.executeUpdate();
System.out.println("插入");
//
//查询//
ps2 = conn.prepareStatement("select * from t_user where id=?");
ps2.setObject(1, 223021);
rs = ps2.executeQuery();
System.out.println("查询");
while (rs.next()) {
Clob c = rs.getClob("myInfo");
r = c.getCharacterStream();
int temp = 0;
while ((temp=r.read())!=-1) {
System.out.print((char)temp);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally{
try {
if (r!=null) {
r.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if (rs!=null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (ps2!=null) {
ps2.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (ps!=null) {
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn!=null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
BLOB二进制大对象的使用
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 测试BLOB 二进制大对象的使用
*/
public class Demo10 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
PreparedStatement ps2 = null;
ResultSet rs = null;
InputStream is = null;
OutputStream os = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","mysql");
//插入//
ps = conn.prepareStatement("insert into t_user(userName,headImg)values(?,?)");
ps.setString(1, "小高");
ps.setBlob(2, new FileInputStream("G:/JAVA/test/d.jpg"));
ps.execute();
//
//查询//
ps2 = conn.prepareStatement("select * from t_user where id=?");
ps2.setObject(1, 223024);
rs = ps2.executeQuery();
System.out.println("查询");
while (rs.next()) {
Blob b = rs.getBlob("headImg");
is = b.getBinaryStream();
os = new FileOutputStream("G:/JAVA/test/h.jpg");
int temp = 0;
while ((temp=is.read())!=-1) {
os.write(temp);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally{
try {
if (os!=null) {
os.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if (is!=null) {
is.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if (rs!=null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (ps2!=null) {
ps2.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (ps!=null) {
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn!=null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
Reference
本作品采用《CC 协议》,转载必须注明作者和本文链接