В прошлом уроке мы изучили что такое соединение и способы его создания.
После того как соединение было установленно, мы можем начинать взаимодействие с базой данных (далее – БД).
Взаимодействовать с БД мы можем с помощью трёх интерфейсов, которые имплементируются каждым драйвером:
- Statement
Этот интерфейс используется для доступа к БД для общих целей. Он крайне полезен, когда мы используем статические SQL – выражения во время работы программы. Этот интерфейс не принимает никаких параметров. - PreparedStatement
Этот интерфейс используется в случае, когда мы планируем использовать SQL – выражения множество раз. Он принимает параметры во время работы программы. - CallableStatement
Этот интерфейс становится полезным в случае, когда мы хотим получить доступ к различным процедурам БД. Он также может принимать параметры во время работы программы.
Создание экземпляра Statement
Прежде, чем мы сможем использовать экземпляр Statement для выполнения SQL – запросов, нам необходимо создать такой экземпляр. Для этого используется метод Connection.createStatement(). В коде это выглядит таким образом:
try {
statement =connection.createStatement();
} catch (SQLException e) {
e.printStackTrace();
} finally {
/*Do some job...*/
}
После этого мы можем использовать наш экземпляр statement для выполнения SQL – запросов.
Для этой цели интерфейс Statement имеет три метода, которые реализуются каждой конкретной реализацией JDBC драйвера:
- boolean execute (String SQL)
Этот метод возвращает логическое значение true, если объект ResultSet может быть получен. В противном случае он возвращает false. Он используется для выполнения DDL SQL – запросов ил в случаях, когда мы используем динамический SQL. - int executeUpdate (String SQL)
Этот метода возвращает количество столбцов в таблице, на которое повлиял наш SQL – запрос. Мы используем этот метод для выполнения SQL – запросов, когда хотим получить количество задействованных столбцов, например количество данных по определённому запросу. - ResultSet executeQuery (String SQL)
Этот метод возвращает нам экземпляр ResultSet. Мы используем этот метод в случаях, когда мы рассчитываем получить множество объектов в результате выполнения нашего SQL – запроса. Например, при получении списка элементов, которые удовлетворяют определённым условиям.
Закрытие экземпляра Statement
Когда мы закрываем наше соединение (Connection) для сохранения результатов в БД мы таким же образом закрываем и экземпляр Statement.
Для этого мы используем метод close().
Рассмотрим, как это выглядит в нашем коде:
Connection connection = null;
Statement statement = null;
Class.forName(JDBC_DRIVER);
connection = DriverManager.getConnection(DATABASE_URL, USER, PASSWORD);
try {
statement = connection.createStatement();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (statement != null) {
statement.close();
}
}
Для понимания того, как это работает на практике, рассмотрим пример простого приложения, в котором мы попытаемся получить данные из БД.
Пример:
import java.sql.*;
public class StatementDemo {
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DATABASE_URL = "jdbc:mysql://localhost/PROSELYTE_TUTORIALS";
static final String USER = "ИМЯ_ПОЛЬЗОВАТЕЛЯ";
static final String PASSWORD = "ВАШ_ПАРОЛЬ";
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Connection connection = null;
Statement statement = null;
System.out.println("Registering JDBC driver...");
Class.forName(JDBC_DRIVER);
System.out.println("Connecting to database...");
connection = DriverManager.getConnection(DATABASE_URL, USER, PASSWORD);
System.out.println("Creating statement...");
statement = connection.createStatement();
String sql = "SELECT * FROM developers";
Boolean isRetrieved = statement.execute(sql);
System.out.println("Is data retrieved: " + isRetrieved);
System.out.println("Displaying retrieved data:");
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String specialty = resultSet.getString("specialty");
int salary = resultSet.getInt("salary");
System.out.println("id: " + id);
System.out.println("Name: " + name);
System.out.println("Specialty: " + specialty);
System.out.println("Salary: " + salary);
System.out.println("\n===================\n");
}
System.out.println("Closing connection and releasing resources...");
try {
resultSet.close();
statement.close();
connection.close();
}finally {
if(statement !=null){
statement.close();
}
if(connection!=null){
connection.close();
}
}
System.out.println("Thank You.");
}
}
В результате работы программы мы получим следующий результат:
/*Some System Messages*/
Registering JDBC driver...
Connecting to database...
Creating statement...
Is data retrieved: true
Displaying retrieved data:
id: 1
Name: Proselyte
Specialty: Java
Salary: 2000
===================
id: 2
Name: Peter
Specialty: C++
Salary: 3000
===================
id: 3
Name: AsyaSmile
Specialty: UI/UX
Salary: 2000
===================
Closing connection and releasing resources...
Thank You.
Создание экземпляра PreparedStatement
PreparedStatement наследует интерфейс Statement, что даёт нам определённые преимущества перед обычным Statement. В частности, мы получаем большую гибкость при динамической поддержке аргументов.
Вот как выглядит создание экземпляра PreparedStatement на практике:
try {
String SQL = "Update developers SET salary WHERE specialty = ?";
preparedStatement = connection.prepareStatement(SQL);
}catch (SQLException e){
e.printStackTrace();
}finally {
/*do some job...*/
}
Все параметры, которые отмечены символом ? называются маркерами параметра. Это означает, что они должны быть переданы через параметры метода.
Каждый параметр ссылается на свой порядковый номер в сигнатуре метода. Т.е. первый маркер находится на первом месте, второй – на втором и т.д. В отличие от массивов, здесь отсчёт идёт с 1. Это связано с особенностями реляционной модели, на которой и основана работа реляционных БД.
Для выполнения SQL – запросов используются методы с такими же названиями (execute(), executeQuery, executeUpdate), которые несколько модифицированы.
Закрытие экземпляра PreparedStatement
Когда мы закрываем наше соединение (Connection) для сохранения результатов в БД мы таким же образом закрываем и экземпляр PreparedStatement.
Для этого мы используем метод close().
Рассмотрим, как это выглядит в нашем коде:
try {
String SQL = "Update developers SET salary WHERE specialty = ?";
preparedStatement = connection.prepareStatement(SQL);
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (preparedStatement != null) {
preparedStatement.close();
}
}
Для понимания того, как это работает на практике, рассмотрим пример простого приложения с использованием PreparedStatement.
Пример:
import java.sql.*;
public class PreparedStatementDemo {
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DATABASE_URL = "jdbc:mysql://localhost/PROSELYTE_TUTORIALS";
static final String USER = "ИМЯ_ПОЛЬЗОВАТЕЛЯ";
static final String PASSWORD = "ВАШ_ПАРОЛЬ";
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Connection connection = null;
PreparedStatement preparedStatement = null;
System.out.println("Registering JDBC driver...");
Class.forName(JDBC_DRIVER);
System.out.println("Creating connection...");
connection = DriverManager.getConnection(DATABASE_URL, USER, PASSWORD);
try {
String SQL = "SELECT * FROM developers";
preparedStatement = connection.prepareStatement(SQL);
System.out.println("Initial developers table content:");
ResultSet resultSet = preparedStatement.executeQuery(SQL);
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String specialty = resultSet.getString("specialty");
int salary = resultSet.getInt("salary");
System.out.println("id: " + id);
System.out.println("Name: " + name);
System.out.println("Specialty: " + specialty);
System.out.println("Salary: " + salary);
System.out.println("\n============================\n");
}
SQL = "Update developers SET salary=? WHERE specialty=?";
System.out.println("Creating statement...");
System.out.println("Executing SQL query...");
preparedStatement = connection.prepareStatement(SQL);
preparedStatement.setInt(1, 3000);
preparedStatement.setString(2, "java");
System.out.println("Rows impacted: " + preparedStatement.executeUpdate());
System.out.println("Final developers table content:");
SQL = "SELECT * FROM developers";
resultSet = preparedStatement.executeQuery(SQL);
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String specialty = resultSet.getString("specialty");
int salary = resultSet.getInt("salary");
System.out.println("id: " + id);
System.out.println("Name: " + name);
System.out.println("Specialty: " + specialty);
System.out.println("Salary: " + salary);
System.out.println("\n============================\n");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (preparedStatement != null) {
preparedStatement.close();
}
if(connection!=null){
connection.close();
}
}
System.out.println("Thank You.");
}
}
В результате работы программы мы получим, примерно, следующий результат:
/*Some System Messages*/
Registering JDBC driver...
Creating connection...
Initial developers table content:
id: 1
Name: Proselyte
Specialty: Java
Salary: 2000
============================
id: 2
Name: Peter
Specialty: C++
Salary: 3000
============================
id: 3
Name: AsyaSmile
Specialty: UI/UX
Salary: 2000
============================
id: 4
Name: Eugene
Specialty: Java
Salary: 2200
============================
Creating statement...
Executing SQL query...
Rows impacted: 2
Final developers table content:
id: 1
Name: Proselyte
Specialty: Java
Salary: 3000
============================
id: 2
Name: Peter
Specialty: C++
Salary: 3000
============================
id: 3
Name: AsyaSmile
Specialty: UI/UX
Salary: 2000
============================
id: 4
Name: Eugene
Specialty: Java
Salary: 3000
============================
Thank You.
В этом приложении мы сначала получаем и выводим в консоль список всех записей из таблицы developer, после чего устанавливаем salary = 3000 для всех разработчиков, у которых specialty = “Java”.
Создание экземпляра CallableStatement
Экземпляр CallableStatement используется для выполнения процедур, непосредственно в самой БД.
Рассмотрим пример, в котором нам необходимо выполнить такую процедуру в MySQL:
DELIMITER $
DROP PROCEDURE IF EXISTS `developers`.`getDeveloperName` $
CREATE PROCEDURE PROSELYTE_TUTORIALS.`getDeveloperName`
(IN DEVELOPER_ID INT, OUT DEVELOPER_NAME VARCHAR(50))
BEGIN
SELECT first INTO DEVELOPER_NAME
FROM developers
WHERE id = DEVELOPER_ID;
END $
DELIMITER ;
Существует три типа параметров: IN, OUT, INOUT. PreparedStatement использует только IN, а CallableStatement, в свою очередь, использует все три.
Рассмотрим, что же это за параметры:
- IN
Параметр, значение которого известно в момент, когда создаётся запрос. Мы назначаем параметр IN с помощью метода типа setXXX(). - OUT
Параметр, значение которого возвращается SQL – запросом. Мы получаем значения из OUT с помощью методов типа getXXX(). - INOUT
Параметр, который использует входные и выходные значения. Мы назначаем параметр с помощью метода типа setXXX(), а получаем значения, с помощью метода типа getXXX().
В коде, создание экземпляра CallableStatement выглядит следующим образом:
try {
String SQL = "{call getDeveloperName (?, ?)}";
callableStatement = connection.prepareCall(SQL);
}finally {
/* do some job */
}
Строка SQL представляет собой процедуру, с параметрами.
Схожим с PreparedStatement способом, мы, используя экземпляр CallableStatement, должны установить значения параметров.
Когда мы используем параметры типа OUT и INOUT, нам необходимо задействовать дополнительный метод registerOutParameter(). Этот метод устанавливает тип данных JDBC в тип данных процедуры.
После того как мы вызвали процедуру, мы получаем значение из параметра OUT с помощью соответствующего метода getXXX(). Этот метод преобразует полученное значение из типа данных SQL в тип данных Java.
Закрытие экземпляра CallableStatement
Когда мы закрываем наше соединение (Connection) для сохранения результатов в БД мы таким же образом закрываем и экземпляр Statement.
Для этого мы используем метод close().
Рассмотрим, как это выглядит в нашем коде:
try {
String SQL = "{call getDeveloperName (?, ?)}";
callableStatement = connection.prepareCall(SQL);
}finally {
if(callableStatement!=null){
callableStatement.close();
}
}
Для понимания того, как это работает на практике, рассмотрим пример простого приложения
Пример:
Создаём процедуру в нашей БД:
DELIMITER $
DROP PROCEDURE IF EXISTS PROSELYTE_TUTORIALS.`getDeveloperName` $
CREATE PROCEDURE PROSELYTE_TUTORIALS.`getDeveloperName`
(IN DEVELOPER_ID INT, OUT DEVELOPER_NAME VARCHAR(50))
BEGIN
SELECT name INTO DEVELOPER_NAME
FROM developers
WHERE id = DEVELOPER_ID;
END $
DELIMITER ;
Класс CallableStatementDemo
import java.sql.*;
public class CallableStatementDemo {
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DATABASE_URL = "jdbc:mysql://localhost/PROSELYTE_TUTORIALS";
static final String USER = "ВАШЕ_ИМЯ_ПОЛЬЗОВАТЕЛЯ";
static final String PASSWORD = "ВАШ_ПАРОЛЬ";
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Connection connection = null;
CallableStatement callableStatement = null;
System.out.println("Registering JDBC driver...");
Class.forName(JDBC_DRIVER);
System.out.println("Creating connection...");
connection = DriverManager.getConnection(DATABASE_URL, USER, PASSWORD);
System.out.println("Creating callable statement...");
try {
String SQL = "{call getDeveloperName (?, ?)}";
callableStatement = connection.prepareCall(SQL);
int developerID = 1;
callableStatement.setInt(1, developerID);
callableStatement.registerOutParameter(2, Types.VARCHAR);
System.out.println("Executing procedure...");
callableStatement.execute();
String developerName = callableStatement.getString(2);
System.out.println("Developer INFO");
System.out.println("id: " + developerID);
System.out.println("Name: " + developerName);
} finally {
if (callableStatement != null) {
callableStatement.close();
}
if (connection != null) {
connection.close();
}
}
System.out.println("Thank You.");
}
}
В результате работы программы мы получим следующий результат:
/*Some System Messages*/
Registering JDBC driver...
Creating connection...
Creating callable statement...
Executing procedure...
Developer INFO
id: 1
Name: Proselyte
Thank You.
В этом уроке мы изучили утверждения (statements) их виды и рассмотрели примеры приложений с их использованием.
В следующем уроке мы изучим такой элемент, как Result Set.