JAVA-JDBC

JAVA-JDBC

​ 参考连接:https://www.yiibai.com/jdbc/jdbc_quick_guide.htmljava

JDBC is wtf?

​ JDBC表明Java数据库链接(Java Database Connectivity),它是用于Java编程语言和数据库之间的数据库无关链接的标准Java API,换句话说:JDBC是用于在Java语言编程中与数据库链接的API。mysql

JDBC库包括一般与数据库使用相关,以下面提到的每一个任务的API -sql

  • 链接到数据库
  • 建立SQL或MySQL语句
  • 在数据库中执行SQL或MySQL查询
  • 查看和修改结果记录

JDBC架构

JDBC API支持用于数据库访问的两层和三层处理模型,但一般,JDBC体系结构由两层组成:数据库

  • JDBC API:提供应用程序到JDBC管理器链接。
  • JDBC驱动程序API:支持JDBC管理器到驱动程序链接。

JDBC API使用驱动程序管理器并指定数据库的驱动程序来提供与异构数据库的透明链接。编程

JDBC驱动程序管理器确保使用正确的驱动程序来访问每一个数据源。 驱动程序管理器可以支持链接到多个异构数据库的多个并发驱动程序。数组

如下是架构图,它显示了驱动程序管理器相对于JDBC驱动程序和Java应用程序的位置服务器

常见的JDBC组件

JDBC API提供如下接口和类 -架构

  • DriverManager:此类管理数据库驱动程序列表。 使用通讯子协议未来自java应用程序的链接请求与适当的数据库驱动程序进行匹配。在JDBC下识别某个子协议的第一个驱动程序将用于创建数据库链接。
  • Driver:此接口处理与数据库服务器的通讯。咱们不多会直接与Driver对象进行交互。 但会使用DriverManager对象来管理这种类型的对象。 它还提取与使用Driver对象相关的信息。
  • Connection:此接口具备用于联系数据库的全部方法。 链接(Connection)对象表示通讯上下文,即,与数据库的全部通讯仅经过链接对象。
  • Statement:使用今后接口建立的对象将SQL语句提交到数据库。 除了执行存储过程以外,一些派生接口还接受参数。
  • ResultSet:在使用Statement对象执行SQL查询后,这些对象保存从数据库检索的数据。 它做为一个迭代器并可移动ResultSet对象查询的数据。
  • SQLException:此类处理数据库应用程序中发生的任何错误。

demo

//STEP 1. Import required packages
import java.sql.*;

public class FirstExample {
   // 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 = "root";
   static final String PASS = "123456";

   public static void main(String[] args) {
   Connection conn = null;
   Statement stmt = 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...");
      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(stmt!=null)
            stmt.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("There are so thing wrong!");
}//end main
}//end FirstExample

JDBC数据库链接

创建JDBC链接所涉及的编程至关简单。 如下是基本的四个步骤 -并发

  • 导入JDBC包:使用Java语言的import语句在Java代码开头位置导入所需的类。
  • 注册JDBC驱动程序:使JVM将所需的驱动程序实现加载到内存中,从而能够知足JDBC请求。
  • 数据库URL配置:建立一个正确格式化的地址,指向要链接到的数据库(如:MySQL,Oracle和MSSQL等等)。
  • 建立链接对象:最后,调用DriverManager对象的getConnection()方法来创建实际的数据库链接。
Class.forName("com.mysql.jdbc.Driver");
Connection conn = null;
conn = DriverManager.getConnection("jdbc:mysql://hostname:port/db_name","db_username", "db_password");
conn.close()

JDBC Statements

接口 推荐使用
Statement 用于对数据库进行通用访问,在运行时使用静态SQL语句时颇有用。 Statement接口不能接受参数。
PreparedStatement 当计划要屡次使用SQL语句时使用。PreparedStatement接口在运行时接受输入参数。
CallableStatement 当想要访问数据库存储过程时使用。CallableStatement接口也能够接受运行时输入参数。

Statement对象

​ 在使用Statement对象执行SQL语句以前,须要使用Connection对象的createStatement()方法建立一个Statement对象,如如下示例所示:

Statement stmt = null;
try {
   stmt = conn.createStatement( );
   . . .
}
catch (SQLException e) {
   . . .
}
finally {
   stmt.close();
}

在建立Statement对象后,可使用它来执行一个SQL语句,它有三个执行方法能够执行。它们分别是 -

  • boolean execute (String SQL) : 若是能够检索到ResultSet对象,则返回一个布尔值true; 不然返回false。使用此方法执行SQLDDL语句或须要使用真正的动态SQL,可以使用于执行建立数据库,建立表的SQL语句等等。
  • int executeUpdate (String SQL): 返回受SQL语句执行影响的行数。使用此方法执行预期会影响多行的SQL语句,例如:INSERTUPDATEDELETE语句。
  • ResultSet executeQuery(String SQL):返回一个ResultSet对象。 当您但愿得到结果集时,请使用此方法,就像使用SELECT语句同样。

PreparedStatement对象

PreparedStatement接口扩展了Statement接口,它添加了比Statement对象更好一些优势的功能。

此语句能够动态地提供/接受参数。

PreparedStatement pstmt = null;
try {
   String SQL = "Update Employees SET age = ? WHERE id = ?";
   pstmt = conn.prepareStatement(SQL);
   . . .
}
catch (SQLException e) {
   . . .
}
finally {
   pstmt.close();
}

​ JDBC中的全部参数都由 ? 符号做为占位符,这被称为参数标记。 在执行SQL语句以前,必须为每一个参数(占位符)提供值。

setXXX()方法将值绑定到参数,其中XXX表示要绑定到输入参数的值的Java数据类型。 若是忘记提供绑定值,则将会抛出一个SQLException

​ 每一个参数标记是它其顺序位置引用。第一个标记表示位置1,下一个位置2等等。 该方法与Java数组索引不一样(它不从0开始)。

​ 全部Statement对象与数据库交互的方法(a)execute(),(b)executeQuery()和(c)executeUpdate()也能够用于PreparedStatement对象。 可是,这些方法被修改成可使用输入参数的SQL语句。

CallableStatement对象

​ 相似Connection对象建立StatementPreparedStatement对象同样,它还可使用一样的方式建立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;

​ 存在三种类型的参数:INOUTINOUTPreparedStatement对象只使用IN参数。CallableStatement对象可使用上面三个参数类型。

如下是上面三种类型参数的定义 -

参数 描述
IN 建立SQL语句时其参数值是未知的。 使用setXXX()方法将值绑定到IN参数。
OUT 由SQL语句返回的参数值。可使用getXXX()方法从OUT参数中检索值。
INOUT 提供输入和输出值的参数。使用setXXX()方法绑定变量并使用getXXX()方法检索值。

​ 如下代码片断显示了如何使用Connection.prepareCall()方法根据上述存储过程来实例化一个CallableStatement对象 -

CallableStatement cstmt = 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...");
      String sql = "{call getEmpName (?, ?)}";
      stmt = conn.prepareCall(sql);

      //Bind IN parameter first, then bind OUT parameter
      int empID = 102;
      stmt.setInt(1, empID); // This would set ID as 102
      // Because second parameter is OUT so register it
      stmt.registerOutParameter(2, java.sql.Types.VARCHAR);

      //Use execute method to run stored procedure.
      System.out.println("Executing stored procedure..." );
      stmt.execute();

      //Retrieve employee name with getXXX method
      String empName = stmt.getString(2);
      System.out.println("Emp Name with ID:" + 
               empID + " is " + empName);
      stmt.close();
      conn.close();
}
catch (SQLException e) {
   . . .
}
finally {
   . . .
}

​ String变量strSQL表示存储过程,带有两个参数占位符。

​ 使用CallableStatement对象就像使用PreparedStatement对象同样。 在执行语句以前,必须将值绑定到全部参数,不然将抛出一个SQLException异常。

​ 若是有IN参数,只需遵循适用于PreparedStatement对象的相同规则和技术; 使用与绑定的Java数据类型相对应的setXXX()方法。

​ 使用OUTINOUT参数时,必须使用一个额外的CallableStatement对象方法registerOutParameter()registerOutParameter()方法将JDBC数据类型绑定到存储过程并返回预期数据类型。

​ 当调用存储过程,可使用适当的getXXX()方法从OUT参数中检索该值。 此方法将检索到的SQL类型的值转换为对应的Java数据类型。

JDBC结果集

​ SQL语句执行后从数据库查询读取数据,返回的数据放在结果集中。 SELECT语句用于从数据库中选择行并在结果集中查看它们的标准方法。 java.sql.ResultSet接口表示数据库查询的结果集。ResultSet对象维护指向结果集中当前行的游标。 术语“结果集”是指包含在ResultSet对象中的行和列数据。

ResultSet接口的方法能够分为三类:

  • 浏览方法:用于移动光标。
  • 获取方法:用于查看光标指向的当前行的列中的数据。
  • 更新方法:用于更新当前行的列中的数据。 而后在基础数据库中更新数据。

光标能够基于ResultSet的属性移动。当建立生成ResultSet的相应Statement时,将指定这些属性。

JDBC提供如下链接方法来建立具备所需ResultSet的语句 -

  • createStatement(int RSType, int RSConcurrency);
  • prepareStatement(String SQL, int RSType, int RSConcurrency);
  • prepareCall(String sql, int RSType, int RSConcurrency);

ResultSet类型

可能的RSType值以下。若是不指定任何ResultSet类型,将自动分配一个TYPE_FORWARD_ONLY值。

类型 描述
ResultSet.TYPE_FORWARD_ONLY 光标只能在结果集中向前移动。
ResultSet.TYPE_SCROLL_INSENSITIVE 光标能够向前和向后滚动,结果集对建立结果集后发生的数据库所作的更改不敏感。
ResultSet.TYPE_SCROLL_SENSITIVE 光标能够向前和向后滚动,结果集对建立结果集以后发生的其余数据库的更改敏感。

ResultSet的并发性

可能的RSConcurrency以下。 若是不指定任何并发类型,将自动得到一个CONCUR_READ_ONLY值。

并发 描述
ResultSet.CONCUR_READ_ONLY 建立只读结果集,这是默认值。
ResultSet.CONCUR_UPDATABLE 建立可更新的结果集
try {
   Statement stmt = conn.createStatement(
                           ResultSet.TYPE_FORWARD_ONLY,
                           ResultSet.CONCUR_READ_ONLY);
}
catch(Exception ex) {
   ....
}
finally {
   ....
}

浏览结果集

ResultSet接口中有几种涉及移动光标的方法,包括 -

编号 方法 描述
1 public void beforeFirst() throws SQLException 将光标移动到第一行以前
2 public void afterLast() throws SQLException 将光标移动到最后一行以后。
3 public boolean first() throws SQLException 将光标移动到第一行。
4 public void last() throws SQLException 将光标移动到最后一行。
5 public boolean absolute(int row) throws SQLException 将光标移动到指定的行。
6 public boolean relative(int row) throws SQLException 从当前指向的位置,将光标向前或向后移动给定行数。
7 public boolean previous() throws SQLException 将光标移动到上一行。 若是上一行关闭结果集,此方法返回false
8 public boolean next() throws SQLException 将光标移动到下一行。 若是结果集中没有更多行,则此方法返回false
9 public int getRow() throws SQLException 返回光标指向的行号。
10 public void moveToInsertRow() throws SQLException 将光标移动到结果集中的特殊行,该行可用于将新行插入数据库。当前光标位置被记住。
11 public void moveToCurrentRow() throws SQLException 若是光标当前位于插入行,则将光标移回当前行; 不然,此方法什么也不作

查看结果集

ResultSet接口包含数十种获取当前行数据的方法。

每一个可能的数据类型都有一个get方法,每一个get方法有两个版本 -

  • 一个是采用列名称。
  • 另外一个采用列索引。

例如,若是对查看感兴趣的列包含一个int,则须要使用ResultSet的其中一个getInt()方法 -

序号 方法 描述
1 public int getInt(String columnName) throws SQLException 返回名为columnName的列中当前行中的int值。
2 public int getInt(int columnIndex) throws SQLException 返回指定列索引当前行中的int值。列索引从1开始,意味着行的第一列为1,行的第二列为2,依此类推。

更新结果集

ResultSet接口包含用于更新结果集的数据的更新方法的集合。

与get方法同样,每种数据类型都有两种更新方法 -

  • 一个是采用列名称。
  • 另外一个采用列索引。

例如,要更新结果集当前行的String列,可使用如下updateString()方法之一:

序号 方法 描述
1 public void updateString(int columnIndex, String s) throws SQLException 将指定列中的String值更改成指定的s值。
2 public void updateString(String columnName, String s) throws SQLException 与前前的方法相似,除了使用列的名称而不是列的索引指定。

有八种基本数据类型的更新方法,以及java.sql包中的StringObjectURL和SQL数据类型。

更新结果集中的一行会更改ResultSet对象中当前行的列,但不会更改底层数据库中的列的值。 要更新数据库中的行,须要调用如下方法之一。

序号 方法 描述
1 public void updateRow() 更新数据库中当前行
2 public void deleteRow() 从数据库中删除当前行
3 public void refreshRow() 刷新结果集中的数据以反映数据库中最近的任何更改。
4 public void cancelRowUpdates() 取消对当前行所作的任何更新。
5 public void insertRow() 在数据库中插入一行。 只有当光标指向插入行时,才能调用此方法。

JDBC事务

若是JDBC链接处于自动提交模式,默认状况下,则每一个SQL语句在完成后都会提交到数据库。

对于简单的应用程序可能没有问题,可是有三个缘由须要考虑是否关闭自动提交并管理本身的事务 -

  • 提升性能
  • 保持业务流程的完整性
  • 使用分布式事务

事务可以控制什么时候更改提交并应用于数据库。 它将单个SQL语句或一组SQL语句视为一个逻辑单元,若是任何语句失败,整个事务将失败。

要启用手动事务支持,而不是使用JDBC驱动程序默认使用的自动提交模式,请调用Connection对象的setAutoCommit()方法。 若是将布尔的false传递给setAutoCommit(),则关闭自动提交。 也能够传递一个布尔值true来从新打开它。

例如,若是有一个名为connConnection对象,请将如下代码关闭自动提交 -

conn.setAutoCommit(false);

提交和回滚

完成更改后,若要提交更改,那么可在链接对象上调用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新添加了Savepoint接口提供了额外的事务控制能力。大多数现代DBMS支持其环境中的保存点,如Oracle的PL/SQL。

设置保存点(Savepoint)时,能够在事务中定义逻辑回滚点。 若是经过保存点(Savepoint)发生错误时,则可使用回滚方法来撤消全部更改或仅保存保存点以后所作的更改。

Connection对象有两种新的方法可用来管理保存点 -

  • setSavepoint(String savepointName): - 定义新的保存点,它还返回一个Savepoint对象。
  • releaseSavepoint(Savepoint savepointName): - 删除保存点。要注意,它须要一个Savepoint对象做为参数。 该对象一般是由setSavepoint()方法生成的保存点。

有一个rollback (String savepointName)方法,它将使用事务回滚到指定的保存点。

如下示例说明了使用Savepoint对象 -

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, 24, 'Curry', 'Stephen')";
   stmt.executeUpdate(SQL);  
   //Submit a malformed SQL statement that breaks
   String SQL = "INSERTED IN Employees " +
                "VALUES (107, 32, 'Kobe', 'Bryant')";
   stmt.executeUpdate(SQL);
   // If there is no error, commit the changes.
   conn.commit();

}catch(SQLException se){
   // If there is any error.
   conn.rollback(savepoint1);
}

在这种状况下,上述INSERT语句都不会成功,由于全部操做都被回滚了。

JDBC批量处理

​ 批量处理容许将相关的SQL语句分组到批处理中,并经过对数据库的一次调用来提交它们,一次执行完成与数据库之间的交互。

一次向数据库发送多个SQL语句时,能够减小通讯开销,从而提升性能。

  • 不须要JDBC驱动程序来支持此功能。应该使用DatabaseMetaData.supportsBatchUpdates()方法来肯定目标数据库是否支持批量更新处理。若是JDBC驱动程序支持此功能,该方法将返回true
  • StatementPreparedStatementCallableStatementaddBatch()方法用于将单个语句添加到批处理。 executeBatch()用于执行组成批量的全部语句。
  • executeBatch()返回一个整数数组,数组的每一个元素表示相应更新语句的更新计数。
  • 就像将批处理语句添加处处理中同样,可使用clearBatch()方法删除它们。此方法将删除全部使用addBatch()方法添加的语句。 可是,没法指定选择某个要删除的语句。

使用Statement对象进行批处理

如下是使用Statement对象的批处理的典型步骤序列 -

  • 使用createStatement()方法建立Statement对象。
  • 使用setAutoCommit()将自动提交设置为false
  • 使用addBatch()方法在建立的Statement对象上添加SQL语句到批处理中。
  • 在建立的Statement对象上使用executeBatch()方法执行全部SQL语句。
  • 最后,使用commit()方法提交全部更改。

使用PrepareStatemeng对象进行批处理

如下是使用PrepareStatement对象进行批处理的典型步骤顺序 -

  • 使用占位符建立SQL语句。
  • 使用prepareStatement()方法建立PrepareStatement对象。
  • 使用setAutoCommit()将自动提交设置为false
  • 使用addBatch()方法在建立的Statement对象上添加SQL语句到批处理中。
  • 在建立的Statement对象上使用executeBatch()方法执行全部SQL语句。
  • 最后,使用commit()方法提交全部更改。
相关文章
相关标签/搜索