JDBC API в Java - преглед и урок

Този път бих искал да се съсредоточа върху работата с бази данни с помощта на JDBC API.

Да оставим спора наречен Hibernate (или по-скоро JPA) срещу JDBC настрана. И двата подхода имат право на съществуване. Ако искате да чуете личното ми мнение по този въпрос, погледнете края на статията.

Сега нека разгледаме следните въпроси:

DriverManager и JDBC драйвер

Във всички примери за свързване към база данни в Интернет определено ще срещнете тези редове:

Където driverClass е низ с пълното име на JDBC клас на драйвера, като org.h2.Driver за H2 база данни или com.mysql.jdbc.Driver за MySql.

Обикновено това е мястото, където историята за драйвера и DriverManager свършва. Е, нека се поразровим малко.

Всички основни обекти в JDBC API, с които ще работите, са интерфейси:

  • Връзка;
  • изявление;
  • PreparedStatement;
  • callableStatement;
  • набор от резултати;
  • шофьор;
  • DatabaseMetaData.

Специфичният за базата данни JDBC драйвер осигурява реализации на тези интерфейси.

DriverManager е сингълтън, който съдържа информация за всички регистрирани драйвери. Методът getConnection намира java.sql.Driver на съответната база данни въз основа на URL параметъра и извиква нейния метод за свързване.

Така че защо да извиквате Class.forName()?

Ако погледнете изходния код за внедряването на който и да е драйвер, той ще съдържа блок за статична инициализация като този:

Извикването на Class.forName зарежда класа и по този начин гарантира изпълнението на блока за статична инициализация, а оттам и регистрацията на драйвера в DriverManager.

Връзка с база данни

В статията предлагам да използвам лека база данни, написана вJava, наречена H2 база данни. В момента за нас предимството на използването му е, че като изтеглите буркана тук, веднага получавате както самата база данни, така и драйвера за свързване с нея, което е много удобно за изучаване.

Така получихме имплементацията на интерфейса java.sql.Connection за нашата база данни.

Пълният код за всички примери може да бъде намерен в края на статията.

Използване на Statement и ResultSet

Въз основа на връзката можете да получите обект java.sql.Statement за запитване към базата данни.

В резултат на изпълнението на този кодов фрагмент ще бъде създадена потребителска таблица с две колони id и име.

Изявлението може да се използва за изпълнение на всякакви заявки, било то DDL, DML или обикновени заявки за извличане на данни.

Обектът ResultSet е резултатът от изпълнението на заявката.

Обектите Connection, Statement и ResultSet трябва да бъдат затворени след употреба. Следователно, горният код трябва да бъде обвит в try-finally и добавено затваряне на ресурс в блока finally:

Не изглежда много красиво, нали? Затварянето на ResultSet може да бъде премахнато, защото според договора:

Обект ResultSet се затваря автоматично от обекта Statement, който го е генерирал, когато този обект Statement е затворен, повторно изпълнен или се използва за извличане на следващия резултат от поредица от множество резултати.

Но пак не е същото.

С появата на Java 1.7 ситуацията се промени малко към по-добро, тъй като беше добавена конструкцията try-with-resources, която гарантира, че всички Closeable ресурси ще бъдат затворени след изпълнение на блока try. Нашият код се превръща в следния по-елегантен фрагмент:

PreparedStatement и партидно изпълнение

Ако трябва да изпълните няколко подобни заявки, тогаваразумно решение би било да се използва PreparedStatement.

PreparedStatement е компилирана версия на SQL израз, който ще бъде по-бърз и по-ефективен при изпълнение.

PreparedStatement поддържа пакетно изпращане на SQL заявки, което значително намалява трафика между клиента и базата данни. Малък пример:

Моля, обърнете внимание, че е необходимо да зададете параметри в PreparedStatement чрез индекси, освен това обратното броене започва от едно. Ако има много параметри и е вероятно те да се добавят или премахват периодично, тогава можете да използвате тази опция:

Транзакции в JDBC

Тези, които са се запознали с Hibernate, заобикаляйки JDBC, обикновено са много изненадани да работят с транзакции.

По подразбиране всеки SQL оператор се ангажира автоматично при изпълнение на statement.execute и подобни методи. За да отворите транзакция, първо трябва да зададете флага autoCommit на връзката на false. Е, тогава познатите методи за ангажиране и връщане назад ще бъдат полезни за всички.

Използване на DatabaseMetaData

Използвайки Connection, можете да получите много полезен обект DatabaseMetaData. Тя ви позволява да получите мета-информация за схемата на базата данни, а именно какви обекти има в базата данни - таблици, колони, индекси, тригери, процедури и т.н.

Лично аз често използвам DatabaseMetaData, за да променя програмно схемата на базата данни, например:

След като добавите нова функционалност, можете да проверите дали съответните обекти вече са създадени и, ако е необходимо, да промените схемата.

Заключение

Разбира се, JDBC API далеч не е идеален. Например, SQLException е проверено изключение и трябва да се влачи или обвива навсякъде; работа с PreparedStatementдоста неудобно, както вече видяхме.

Но в по-голямата част от случаите използвам JDBC за моите приложения, тъй като JDBC дава максимална гъвкавост и ефективност. Може би с Hibernate ще спестите един ден, тъй като не е нужно да пишете код за създаване на схема, както и заявки за четене и писане на обекти. Но какво е един ден в сравнение с живота на приложението? Освен това практиката показва, че от време на време Hibernate поставя разработчиците пред интересни предизвикателства, чието решение може да отнеме не само време, но и нерви.

Пълният изходен код за примерите в статията може да бъде намерен тук SqlExamples.java.

Ако смятате, че съм обърнал недостатъчно внимание на някой въпрос, пишете ми - и определено ще допълня статията.