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.
Ако смятате, че съм обърнал недостатъчно внимание на някой въпрос, пишете ми - и определено ще допълня статията.