В этой статье мы расскажем о всеобъемлющем учебнике по 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, все они будут объяснены более подробно в следующих главах.
- Прежде всего, Java-приложения должны создавать и устанавливать соединение с конкретной базой данных. Это делается с помощью диспетчера драйверов, например, одного экземпляра интерфейса
java.sql.DriverManager
, или напрямую через источник данных JDBC. Для этого можно использовать интерфейсjavax.sql.DataSource
. Как уже упоминалось, мы объясним эти компоненты более подробно в следующих главах. - Когда мы подключены к базе данных, мы можем использовать
java.sql.Connection
для выполнения операторов или операций SQL (создание, чтение, обновление, удаление). Эти утверждения объяснены позже в этом уроке. - Для выполнения этих операций программисты могут использовать
java.sql.PreparedStatement
основеjava.sql.Statement
иjava.sql.PreparedStatement
. Последние более эффективны при выполнении одного и того же оператора несколько раз и предоставляют другие преимущества, которые мы перечислим в этом руководстве.
Интерфейс JDBC-соединения предоставляет механизмы для создания экземпляров операторов:
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. В этом разделе мы собираемся перечислить некоторые из них:
- HSQLDB (база данных Hyper SQL) — это система управления реляционными базами данных, которая предлагает оперативную и постоянную память. Он имеет драйвер JDBC (как показано в некоторых примерах). Он очень полезен для тестирования из-за его непостоянных функций и поддерживает почти все основные функции SQL. Для получения дополнительной информации, пожалуйста, посетите http://hsqldb.org/
- DBUnit является расширением JUnit. Это очень полезно для модульного тестирования, когда задействованы базы данных. Эта структура заботится о состоянии баз данных между тестами и выделяет несколько свойств баз данных при тестировании. Для загрузки источников и дополнительной документации, пожалуйста, посетите http://www.dbunit.org
- DBUtils — это библиотека Apache Commons, реализованная с целью облегчить использование JDBC. Некоторые функции, которые содержит эта библиотека: очистка ресурсов, уменьшение количества кода, упрощение и автоматическое заполнение наборов результатов. Эта библиотека небольшая, прозрачная и быстрая и должна использоваться разработчиками, которые хотят работать непосредственно с JDBC. Java 1.6 или выше необходима для использования этой библиотеки. Для получения дополнительной документации http://commons.apache.org/proper/commons-dbutils/
- Spring Data также содержит модуль, связанный с JDBC. Это называется Spring Data JDBC Extensions. Он предлагает поддержку наиболее часто используемых функций JDBC. Он предлагает специальные функции для работы с базами данных Oracle. Если вы хотите узнать больше об этой библиотеке, пожалуйста, посетите http://projects.spring.io/spring-data-jdbc-ext/
- JOOQ — очень интересный фреймворк от компании datageekery, который использует JDBC; он генерирует код Java из баз данных SQL и предлагает API для установления соединений JDBC, запроса данных и простой обработки результатов. Для получения дополнительной информации, пожалуйста, посетите их учетную запись git hub: https://github.com/jOOQ/jOOL .
14. Модульное тестирование
Когда дело доходит до модульного тестирования и баз данных, всегда есть несколько открытых вопросов:
- Какую среду мы используем для тестирования?
- Тестируем ли мы на реальных данных?
- Или мы используем синтетические данные?
- Как мы тестируем наши базы данных без надлежащих учетных данных?
Несколько библиотек могут помочь нам с этими задачами. В этой главе мы перечислим некоторые из них и предоставим несколько полезных ссылок, где можно найти больше информации:
- DBUnit: как уже было сказано, DBUnit — это инфраструктура тестирования, которая работает в сотрудничестве с Junit. Для получения дополнительной информации http://dbunit.org
- TestNG. Эта среда тестирования охватывает множество сценариев тестирования, таких как модульное тестирование, функциональное тестирование, интеграционное тестирование и т. Д. Она основана на аннотациях. Для получения дополнительной информации об этой структуре, пожалуйста, посетите их веб-сайт: http://testng.org/doc/index.html
- JOOQ. Этот фреймворк предоставляет возможности для JDBC-моделирования и тестирования. Он очень хорошо документирован и прост в использовании. Для получения дополнительной информации, пожалуйста, посетите http://jooq.org
15. Резюме
JDBC (Java Database Connectivity) — это стандартный API-интерфейс для подключения баз данных между Java и огромным количеством баз данных и источников данных (от баз данных на основе SQL до электронных таблиц Excel). В этом уроке мы попытались объяснить архитектуру JDBC и как ее использовать; мы перечислили основные компоненты, которые использует JDBC, и перечислили некоторые драйверы для различных широко используемых баз данных, таких как MySql.
Наиболее важные моменты, которые следует помнить:
- Драйверы — это компоненты, которые позволяют приложению Java работать с базой данных. JDBC требует драйверов для каждой конкретной базы данных. Список доступных драйверов для JDBC можно найти по адресу http://www.oracle.com/technetwork/java/index-136695.html .
- Операторы SQL отправляются непосредственно на серверы баз данных каждый раз. JDBC содержит механизм, вызываемый
PreparedStatement
с предопределенным путем выполнения, который обеспечивает большую эффективность и лучшее использование ресурсов. - Наборы результатов — это представление, используемое для строк, выходящих из запроса.
- Хранимые процедуры — это наборы операторов SQL, сгруппированных вместе, их можно вызывать по имени без необходимости вызывать каждый из них отдельно.
- Транзакции — это группы операторов SQL. Транзакция заканчивается, когда
commit()
илиrollback()
вызываются. Эта группировка позволяет по-разному работать параллельно. - Команды CRUD являются
create
,read
,update
иdelete
команды. JDBC предоставляет механизмы для выполнения этих команд.
Учебное пособие содержит некоторую информацию о новых возможностях Java 8, связанных с JDBC, таких как JOOQ; мы также упомянули некоторые важные библиотеки, реализованные с использованием JDBC, такие как Spring-Data или Apache DBUtils.
16. Скачать исходный код учебника JDBC
17. Ссылки
Помимо всех ссылок и ресурсов, указанных в этой статье, если вы заинтересованы в том, чтобы узнать больше об API JDBC, его функциях и механизмах, лучший источник информации, который вы можете найти, — это официальный веб-сайт Oracle: