Статьи

Учебное пособие по JDBC — ULTIMATE Guide (PDF Download)

В этой статье мы расскажем о всеобъемлющем учебнике по JDBC (Java Database Connectivity), предоставляемом Oracle API, который позволяет программистам обрабатывать различные базы данных из приложений Java: он позволяет разработчикам устанавливать соединения с базами данных, определяет, как конкретный клиент может получить доступ к заданному база данных, обеспечивает механизмы для чтения, вставки, обновления и удаления записей данных в базе данных и заботится о транзакциях, состоящих из различных операторов SQL.

В этой статье мы объясним основные компоненты JDBC, такие как операторы, результирующие наборы или хранимые процедуры.

JDBC нужны драйверы для разных баз данных, с которыми программисты могут захотеть работать; мы объясним это подробно и приведем несколько примеров.

JDBC приходит вместе с Java с незапамятных времен; первый выпуск вышел с JDK 1.1 в феврале 1997 года, и с тех пор JDBC стал важной частью Java. Основные пакеты, в которых содержится JDBC: http://docs.oracle.com/javase/8/docs/api/java/sql/package-summary.html и http://docs.oracle.com/javase/8/ docs / api / javax / sql / package-summary.html .

Всю информацию о последнем выпуске JDBC (4.2), его разработке и обслуживании можно найти в JSR 221 .

Все примеры, показанные в этой статье, были реализованы с использованием Java 8 update 0_25 и Eclipse SDK версии Luna 4.4. В конце статьи вы можете скачать все эти примеры и многое другое!

1. Компоненты

JDBC API позволяет программистам и приложениям Java взаимодействовать с базами данных. Он поддерживает выполнение различных операторов SQL и обработку результатов, поступающих из разных источников данных.

В этом разделе мы попытаемся обобщить и перечислить наиболее важные компоненты JDBC, которые являются частью каждого приложения Java, все они будут объяснены более подробно в следующих главах.

1
2
3
PreparedStatement countriesStatement = connection.prepareStatement("UPDATE COUNTRIES SET NAME = ? WHERE ID = ?");
countriesStatement.setString(1, "Spain");
countriesStatement.setInt(2, 123456789);
  • Такие операции, как Вставка, обновление или удаление, возвращают количество измененных строк и ничего больше:
1
2
// countriesStatement belongs to the class Statement, returning number of updated rows
int n = countriesStatement.executeUpdate();
  • Операции выбора (запросы) возвращают результаты в виде строк внутри java.sql.ResultSet . Строки извлекаются по имени или номеру; Метаданные результатов также доступны:
1
2
3
4
// countriesStatement belongs to the class Statement
ResultSet rs = countriesStatement.executeQuery("SELECT NAME, POPULATION FROM COUNTRIES");
//rs contains the results in rows plus some metadata
...
  • Обычно JDBC использует пулы соединений для управления соединениями. Существуют различные реализации для пулов соединений, таких как C3P0 или DBCP. Это группы соединений JDBC, которые используются или заимствованы из приложений при необходимости и освобождаются после завершения задачи. Существует много документации о том, как использовать и настраивать пулы соединений в JDBC, хороший учебник можно найти по следующей ссылке http://docs.oracle.com/cd/E13222_01/wls/docs81/ConsoleHelp/jdbc_connection_pools.html. ,
  • При работе с JDBC доступны и другие функции: хранимые процедуры, вызываемые операторы, пакетная обработка … все это будет описано в этом руководстве.

2. Соединения

Для подключения к базе данных нам нужно использовать объект java.sql.Connection . Мы можем сделать это с помощью метода getConnection() класса java.sql.DriverManager . Этот метод получает хост базы данных и учетные данные в качестве параметров.

Этот фрагмент показывает, как создать соединение для локальной базы данных MySQL.

1
2
3
4
5
//MySQL driver is loaded
Class.forName( "com.mysql.jdbc.Driver" );
//Connection object is created using the db host and credentials
Connection connect = DriverManager.getConnection("jdbc:mysql://localhost/countries?"
            + "user=root&password=root" );

Объект подключения позволяет программистам выполнять следующие действия:

  • Создание операторов JDBC. С помощью объекта подключения можно создавать экземпляры Statement , PreparedStatement или CallableStatement которые предлагают методы для выполнения различных операторов SQL. Вот пример создания PreparedStatement :
1
2
//the connection conn is used to create a prepared statement with the given sql operation
PreparedStatement updateStmt = conn.prepareStatement( sql );

Этот оператор может выполнить обновление sql, переданное в качестве параметра.

  • Предоставляет возможность зафиксировать или откатить данную транзакцию. Соединение JDBC поддерживает два разных способа работы: autocommit=true и autocommit=false . Первый фиксирует все транзакции непосредственно в базе данных, второй требуется специальная команда для фиксации или отката транзакций. Мы увидим это более подробно в соответствующей главе этого урока. Следующий фрагмент кода показывает, как изменить режим автоматической фиксации соединения JDBC:
1
2
//it changes the mode to auto commit=false
connect.setAutoCommit( false );
  • Возможность получить метаинформацию об используемой базе данных.
  • Другие параметры, такие как пакетная обработка, хранимые процедуры и т. Д.

Мы подробно расскажем обо всех этих функциях, на данный момент полезно знать, что такое JDBC-соединение и что можно сделать с помощью JDBC-соединений.

3. Типы данных

JDBC преобразует типы данных Java в надлежащие типы JDBC, прежде чем использовать их в базе данных. Существует стандартное отображение между типами данных Java и JDBC, которое обеспечивает согласованность между реализациями базы данных и драйверами.

Следующая таблица содержит эти сопоставления:

SQL JDBC / Java сеттер добытчик
VARCHAR java.lang.String SetString GetString
CHAR java.lang.String SetString GetString
LONGVARCHAR java.lang.String SetString GetString
НЕМНОГО логический setBoolean getBoolean
NUMERIC BigDecimal setBigDecimal getBigDecimal
TINYINT байт setByte GetByte
SMALLINT короткая setShort getShort
INTEGER ИНТ SetInt GetInt
BIGINT долго setLong getLong
РЕАЛЬНЫЙ поплавок SetFloat GetFloat
FLOAT поплавок SetFloat GetFloat
DOUBLE двойной setDouble getDouble
VARBINARY байт [] setBytes GetBytes
BINARY байт [] setBytes GetBytes
ДАТА java.sql.Date SETDATE GetDate
ВРЕМЯ java.sql.Time установить время GetTime
TIMESTAMP java.sql.Timestamp setTimestamp getTimestamp
CLOB java.sql.Clob setClob getClob
большой двоичный объект java.sql.Blob setBlob getBlob
ARRAY java.sql.Array setARRAY GetArray
REF java.sql.Ref SetRef getRef
STRUCT java.sql.Struct SetStruct getStruct

Нулевые значения обрабатываются по-разному в SQL и в Java. При работе с нулевыми значениями SQL в Java полезно следовать некоторым рекомендациям, таким как избегать использования примитивных типов, поскольку они не могут быть нулевыми, но преобразованы в значения по умолчанию, такие как 0 для int, false для логических значений и т. Д.

Вместо этого рекомендуется использовать классы-обертки для примитивных типов. Класс ResultSet содержит метод wasNull() который очень полезен в этих сценариях. Вот пример его использования:

1
2
3
4
5
6
7
8
Statement stmt = conn.createStatement( );
String sql = "SELECT NAME, POPULATION FROM COUNTRIES";
ResultSet rs = stmt.executeQuery(sql);
 
int id = rs.getInt(1);
if( rs.wasNull( ) ) {
   id = 0;
}

4. Водители

Диспетчер драйверов JDBC, java.sql.DriverManager , является одним из наиболее важных элементов API JDBC. Это базовый сервис для обработки списка драйверов JDBC. Он содержит механизмы и объекты, которые позволяют приложениям Java подключаться к нужному драйверу JDBC. Он отвечает за управление различными типами драйверов баз данных JDBC. Резюмируя, основная задача диспетчера драйверов — знать список доступных драйверов и обрабатывать соединение между конкретным выбранным драйвером и базой данных.

Наиболее часто используемый метод этого класса — DriverManager.getConnetion() . Этот метод устанавливает соединение с базой данных.

Вот пример его использования:

1
2
// Create the connection with the default credentials
java.sql.Connection conn = DriverManager.getConnection("jdbc:hsqldb:mem:mydb", "SA", "" );

Мы можем зарегистрировать драйверы, используя метод DriverManager.registerDriver(). :

1
2
new org.hsqldb.jdbc.JDBCDriver();
DriverManager.registerDriver( new org.hsqldb.jdbc.JDBCDriver() );

Мы также можем загрузить драйвер, вызвав метод Class.forName() :

1
2
3
4
5
6
7
// Loading the HSQLDB JDBC driver
Class.forName( "org.hsqldb.jdbc.JDBCDriver" );
 
...
 
// connection to JDBC using mysql driver
Class.forName( "com.mysql.jdbc.Driver" );

Основное отличие состоит в том, что метод registerDriver() требует, чтобы драйвер был доступен во время компиляции, а загрузка класса драйвера не требует, чтобы драйвер был доступен во время компиляции. После JDBC 4 нет реальной необходимости вызывать эти методы, и приложениям не нужно регистрировать драйверы по отдельности, ни загружать классы драйверов. Также не рекомендуется регистрировать драйверы вручную, используя метод registerDriver() .

Другими интересными методами класса DriverManager являются getDriver(String url) , который пытается найти драйвер по заданной строке, и getDrivers() который возвращает перечисление всех драйверов, которые были ранее зарегистрированы в диспетчере драйверов:

1
2
3
4
5
6
Enumeration drivers = DriverManager.getDrivers();
while( drivers.hasMoreElements() )
{
    Driver driver = drivers.nextElement();
    System.out.println( driver.getClass() );
}

5. Базы данных

JDBC поддерживает большой список баз данных. Он излагает свои различия и способы работы с использованием разных драйверов. Класс DriverManager отвечает за загрузку соответствующей базы данных, после загрузки код, который обращается к базе данных для запроса и изменения данных, останется (более или менее) неизменным.

Вот список поддерживаемых баз данных в JDBC (официально зарегистрированный в Oracle): http://www.oracle.com/technetwork/java/index-136695.html .

В этой главе мы собираемся показать, как использовать разные базы данных: MySQL и HSQLDB. Первый широко известен программистам и широко используется, второй — HSQLDB — это база данных, очень полезная для целей тестирования, которая предлагает возможности памяти. Мы увидим, как использовать оба, и мы обнаружим, что кроме загрузки соответствующего драйвера JDBC, остальная часть приложения остается неизменной:

Пример MySQL:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public static void main( String[] args ) throws ClassNotFoundException, SQLException
    {
 
        // connection to JDBC using mysql driver
        Class.forName( "com.mysql.jdbc.Driver" );
        Connection connect = DriverManager.getConnection("jdbc:mysql://localhost/countries?"
            + "user=root&password=root" );
 
        
        selectAll( connect );
 
        // close resources, in case of exception resources are not properly cleared
...
 
    }
     
    /**
     * select statement and print out results in a JDBC result set
     *
     * @param conn
     * @throws SQLException
     */
    private static void selectAll( java.sql.Connection conn ) throws SQLException
    {
        Statement statement = conn.createStatement();
 
        ResultSet resultSet = statement.executeQuery( "select * from COUNTRIES" );
 
        while( resultSet.next() )
        {
            String name = resultSet.getString( "NAME" );
            String population = resultSet.getString( "POPULATION" );
 
            System.out.println( "NAME: " + name );
            System.out.println( "POPULATION: " + population );
        }
 
    }

В памяти (HSQLDB) пример:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public static void main( String[] args ) throws ClassNotFoundException, SQLException
    {
 
        // Loading the HSQLDB JDBC driver
        Class.forName( "org.hsqldb.jdbc.JDBCDriver" );
 
        // Create the connection with the default credentials
        java.sql.Connection conn = DriverManager.getConnection( "jdbc:hsqldb:mem:mydb", "SA", "" );
 
        // Create a table in memory
        String countriesTableSQL = "create memory table COUNTRIES (NAME varchar(256) not null primary key, POPULATION varchar(256) not null);";
 
        // execute the statement using JDBC normal Statements
        Statement st = conn.createStatement();
        st.execute( countriesTableSQL );
 
        // nothing is in the database because it is just in memory, non persistent
        selectAll( conn );
 
        // after some insertions, the select shows something different, in the next execution these
        // entries will not be there
        insertRows( conn );
        selectAll( conn );
 
    }
 
...
 
    /**
     * select statement and print out results in a JDBC result set
     *
     * @param conn
     * @throws SQLException
     */
    private static void selectAll( java.sql.Connection conn ) throws SQLException
    {
        Statement statement = conn.createStatement();
 
        ResultSet resultSet = statement.executeQuery( "select * from COUNTRIES" );
 
        while( resultSet.next() )
        {
            String name = resultSet.getString( "NAME" );
            String population = resultSet.getString( "POPULATION" );
 
            System.out.println( "NAME: " + name );
            System.out.println( "POPULATION: " + population );
        }
 
    }

Как мы видим в прошлых программах, код методов selectAll полностью одинаков, меняется только загрузка драйвера JDBC и создание соединения; Вы можете себе представить, насколько это эффективно при работе в разных средах. HSQLDB-версия кода также содержит фрагмент кода, отвечающий за создание базы данных в памяти и вставку некоторых строк, но это только для целей наглядности и ясности и может быть сделано по-другому.

6. Наборы результатов

Класс java.sql.ResultSet представляет набор результатов таблицы базы данных. Он создан, как правило; выполнив запрос SQL (выберите оператор с помощью Statement или PreparedStatement). Содержит строки данных, в которых хранятся данные. Эти данные могут быть доступны по индексу (начиная с 1) или по имени атрибута:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// creating the result set
ResultSet resultSet = statement.executeQuery( "select * from COUNTRIES" );
 
// iterating through the results rows
 
while( resultSet.next() )
{
    // accessing column values by index or name
    String name = resultSet.getString( "NAME" );
    int population = resultSet.getInt( "POPULATION" );
 
    System.out.println( "NAME: " + name );
    System.out.println( "POPULATION: " + population );
 
 
    // accessing column values by index or name
    String name = resultSet.getString( 1 );
    int population = resultSet.getInt( 2 );
 
    System.out.println( "NAME: " + name );
    System.out.println( "POPULATION: " + population );
 
 
}

Как показано ранее, ResultSets содержит методы получения для получения значений столбцов для различных типов Java. Он также содержит курсор, указывающий на текущую строку данных. Первоначально курсор указывает на первую строку. Следующий метод перемещает курсор в следующую строку: java.sql.ResultSet.next() .

Можно создать ResultSets со свойствами по умолчанию, такими как курсор, который перемещается только вперед и не подлежит обновлению. Если программисты хотели бы использовать другие виды свойств, он может указать это при создании Statement, который будет генерировать результирующие наборы путем изменения передаваемых аргументов:

1
2
3
4
5
6
/**
* indicating result sets properties that will be created from this statement: type,
* concunrrency and holdability
*/
Statement statement = conn.createStatement( ResultSet. TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE, ResultSet.CLOSE_CURSORS_AT_COMMIT );

Используя этот тип наборов результатов, можно перемещать курсор в обоих направлениях и обновлять или вставлять новые данные в базу данных, используя набор результатов для этой цели.

7. Хранимые процедуры

В этой главе мы собираемся объяснить, что такое хранимые процедуры и как мы можем использовать их в JDBC. Для примеров мы будем использовать хранимые процедуры на основе MySQL.

Хранимые процедуры — это наборы операторов SQL как части логической единицы выполнения и выполнения определенной задачи. Они очень полезны при инкапсуляции группы операций, выполняемых в базе данных.

Прежде всего мы собираемся создать процедуру в нашей базе данных MySQL, следующий скрипт поможет нам в этой задаче:

01
02
03
04
05
06
07
08
09
10
11
12
delimiter //
  
CREATE PROCEDURE spanish (OUT population_out INT)
 BEGIN
 SELECT COUNT(*) INTO population_out FROM countries;
 END//
  
  
 delimiter ;
  
 CALL simpleproc(@a);

По сути, приведенный выше скрипт создает процедуру с именем Spanish с одним выходным атрибутом типа int и без входных параметров. Процедура возвращает количество всех стран в базе данных.

Как только мы создали процедуру, мы можем работать с ней из наших Java-приложений. Чтобы вызвать хранимые процедуры, нам нужно использовать специальные операторы интерфейса java.sql.CallableStatement , эти операторы позволяют программистам выполнять хранимые процедуры с указанием выходных атрибутов и входные параметры, которые будут использоваться. В нашем простом примере настраиваются только выходные атрибуты. Вот пример:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
CallableStatement callableStatement = null;
 
// the procedure should be created in the database
String spanishProcedure = "{call spanish(?)}";
 
// callable statement is used
callableStatement = connect.prepareCall( spanishProcedure );
 
// out parameters, also in parameters are possible, not in this case
callableStatement.registerOutParameter( 1, java.sql.Types.VARCHAR );
 
// execute using the callable statement method executeUpdate
callableStatement.executeUpdate();
 
// attributes are retrieved by index
String total = callableStatement.getString( 1 );
 
System.out.println( "amount of spanish countries " + total );

Мы можем оценить, как указать, где хранить выходные данные процедуры и как ее выполнить, используя метод java.sql.PreparedStatement.executeUpdate() . Хранимые процедуры поддерживаются в большинстве баз данных, но их синтаксис и поведение могут отличаться, поэтому могут быть различия в приложениях Java, обрабатывающих хранимые процедуры, в зависимости от баз данных, в которых хранятся процедуры.

8. Заявления

Как уже упоминалось в этом руководстве, JDBC использует интерфейс java.sql.Statement для выполнения различных запросов и операций SQL, таких как вставка, обновление или удаление. Это базовый интерфейс, который содержит все основные методы, такие как java.sql.Statement.executeQuery(String) или java.sql.Statement.executeUpdate(String) .

Реализации этого интерфейса рекомендуются, когда программистам не нужно выполнять один и тот же запрос несколько раз или когда запросы и операторы не нуждаются в параметризации. В целом можно сказать, что этот интерфейс подходит для выполнения операторов DDL (Create, Alter, Drop). Эти операторы обычно не выполняются несколько раз и не требуют поддержки различных параметров.

Если программистам нужна более высокая эффективность при повторении запросов или параметризации SQL, они должны использовать java.sql.PreparedStatement . Этот интерфейс наследует упомянутый ранее базовый интерфейс оператора и предлагает параметризацию. Благодаря этой функциональности этот интерфейс более безопасен против атак с использованием SQL-инъекций. Вот фрагмент кода, показывающий пример этого интерфейса:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
System.out.println( "Updating rows for " + name + "..." );
 
String sql = "UPDATE COUNTRIES SET POPULATION=? WHERE NAME=?";
 
PreparedStatement updateStmt = conn.prepareStatement( sql );
 
// Bind values into the parameters.
updateStmt.setInt( 1, 10000000 ); // population
updateStmt.setString( 2, name ); // name
 
// update prepared statement using executeUpdate
int numberRows = updateStmt.executeUpdate();
 
System.out.println( numberRows + " rows updated..." );

Еще одним преимуществом использования подготовленных операторов является возможность обрабатывать нестандартные объекты с помощью setObject() . Вот пример:

01
02
03
04
05
06
07
08
09
10
11
12
PreparedStatement updateStmt2 = conn.prepareStatement( sql );
 
// Bind values into the parameters using setObject, can be used for any kind and type of
// parameter.
updateStmt2.setObject( 1, 10000000 ); // population
updateStmt2.setObject( 2, name ); // name
 
// update prepared statement using executeUpdate
numberRows = updateStmt2.executeUpdate();
 
System.out.println( numberRows + " rows updated..." );
updateStmt2.close();

Как упоминалось в главе, относящейся к хранимым процедурам, для этой цели доступен еще один интерфейс, он называется java.sql.CallableStatement и расширяет java.sql.CallableStatement PreparedStatement.

9. Пакетные команды

JDBC предлагает возможность выполнять список операторов SQL в виде пакета, то есть все подряд. В зависимости от того, какой тип утверждений используют программисты, код может отличаться, но общая идея одна и та же. В следующем фрагменте показано, как использовать пакетную обработку с java.sql.Statement :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
Statement statement = null;
 
statement = connect.createStatement();
 
// adding batchs to the statement
statement.addBatch( "update COUNTRIES set POPULATION=9000000 where NAME='USA'" );
statement.addBatch( "update COUNTRIES set POPULATION=9000000 where NAME='GERMANY'" );
statement.addBatch( "update COUNTRIES set POPULATION=9000000 where NAME='ARGENTINA'" );
 
// usage of the executeBatch method
int[] recordsUpdated = statement.executeBatch();
 
int total = 0;
for( int recordUpdated : recordsUpdated )
{
    total += recordUpdated;
}
 
System.out.println( "total records updated by batch " + total );

И используя java.sql.PreparedStatement :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
String sql = "update COUNTRIES set POPULATION=? where NAME=?";
 
 PreparedStatement preparedStatement = null;
 
 preparedStatement = connect.prepareStatement( sql );
 
 preparedStatement.setObject( 1, 1000000 );
 preparedStatement.setObject( 2, "SPAIN" );
 
 // adding batches
 preparedStatement.addBatch();
 
 preparedStatement.setObject( 1, 1000000 );
 preparedStatement.setObject( 2, "USA" );
 
 // adding batches
 preparedStatement.addBatch();
 
 // executing all batchs
 int[] updatedRecords = preparedStatement.executeBatch();
 int total = 0;
 for( int recordUpdated : updatedRecords )
 {
     total += recordUpdated;
 }
 
 System.out.println( "total records updated by batch " + total );

Мы можем видеть, что различия в основном в том, как используются параметры SQL-запросов и как строятся запросы, но идея выполнения нескольких операторов в одной строке одинакова. В первом случае с помощью метода java.sql.Statement.executeBatch() , с использованием java.sql.PreparedStatement.addBatch() и java.sql.Statement.executeBatch() во втором.

10. Транзакции

JDBC поддерживает транзакции и содержит методы и функциональные возможности для реализации приложений на основе транзакций. Мы собираемся перечислить наиболее важные из них в этой главе.

  • java.sql.Connection.setAutoCommit(boolean) : этот метод получает логический параметр в качестве параметра; в случае true (поведение по умолчанию) все операторы SQL будут автоматически сохраняться в базе данных. В случае ложного изменения не будут сохранены автоматически, это будет сделано с помощью метода java.sql.Connection.commit() .
  • java.sql.Connection.commit() . Этот метод можно использовать только в том случае, если для автоматического принятия установлено значение false или отключено; то есть он работает только в неавтоматическом режиме фиксации. При выполнении этого метода все изменения с момента последнего коммита / отката будут сохранены в базе данных.
  • java.sql.Connection.rollback() . Этот метод можно использовать только когда автоматическая фиксация отключена. Он отменяет или отменяет все изменения, сделанные в текущей транзакции.

И вот пример использования, где мы можем увидеть, как отключить режим автоматической фиксации с помощью метода setAutoCommit(false) . Все изменения фиксируются при вызове commit() а текущие изменения транзакций откатываются с помощью метода rollback() :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Class.forName( "com.mysql.jdbc.Driver" );
Connection connect = null;
try
{
    // connection to JDBC using mysql driver
    connect = DriverManager.getConnection( "jdbc:mysql://localhost/countries?"
                + "user=root&password=root" );
    connect.setAutoCommit( false );
 
    System.out.println( "Inserting row for Japan..." );
    String sql = "INSERT INTO COUNTRIES (NAME,POPULATION) VALUES ('JAPAN', '45000000')";
 
    PreparedStatement insertStmt = connect.prepareStatement( sql );
 
    // insert statement using executeUpdate
    insertStmt.executeUpdate( sql );
    connect.rollback();
 
    System.out.println( "Updating row for Japan..." );
    // update statement using executeUpdate -> will cause an error, update will not be
    // executed becaues the row does not exist
    sql = "UPDATE COUNTRIES SET POPULATION='1000000' WHERE NAME='JAPAN'";
    PreparedStatement updateStmt = connect.prepareStatement( sql );
 
    updateStmt.executeUpdate( sql );
    connect.commit();
 
}
catch( SQLException ex )
{
    ex.printStackTrace();
    //undoes all changes in current transaction
    connect.rollback();
}
finally
{
    connect.close();
}

11. CRUD команды

CRUD происходит от создания, чтения, обновления и удаления. JDBC поддерживает все эти операции и команды, в этой главе мы покажем различные фрагменты кода Java, выполняющие все из них:

Создать заявление. С помощью JDBC можно создавать базы данных, вот пример создания базы данных в памяти:

1
2
3
4
5
6
// Create a table in memory
String countriesTableSQL = "create memory table COUNTRIES (NAME varchar(256) not null primary key, POPULATION varchar(256) not null);";
 
// execute the statement using JDBC normal Statements
Statement st = conn.createStatement();
st.execute( countriesTableSQL );

Вставить заявление. Вставки поддерживаются в JDBC. Программисты могут использовать обычный синтаксис SQL и передавать их различным классам операторов, которые предлагает JDBC, например, Statement , PreparedStatement или CallableStatement . Вот пара примеров:

01
02
03
04
05
06
07
08
09
10
Statement insertStmt = conn.createStatement();
 
String sql = "INSERT INTO COUNTRIES (NAME,POPULATION) VALUES ('SPAIN', '45Mill')";
insertStmt.executeUpdate( sql );
 
sql = "INSERT INTO COUNTRIES (NAME,POPULATION) VALUES ('USA', '200Mill')";
insertStmt.executeUpdate( sql );
 
sql = "INSERT INTO COUNTRIES (NAME,POPULATION) VALUES ('GERMANY', '90Mill')";
insertStmt.executeUpdate( sql );

Эти операторы возвращают количество вставленных строк. То же самое применимо к операторам обновления, вот пример того, как обновить набор строк в базе данных:

1
2
3
4
5
6
7
8
9
System.out.println( "Updating rows for " + name + "..." );
 
 Statement updateStmt = conn.createStatement();
 
 // update statement using executeUpdate
 String sql = "UPDATE COUNTRIES SET POPULATION='10000000' WHERE NAME='" + name + "'";
 int numberRows = updateStmt.executeUpdate( sql );
 
 System.out.println( numberRows + " rows updated..." );

Выход будет:

1
2
Updating rows for SPAIN...
4 rows updated...

Выберите Заявление. Можно выполнить любой (почти) вид SQL-запроса, используя операторы JDBC. Вот очень простой пример, который читает все строки данной таблицы и выводит их на стандартную консоль:

01
02
03
04
05
06
07
08
09
10
11
Statement statement = conn.createStatement();
 
ResultSet resultSet = statement.executeQuery( "select * from COUNTRIES" );
 
while( resultSet.next() )
{
    String name = resultSet.getString( "NAME" );
        String population = resultSet.getString( "POPULATION" );
    System.out.println( "NAME: " + name );
        System.out.println( "POPULATION: " + population );
}

Вывод этого будет (в зависимости от состояния базы данных):

1
2
3
4
5
6
NAME: GERMANY
POPULATION: 90Mill
NAME: SPAIN
POPULATION: 45Mill
NAME: USA
POPULATION: 200Mill

Удалить заявление. Наконец, JDBC поддерживает удаление строк и удаление таблиц и других элементов SQL. Вот фрагмент, показывающий удаление всех строк с определенными критериями (в этом случае имя должно быть «ЯПОНИЯ»):

1
2
3
4
5
6
7
8
System.out.println( "Deleting rows for JAPAN..." );
String sql = "DELETE FROM COUNTRIES WHERE NAME='JAPAN'";
PreparedStatement deleteStmt = connect.prepareStatement( sql );
 
// delete statement using executeUpdate
int numberRows = deleteStmt.executeUpdate( sql );
 
System.out.println( numberRows + " rows deleted..." );

Операторы Delete возвращают количество затронутых строк, в этом случае вывод будет (в зависимости от состояния базы данных):

1
2
Deleting rows for JAPAN...
0 rows deleted...

Эти примеры очень простые; они были написаны для целей обучения, но вы можете представить, что можете выполнять более сложные запросы SQL, просто изменив аргумент, передаваемый методам executeQuery() или executeUpdate() .

12. Java 8

Java 8 не содержит каких-либо серьезных изменений, связанных с JDBC или структурой JDBC. Но некоторые функции Java 8 могут быть применены при работе с JDBC с очень хорошими результатами. Мы собираемся показать некоторые из них. Например, можно выполнять запросы на выборку совершенно по-другому, как мы привыкли. Вот пример того, как мы делаем это без функций Java 8, он более или менее такой же, как и во всех наших примерах в этой статье:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// we always need to write this code
System.out.println( "using Java 7" );
// connection to JDBC using mysql driver
Class.forName( "com.mysql.jdbc.Driver" );
Connection connect = DriverManager.getConnection( "jdbc:mysql://localhost/countries?"
            + "user=root&password=root" );
 
// select query
PreparedStatement statement = connect.prepareStatement( "select * from COUNTRIES" );
ResultSet resultSet = statement.executeQuery();
 
// iterating results
while( resultSet.next() )
{
    // access via name
       Object name = resultSet.getObject( 1 );
       Object population = resultSet.getObject( 2 );
 
       System.out.println( "Name: " + name );
       System.out.println( "Population: " + population );
}
 
// close resources, in case of exception resources are not properly cleared
resultSet.close();
statement.close();
connect.close();

И вот версия, которая делает то же самое, но с использованием Lambdas.

1
2
3
4
5
6
// select method is called and lambda expression is provided, this expression will be used
// in the handle method of the functional interface
select( connect, "select * from COUNTRIES", ( resultSet ) -> {
    System.out.println( resultSet.getObject( 1 ) );
       System.out.println( resultSet.getObject( 2 ) );
} );

Приведенный выше фрагмент кода содержит вызов метода select, где первый параметр — это объект Connection, второй параметр — это запрос SQL, а третий — выражение Lambda. Это лямбда-выражение получает один параметр (экземпляр ResultSet ) и распечатывает его первые два атрибута, но с этим набором результатов можно сделать что угодно в теле лямбда-выражения. Вот реализация метода select() :

01
02
03
04
05
06
07
08
09
10
11
12
13
public static void select( Connection connect, String sql, ResultSetHandler handler ) throws SQLException
    {
        PreparedStatement statement = connect.prepareStatement( sql );
 
        try (ResultSet rs = statement.executeQuery())
        {
            while( rs.next() )
            {
                handler.handle( rs );
            }
 
        }
    }

И функциональный интерфейс ResultSetHandler :

01
02
03
04
05
06
07
08
09
10
11
12
13
@FunctionalInterface
public interface ResultSetHandler
{
 
    /**
     * This method will be executed by the lambda expression
     *
     * @param resultSet
     * @throws SQLException
     */
    public void handle( ResultSet resultSet ) throws SQLException;
 
}

Здесь мы видим, что код более четкий и значительно сокращен (или нет) при использовании некоторых новых функций Java 8.

13. Библиотеки SQL построены на основе JDBC

JDBC используется несколькими известными библиотеками Java для создания своих API. В этом разделе мы собираемся перечислить некоторые из них:

14. Модульное тестирование

Когда дело доходит до модульного тестирования и баз данных, всегда есть несколько открытых вопросов:

  • Какую среду мы используем для тестирования?
  • Тестируем ли мы на реальных данных?
  • Или мы используем синтетические данные?
  • Как мы тестируем наши базы данных без надлежащих учетных данных?

Несколько библиотек могут помочь нам с этими задачами. В этой главе мы перечислим некоторые из них и предоставим несколько полезных ссылок, где можно найти больше информации:

15. Резюме

JDBC (Java Database Connectivity) — это стандартный API-интерфейс для подключения баз данных между Java и огромным количеством баз данных и источников данных (от баз данных на основе SQL до электронных таблиц Excel). В этом уроке мы попытались объяснить архитектуру JDBC и как ее использовать; мы перечислили основные компоненты, которые использует JDBC, и перечислили некоторые драйверы для различных широко используемых баз данных, таких как MySql.

Наиболее важные моменты, которые следует помнить:

Учебное пособие содержит некоторую информацию о новых возможностях Java 8, связанных с JDBC, таких как JOOQ; мы также упомянули некоторые важные библиотеки, реализованные с использованием JDBC, такие как Spring-Data или Apache DBUtils.

16. Скачать исходный код учебника JDBC

17. Ссылки

Помимо всех ссылок и ресурсов, указанных в этой статье, если вы заинтересованы в том, чтобы узнать больше об API JDBC, его функциях и механизмах, лучший источник информации, который вы можете найти, — это официальный веб-сайт Oracle: