RussianLDP Рейтинг@Mail.ru
WebMoney: 
WMZ Z294115950220 
WMR R409981405661 
WME E134003968233 
Visa 
4274 3200 2453 6495 

Глава 7. Концепции JDBC

Эта секция обеспечивает некоторые общие концепции JDBC.

7.1. Соединение с MySQL, используя интерфейс JDBC DriverManager

Когда вы используете JDBC за пределами сервера приложений, класс DriverManager управляет связями.

Определите для DriverManager, с какими драйверами JDBC попытаться установить связи. Самый легкий способ сделать это: использовать Class.forName() в классе, который осуществляет интерфейс java.sql.Driver. С MySQL Connector/J название этого класса com.mysql.cj.jdbc.Driver. С этим методом вы могли использовать внешний конфигурационный файл, чтобы поставлять имя класса драйвера и параметры, чтобы использовать, соединяясь с базой данных.

Следующий раздел показывает код Java, как вы могли бы зарегистрировать MySQL Connector/J из метода main() вашего приложения. Проверяя этот код, сначала прочитайте инсталляционную секцию в главе 4, чтобы удостовериться, что установили соединитель правильно и настроили CLASSPATH. Кроме того, гарантируйте, что MySQL формируется, чтобы принять внешние связи TCP/IP.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

// Notice, do not import com.mysql.cj.jdbc.*
// or you will have problems!
public class LoadDriver {
  public static void main(String[] args)
  {
    try {
      // The newInstance() call is a work around for some
      // broken Java implementations
      Class.forName("com.mysql.cj.jdbc.Driver").newInstance();
    } catch (Exception ex) {
      // handle the error
    }
  }
}

После того, как драйвер был зарегистрирован в DriverManager, можно получить экземпляр Connection, который связан с конкретной базой данных, вызывая DriverManager.getConnection():

Пример 7.1. Connector/J: Получение связи от DriverManager

Если вы еще не сделали этого, пожалуйста, просмотрите раздел 7.1 выше прежде, чем работать с примером ниже.

Этот пример показывает, как можно получить экзепляр Connection из DriverManager. Есть несколько различных сигнатур для метода getConnection(). Консультируйтесь с документацией API, которая идет с вашим JDK для более определенной информации о том, как использовать их.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

Connection conn = null;
...
try {
  conn = DriverManager.getConnection("jdbc:mysql://localhost/test?" +
                                     "user=minty&password=greatsqldb");
  // Do something with the Connection
   ...
} catch (SQLException ex) {
  // handle any errors
  System.out.println("SQLException: " + ex.getMessage());
  System.out.println("SQLState: " + ex.getSQLState());
  System.out.println("VendorError: " + ex.getErrorCode());
}

После установки Connection это может использоваться, чтобы создать объекты Statement и PreparedStatement, а также получить метаданные о базе данных. Это объяснено в следующих разделах.

7.2. Использование объектов JDBC Statement, чтобы выполнить SQL

Объекты Statement позволяют вам выполнять основные SQL-запросы и получать результаты через класс ResultSet, который описан позже.

Чтобы создать экземпляр Statement, вызовите метод createStatement() на объекте Connection, который вы получили с использованием одного из методов DriverManager.getConnection() или DataSource.getConnection(), описанных ранее.

Имея экземпляр Statement, можно выполнить запрос SELECT вызовом метода executeQuery(String) с SQL, который вы хотите использовать.

Чтобы обновить данные в базе данных, используйте метод executeUpdate(String SQL). Этот метод возвращает количество строк, соответствовавших запросу обновления, а не количество строк, которые были изменены.

Если вы не знаете заранее, будет ли SQL-оператор be a SELECT или UPDATE/ INSERT, тогда можно использовать метод. Он вернет true, если SQL-запрос был SELECT, или false, если SQL-запрос был UPDATE, INSERT или DELETE. Если это был SELECT, можно получить результаты, вызвав метод getResultSet(). Если это был UPDATE, INSERT или DELETE, можно узнать затронутое количество строк вызовом getUpdateCount() в экземпляре Statement.

Пример 7.2. Connector/J: применение java.sql.Statement для выполнения SELECT

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.ResultSet;

// assume that conn is an already created JDBC connection (see previous examples)
Statement stmt = null;
ResultSet rs = null;
try {
  stmt = conn.createStatement();
  rs = stmt.executeQuery("SELECT foo FROM bar");
  // or alternatively, if you don't know ahead of time that
  // the query will be a SELECT...
  if (stmt.execute("SELECT foo FROM bar")) {
     rs = stmt.getResultSet();
  }
  // Now do something with the ResultSet ....
}
catch (SQLException ex) {
  // handle any errors
  System.out.println("SQLException: " + ex.getMessage());
  System.out.println("SQLState: " + ex.getSQLState());
  System.out.println("VendorError: " + ex.getErrorCode());
}
finally {
  // it is a good idea to release resources in a finally{} block
  // in reverse-order of their creation
  // if they are no-longer needed
  if (rs != null) {
     try {
       rs.close();
     } catch (SQLException sqlEx) { }   // ignore
     rs = null;
  }
  if (stmt != null) {
     try {
       stmt.close();
     } catch (SQLException sqlEx) { }   // ignore
     stmt = null;
  }
}

7.3. Применение JDBC CallableStatements для выполнения сохраненных процедур

Connector/J полностью понимает интерфейс java.sql.CallableStatement.

Для получения дополнительной информации о хранимых процедурах MySQL, пожалуйста, обратитесь к Using Stored Routines.

Connector/J выставляет функциональность хранимой процедуры через интерфейс JDBC CallableStatement.

Следующий пример показывает хранимую процедуру, которая возвращает значение inOutParam +1 и последовательность, переданную с использованием inputParam, как ResultSet:

Пример 7.3. Connector/J: Запрос хранимых процедур

CREATE PROCEDURE demoSp(IN inputParam VARCHAR(255), INOUT inOutParam INT)
BEGIN
  DECLARE z INT;
  SET z = inOutParam + 1;
  SET inOutParam = z;
  SELECT inputParam;
  SELECT CONCAT('zyxw', inputParam);
END

Чтобы использовать процедуру demoSp с Connector/J, выполните эти шаги:

  1. Подготовьте запрос при помощи Connection.prepareCall().

    Заметьте, что необходимо использовать синтаксис JDBC escape и что круглые скобки, окружающие заполнители параметра, не опциональны:

    Пример 7.4. Connector/J: Применение Connection.prepareCall()

    import java.sql.CallableStatement;
    ...
    // Prepare a call to the stored procedure 'demoSp'
    // with two parameters
    //
    // Notice the use of JDBC-escape syntax ({call ...})
    CallableStatement cStmt = conn.prepareCall("{call demoSp(?, ?)}");
    cStmt.setString(1, "abcdefg");
    

    Connection.prepareCall() дорогой метод из-за поиска метаданных, который драйвер выполняет, чтобы поддержать параметры вывода. По исполнительным причинам минимизируйте ненужные запросы Connection.prepareCall() снова используя экземпляр CallableStatement.

  2. Зарегистрируйте параметры вывода (если есть).

    Чтобы получить значения параметров вывода (параметры, определенные как OUT или INOUT, когда вы создали хранимую процедуру), JDBC требует, чтобы они были определены перед выполнением запроса, используя различные методы registerOutputParameter() в интерфейсе the CallableStatement:

    Пример 7.5. Connector/J: Регистрация параметров вывода

    import java.sql.Types;
    ...
    // Connector/J supports both named and indexed
    // output parameters. You can register output
    // parameters using either method, as well
    // as retrieve output parameters using either
    // method, regardless of what method was used to register them.
    //
    // The following examples show how to use
    // the various methods of registering
    // output parameters (you should of course
    // use only one registration per parameter).
    
    // Registers the second parameter as output, and
    // uses the type 'INTEGER' for values returned
    // from getObject()
    //
    cStmt.registerOutParameter(2, Types.INTEGER);
    
    // Registers the named parameter 'inOutParam', and
    // uses the type 'INTEGER' for values returned
    // from getObject()
    cStmt.registerOutParameter("inOutParam", Types.INTEGER);
    ...
    
  3. Установите входные параметры (если есть).

    Параметры ввода и in/out заданы как для объектов PreparedStatement, но CallableStatement также понимают параметры по имени:

    Пример 7.6. Connector/J: Установка входных параметров CallableStatement

    ...
    // Set a parameter by index
    cStmt.setString(1, "abcdefg");
    
    // Alternatively, set a parameter using the parameter name
    cStmt.setString("inputParam", "abcdefg");
    // Set the 'in/out' parameter using an index
    cStmt.setInt(2, 1);
    
    // Alternatively, set the 'in/out' parameter by name
    cStmt.setInt("inOutParam", 1);
    ...
    
  4. Выполните CallableStatement и получите любые наборы результатов или выходные параметры.

    Хотя CallableStatement допускает вызов любого из методов выполнения Statement (executeUpdate(), executeQuery() или execute()), самый гибкий метод, это вызвать execute(), поскольку вы не должны знать заранее, что хранимая процедура возвращает наборы результатов:

    Пример 7.7. Connector/J: Получение результатов и значений выходных параметров

    ...
    boolean hadResults = cStmt.execute();
    //
    // Process all returned result sets
    //
    while (hadResults) {
      ResultSet rs = cStmt.getResultSet();
      // process result set
      ...
      hadResults = cStmt.getMoreResults();
    }
    
    // Retrieve output parameters
    //
    // Connector/J supports both index-based and
    // name-based retrieval
    int outputValue = cStmt.getInt(2);          // index-based
    outputValue = cStmt.getInt("inOutParam");   // name-based
    ...
    

7.4. Получение силами JDBC значений столбцов AUTO_INCREMENT

getGeneratedKeys() это предпочтительный метод, если необходимо получить AUTO_INCREMENT через JDBC, это иллюстрировано в первом примере ниже. Второй пример показывает, как можно получить то же самое значение, используя стандартный запрос SELECT LAST_INSERT_ID(). Заключительный пример показывает, как обновляемые наборы результатов могут получить значение AUTO_INCREMENT, используя метод insertRow().

Пример 7.8. Connector/J: Получение значений столбца AUTO_INCREMENT через Statement.getGeneratedKeys()

Statement stmt = null;
ResultSet rs = null;
try {
  // Create a Statement instance that we can use for
  // 'normal' result sets assuming you have a
  // Connection 'conn' to a MySQL database already available
  stmt = conn.createStatement();

  // Issue the DDL queries for the table for this example
  stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial");
  stmt.executeUpdate("CREATE TABLE autoIncTutorial ("
                     + "priKey INT NOT NULL AUTO_INCREMENT, "
                     + "dataField VARCHAR(64), PRIMARY KEY (priKey))");

  // Insert one row that will generate an AUTO INCREMENT
  // key in the 'priKey' field
  stmt.executeUpdate("INSERT INTO autoIncTutorial (dataField) "
                     + "values ('Can I Get the Auto Increment Field?')",
                     Statement.RETURN_GENERATED_KEYS);

  // Example of using Statement.getGeneratedKeys()
  // to retrieve the value of an auto-increment value
  int autoIncKeyFromApi = -1;
  rs = stmt.getGeneratedKeys();
  if (rs.next()) {
     autoIncKeyFromApi = rs.getInt(1);
  } else {
    // throw an exception from here
  }
  System.out.println("Key returned from getGeneratedKeys():"
                     + autoIncKeyFromApi);
} finally {
  if (rs != null) {
     try {
       rs.close();
     } catch (SQLException ex) {
       // ignore
     }
  }
  if (stmt != null) {
     try {
       stmt.close();
     } catch (SQLException ex) {
       // ignore
     }
  }
}

Пример 7.9. Connector/J: Получение значений столбца AUTO_INCREMENT через SELECT LAST_INSERT_ID()

Statement stmt = null;
ResultSet rs = null;
try {
  // Create a Statement instance that we can use for
  // 'normal' result sets.
  stmt = conn.createStatement();

  // Issue the DDL queries for the table for this example
  stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial");
  stmt.executeUpdate("CREATE TABLE autoIncTutorial ("
                     + "priKey INT NOT NULL AUTO_INCREMENT, "
                     + "dataField VARCHAR(64), PRIMARY KEY (priKey))");

  // Insert one row that will generate an AUTO INCREMENT
  // key in the 'priKey' field
  stmt.executeUpdate("INSERT INTO autoIncTutorial (dataField) "
                     + "values ('Can I Get the Auto Increment Field?')");

  // Use the MySQL LAST_INSERT_ID()
  // function to do the same thing as getGeneratedKeys()
  int autoIncKeyFromFunc = -1;
  rs = stmt.executeQuery("SELECT LAST_INSERT_ID()");
  if (rs.next()) {
     autoIncKeyFromFunc = rs.getInt(1);
  } else {
    // throw an exception from here
  }
  System.out.println("Key returned from " + "'SELECT LAST_INSERT_ID()': " +
                     autoIncKeyFromFunc);
} finally {
  if (rs != null) {
     try {
       rs.close();
     } catch (SQLException ex) {
       // ignore
     }
  }
  if (stmt != null) {
     try {
       stmt.close();
     } catch (SQLException ex) {
       // ignore
     }
  }
}

Пример 7.10. Connector/J: Получение значений столбца AUTO_INCREMENT в Updatable ResultSets

Statement stmt = null;
ResultSet rs = null;
try {
  // Create a Statement instance that we can use for
  // 'normal' result sets as well as an 'updatable'
  // one, assuming you have a Connection 'conn' to
  // a MySQL database already available
  stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
  java.sql.ResultSet.CONCUR_UPDATABLE);

  // Issue the DDL queries for the table for this example
  stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial");
  stmt.executeUpdate("CREATE TABLE autoIncTutorial ("
                     + "priKey INT NOT NULL AUTO_INCREMENT, "
                     + "dataField VARCHAR(64), PRIMARY KEY (priKey))");

  // Example of retrieving an AUTO INCREMENT key
  // from an updatable result set
  rs = stmt.executeQuery("SELECT priKey, dataField "+"FROM autoIncTutorial");
  rs.moveToInsertRow();
  rs.updateString("dataField", "AUTO INCREMENT here?");
  rs.insertRow();

  // the driver adds rows at the end
  rs.last();
  // We should now be on the row we just inserted
  int autoIncKeyFromRS = rs.getInt("priKey");
  System.out.println("Key returned for inserted row: " + autoIncKeyFromRS);
} finally {
  if (rs != null) {
     try {
       rs.close();
     } catch (SQLException ex) {
       // ignore
     }
  }
  if (stmt != null) {
     try {
       stmt.close();
     } catch (SQLException ex) {
       // ignore
     }
  }
}

Управление предыдущим примером кода должно произвести следующий вывод:

Key returned from getGeneratedKeys(): 1
Key returned from SELECT LAST_INSERT_ID(): 1
Key returned for inserted row: 1

Время от времени это может быть хитро, чтобы использовать запрос SELECT LAST_INSERT_ID(), поскольку значение привязано к соединению. Так, если некоторый другой запрос происходит на той же самой связи, значение переписано. С другой стороны, метод getGeneratedKeys() относится к экземпляру Statement, таким образом, это может использоваться, даже если другие запросы происходят на той же самой связи, но не на том же самом Statement.

Поиск

 

Найди своих коллег!

Вы можете направить письмо администратору этой странички, Алексею Паутову. mailto:alexey.v.pautov@mail.ru