п'ять секретів
Освіжіть свої взаємини з API JDBC
JDBC, або Java ™ Database Connectivity, - один з найбільш популярних пакетів у всьому JDK, і, тим не менше, не всі Java-розробники використовують все - або всі найсучасніші - його можливості. Тед Ньюард знайомить Новомосковсктелей з новими функціями JDBC, такими як набори ResultSet. які автоматично забезпечують прокрутку і динамічне оновлення даних, Rowset. які можуть працювати як з відкритим з'єднанням з базою даних, так і без нього, і пакетні поновлення - можливість виконувати кілька SQL-операторів при одній короткій передачі по мережі.
Тед Ньюворд. директор, Neward Associates
Про це циклі статей
Ви думаєте, що знаєте про Java-програмуванні все? Насправді більшість розробників тільки шкребе по поверхні платформи Java, вивчивши її лише настільки, щоб виконувати свою роботу. У цьому циклі статей Тед Ньюард поглиблюється в функціональні можливості платформи Java, виявляючи маловідомі факти, які можуть допомогти у вирішенні найбільш хитромудрих завдань програмування.
Сьогодні багато Java-розробники взаємодіють з API Java Database Connectivity (JDBC) через платформу доступу до даних, таку як Hibernate або Spring. Але JDBC - це не тільки посередник в підключенні до бази даних. Чим краще ви будете знати його, тим ефективніше буде ваша робота з РСУБД.
У цьому випуску циклу П'ять секретів ... я продемонструю кілька нових можливостей, що з'явилися в період між JDBC 2.0 і JDBC 4.0. Розраховані на сучасні завдання програмування, ці функції сприяють підвищенню масштабованості додатків і продуктивності розробки - вирішуючи дві найважливіші проблеми, які стоять сьогодні перед Java-програмістами.
1. Скалярні функції
Розвинути навички по цій темі
Цей матеріал - частина knowledge path для розвитку ваших навичок. Дивись Стати Java-програмістом
Різні реалізації СУБД пропонують ту чи іншу підтримку SQL і / або додаткові можливості, покликані полегшити життя розробника. Добре відомо, наприклад, що в SQL є скалярна операція COUNT (). яка повертає число рядків, які відповідають певним критеріям SQL-фільтрації (тобто твердження WHERE). Але в інших випадках змінити значення, які повертаються SQL, важко - і спроба змусити базу даних видавати поточну дату і час може звести з розуму навіть самого терплячого (і сміливого) JDBC-розробника.
В цьому плані специфікація JDBC передбачає певну ступінь ізоляції / адаптації по відношенню до різних реалізацій СУБД за допомогою скалярних функцій. У специфікації JDBC міститься список підтримуваних операцій, які драйвери JDBC повинні розпізнавати і в міру необхідності пристосовувати до конкретної реалізації бази даних. Для бази даних, яка підтримує повернення поточної дати або часу, може виявитися достатнім лише того, що показано в лістингу 1.
Лістинг 1. Котра година?
Повний список скалярних функцій, підтримуваних API JDBC, наведено в додатку до специфікації JDBC (див. Розділ Ресурси), але конкретний драйвер або база даних можуть не підтримувати весь цей список. За допомогою об'єкта DatabaseMetaData. повертається об'єктом Connection. можна отримати список функцій, підтримуваних даною реалізацією JDBC, як показано в лістингу 2.
Лістинг 2. Що ти можеш?
Список скалярних функцій - це рядок (String) розділених комами значень, яка повертається різними методами класу DatabaseMetaData. Наприклад, при виклику getNumericFunctions () перераховуються всі числові скаляри. Застосуйте до результату метод String.split () і -вуаля! - миттєво з'явиться список equals () - testable.
2. прокручувати набори ResultSet
У JDBC дуже поширена процедура створення (або отримання існуючого) об'єкта Connection і його використання для створення об'єктів Statement. У відповідь на вираз SQL SELECT Statement повертає ResultSet. Потім ResultSet пропускають через цикл while (подібно Iterator) до тих пір, поки ResultSet не повідомив, що він порожній, причому тіло циклу витягує по одному стовпцю за раз зліва направо.
Вся ця операція настільки звична, що її майже не помічають: вона виконується машинально. Але на жаль, це зовсім не обов'язково.
прокручуваний ResultSet
Багатьом розробникам невтямки, що з роками JDBC еволюціонує, хоча все удосконалення відображені в нових номерах версій і випусках. Перше велике оновлення, JDBC 2.0, з'явилося десь за часів виходу JDK 1.2. На момент написання цієї статті JDBC поставляється в версії 4.0.
Одне з цікавих (хоча і часто ігнорованих) удосконалень JDBC 2.0 - це можливість «прокрутки» набору ResultSet. в міру необхідності його можна перегортати вперед / назад або навіть в обох напрямках відразу. Однак для цього потрібна деяка попередня інформація - у виклику JDBC в момент створення об'єкту Statement має бути сказано, що потрібно прокручуваний ResultSet.
Перевірка типу ResultSet
Якщо, не дивлячись на те, що сказано в DatabaseMetaData. ви підозрюєте, що драйвер може не підтримувати прокручуваний набори ResultSet. перевірте тип ResultSet. викликавши метод getType (). Звичайно, при такій параної можна не довіряти і значенням, що повертається getType (). Що ж, якщо навіть getType () бреше з приводу повертаються наборів ResultSet. то вони явно ховаються від вас.
Якщо драйвер JDBC підтримує прокрутку, то Statement поверне прокручуваний ResultSet. але це краще з'ясувати до того, як звертатися до нього. Про підтримку прокрутки можна дізнатися за допомогою об'єкта DatabaseMetaData. який можна отримати за допомогою будь-якого виразу Connection. як описано вище.
Маючи об'єкт DatabaseMetaData і викликавши getJDBCMajorVersion (). можна визначити, чи підтримує драйвер, по крайней мере, специфікацію JDBC 2.0. Звичайно, драйвер може збрехати про свій рівень підтримки даної специфікації, тому для більшої безпеки викличте метод supportsResultSetType () з бажаним типом ResultSet. (Це константа класу ResultSet; ми ще поговоримо про всі ці значеннях.)
Лістинг 3. Чи підтримується прокрутка?
Запит прокручуваного набору ResultSet
Якщо драйвер сказав "так" (в іншому випадку необхідно замінити драйвер або базу даних), то можна запросити прокручуваний об'єкт ResultSet. передавши викликом Connection.createStatement () два параметра, як показано в лістингу 4.
Лістинг 4. Хочу прокрутку!
При виклику createStatement () потрібно бути особливо обережним, тому що і перший, і другий параметри представляють собою значення типу int. (Як шкода, що до Java 5 у нас не було перелічуваних типів!) З createStatement () буде працювати будь-яке значення int (включаючи помилкове значення константи).
Перший параметр, який вказує на бажаність «прокручіваемості» ResultSet. може приймати одне з трьох допустимих значень:
- ResultSet.TYPE_FORWARD_ONLY. це значення за замовчуванням, знайомий курсор типу пожежного рукава;
- ResultSet.TYPE_SCROLL_INSENSITIVE. такий ResultSet допускає ітерації назад і вперед, але якщо дані в базі даних зміняться, ResultSet не відобразить цього. Мабуть, цей тип прокручуваного ResultSet - найбільш затребуваний;
- ResultSet.TYPE_SCROLL_SENSITIVE. цей тип ResultSet не тільки допускає двонаправлені ітерації, але і створює «живе» уявлення даних в базі даних у міру їх зміни.
Другий параметр обговорюється в наступному раді, так що поки залишимо його.
спрямована прокрутка
Також можуть бути корисні методи relative () і absolute (). перший переміщує курсор на вказане кількість рядків (вперед, якщо значення позитивно, або назад, якщо воно негативно), а другий - до зазначеної рядку ResultSet. де б він не знаходився в даний момент. Звичайно, номер поточного рядка, можна отримати за допомогою методу getRow ().
Якщо передбачається багато прокрутки в певному напрямку, то може бути корисно вказати цей напрям шляхом виклику функції setFetchDirection (). (ResultSet буде працювати незалежно від напрямку прокрутки, але знання його наперед дозволить оптимізувати процес вилучення даних.)
3. Редаговані набори ResultSet
JDBC підтримує не тільки двосторонню прокрутку, але і редагування наборів ResultSet. Це означає, що замість того щоб створювати новий оператор SQL для зміни значень, що зберігаються в базі даних, можна просто змінити значення всередині ResultSet. і воно автоматично відіб'ється у відповідному стовпці потрібного рядка бази даних.
Дізнатися про підтримку обновляемости ResultSet можна так само, як і про підтримки прокрутки. Тут-то якраз і використовується другий параметр оператора createStatement (). Замість ResultSet.CONCUR_READ_ONLY вкажіть в якості другого параметра ResultSet.CONCUR_UPDATEABLE. як показано в лістингу 5.
Лістинг 5. Хочу оновлюваний ResultSet
Якщо драйвер підтримує оновлювані курсори (це ще одна особливість специфікації JDBC 2.0, яку підтримує більшість реальних баз даних), то будь-який заданий значення в ResultSet можна оновити, перейшовши в потрібний рядок і викликавши один з методів update. () (Як показано в лістингу 6). Як і методів get. () Класу ResultSet. існує безліч методів update. () Для стовпців ResultSet різного типу. Так, щоб змінити стовпець значень з плаваючою точкою PRICE. потрібно викликати updateFloat ( "PRICE"). Однак це призведе лише до оновлення значення в наборі ResultSet. Щоб передати його в базу даних, викличте updateRow (). Якщо користувач передумав міняти ціну, виклик cancelRowUpdates () скасує всі очікують оновлення.
Лістинг 6. Кращий спосіб
JDBC 2.0 підтримує не тільки оновлення. Щоб додати новий рядок без створення нового об'єкта Statement і виконання оператора INSERT. просто викличте метод moveToInsertRow (). метод update. () Для кожного стовпця, і, нарешті, метод insertRow (). Якщо значення для стовпця не вказано, передбачається SQL NULL (що може викликати виключення SQLException. Якщо схема бази даних не допускає значення NULL для цього стовпця).
Природно, якщо ResultSet підтримує оновлення рядка, він повинен підтримувати і її видалення за допомогою виклику методу deleteRow ().
Так, поки я не забув, вся ця прокручіваемость і редагований в рівній мірі може бути застосована і до PreparedStatement (шляхом передачі тих же параметрів методу prepareStatement ()), який набагато краще звичайного Statement з огляду на постійну небезпеку атак SQL-ін'єкції.
4. Набори Rowset
Якщо вся ця функціональність JDBC відома більшу частину десятиліття, то чому ж більшість розробників все ще сидять на прямий прокручуванні наборів ResultSet і роз'єднаному доступі?
Головна причина - масштабованість. Збереження мінімального числа з'єднань з базою даних - ключова умова підтримки великого числа користувачів, яких Інтернет може привести на Web-сайт компанії. Оскільки для прокрутки і / або редагування об'єктів ResultSet зазвичай потрібно відкрите з'єднання з мережею, багато розробники не хочуть (або не можуть) їх використовувати.
На щастя, в JDBC 3.0 з'явилася альтернатива, яка дозволяє робити багато чого з того, що можна робити з ResultSet. не полишаючи підключення до бази даних відкритим.
Ідея в тому, що об'єкт Rowset. по суті, це той же ResultSet. але допускає модель як з підключенням, так і без нього. Для цього досить створити Rowset. вказати йому на ResultSet. і коли він заповниться, використовувати його замість ResultSet. як показано в лістингу 7.
Лістинг 7. Rowset замість ResultSet
У JDBC п'ять «реалізацій» (тобто розширень) інтерфейсу Rowset. JdbcRowSet - це реалізація Rowset з підключенням; інші чотири без підключення:
- CachedRowSet - це просто Rowset без підключення;
- WebRowSet - це підклас CachedRowSet. який "знає", як перетворити свої результати в XML і назад;
- JoinRowSet - це WebRowSet. який також "уміє" формувати еквівалент SQL JOIN без необхідності підключення до бази даних;
- FilteredRowSet - це WebRowSet. здатний ще і відфільтрувати отримані дані без необхідності підключення до бази даних.
Класи Rowset - це повноцінні модулі JavaBean, які підтримують події типу прослуховування, так що будь-які зміни в Rowset можна зафіксувати, розглянути і при необхідності прийняти. Насправді, Rowset може навіть управляти всім процесом щодо бази даних, якщо задані його властивості Username. Password. URL і DatasourceName (тобто він створить з'єднання з використанням DriverManager.getConnection ()) або властивості Datasource (які, ймовірно, можна отримати за допомогою JNDI). Потім можна замовити виконання SQL у властивості Command. викликати execute () і приступати до роботи з результатами - більше нічого не потрібно.
Реалізації Rowset. як правило, надаються драйвером JDBC, фактичний ім'я і / або пакет буде залежати від використовуваного драйвера JDBC. Реалізації Rowset стали частиною стандартного дистрибутива, починаючи з Java 5, тому досить просто створити. RowsetImpl () і працювати. (На той малоймовірний випадок, якщо в драйвері його немає, Sun пропонує еталонну реалізацію; див. Посилання в розділі Ресурси).
5. Пакетні поновлення
Незважаючи на всю їх корисність, варіанти Rowset іноді не відповідають усім вимогам, і доводиться повернутися до написання прямих SQL-операторів. У таких ситуаціях, особливо коли роботи багато, можна оцінити можливість пакетного поновлення, тобто виконання декількох SQL-операторів над базою даних в рамках одного взаємодії по мережі.
Щоб визначити, чи підтримує драйвер JDBC пакетні поновлення, викличте DatabaseMetaData.supportsBatchUpdates (). і ви отримаєте однозначну відповідь. Якщо пакетні поновлення підтримуються (на це може вказувати всі що завгодно, крім SELECT -виражена), складіть чергу і передайте її одним махом, як показано в лістингу 8.
Лістинг 8. Лови, база даних!
Виклик setAutoCommit () потрібен тому, що за замовчуванням драйвер спробує виконати кожен оператор окремо. В іншому код гранично простий: звичайні SQL-оператори з використанням виразів Statement або PreparedStatement. але натомість методу execute () викликається метод executeBatch (). який замість того щоб відразу відправити запит, ставить його в чергу.
Коли все готово, відправте всю чергу в базу даних, викликавши метод executeBatch () - він поверне масив цілочисельних значень, що містить ті ж результати, що і при використанні методу executeUpdate ().
висновок
API JDBC, один із стовпів Java-програмування, кожен Java-розробник повинен знати як "Отче наш". Забавно, що більшість програмістів не встигає за вдосконаленнями API, і в результаті ці розробники втрачають можливість заощадити час за допомогою прийомів, описаних в цій статті.
Звичайно, використовувати нові функції JDBC, вирішувати вам. Головний аспект, що вимагає розгляду, - це масштабованість системи, над якою ви працюєте. Чим більше її необхідно нарощувати, тим більш обмеженим буде доступ до бази даних, і отже, тим сильніше доведеться скорочувати мережевий трафік до неї. В цьому випадку вашими помічниками будуть набори рядків Rowset. скалярні виклики і пакетні поновлення. В інших випадках спробуйте прокручуваний і оновлювані набори ResultSet (які не споживають так багато пам'яті, як набори Rowset) і виміряйте їх вплив на масштабованість. Не виключено, що воно не буде так велико, як може здатися.
Далі в циклі 5 секретів .... прапори командного рядка.