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

JDBC详解

JDBC指南

Java中JDBC的使用详解

JDBC详细介绍

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!