Використання DataSource

Spring отримує з'єднання з базою даних через DataSource. DataSource є частиною специфікації JDBC і є узагальненою фабрикою з'єднань. Він дозволяє контейнеру або фреймворку приховувати проблеми об'єднання з'єднань у пул та керування транзакціями від коду програми. Як розробнику тобі не потрібно докладно знати, як підключитися до бази даних. За це відповідає адміністратор, який встановлює джерело даних. Скоріше за все ти виконуєш обидві ролі, оскільки розробляєш та тестуєш код, але тобі не обов'язково знати, як налаштовано виробниче джерело даних.

Якщо ти використовуєш рівень JDBC у Spring, то можеш отримати джерело даних з JNDI або налаштувати власний за допомогою реалізації пулу з'єднань, наданого третьою стороною. Традиційними варіантами є Apache Commons DBCP та C3P0 з класами DataSource на основі біну; для сучасного пулу з'єднань JDBC розглянь HikariCP з його API-інтерфейсом на основі конструктора

Класи DrivermanGerDataZarche та SimpleDriverDataZone (вони входять до дистрибутиву Spring) слід використовувати тільки з метою тестування! Ці варіанти не забезпечують об'єднання в пул і погано працюють, коли виконується кілька запитів на з'єднання.

У наступному розділі використовується реалізація DriverManagerDataSource зі Spring. Декілька інших варіантів DataSource будуть розглянуті пізніше.

Щоб налаштувати DriverManagerDataSource:

  1. Встанови з'єднання з DriverManagerDataSource так само, як ти зазвичай встановлюєш з'єднання для JDBC.

  2. Вкажи повне ім'я класу драйвера JDBC, щоб DriverManager міг завантажити клас драйвера.

  3. Вкажи URL-адресу, яка відрізняється для різних драйверів JDBC. (Правильне значення див. у документації до драйвера).

  4. Вкажи ім'я користувача та пароль для підключення до бази даних.

У цьому прикладі показано, як налаштувати DriverManagerDataSource в Java:

Java

DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa");
dataSource.setPassword("");
Kotlin

val dataSource = DriverManagerDataSource().apply {
    setDriverClassName("org.hsqldb.jdbcDriver")
    url = "jdbc:hsqldb:hsql://localhost:"
    username = "sa"
    password = ""
}

У наступному прикладі показано відповідну XML-конфігурацію:


<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>

У наступних двох прикладах показано базове підключення та конфігурацію для DBCP і C3P0. Щоб дізнатися про інші опції, які допомагають керувати функціями пулу, звернися до документації про продукти для відповідних реалізацій пулу з'єднань.

У наступному прикладі показано конфігурацію DBCP:


<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>

У цьому прикладі показано конфігурацію C3P0:


<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="driverClass" value="${jdbc.driverClassName}"/>
    <property name="jdbcUrl" value="${jdbc.url}"/>
    <property name="user" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>

Використання DataSourceUtils

Клас DataSourceUtils – це зручний та повнофункціональний допоміжний клас, який надає статичні методи для встановлення з'єднань з JNDI та закриття з'єднань за потреби. Він підтримує потокові з'єднання, наприклад, з DataSourceTransactionManager.

Реалізація SmartDataSource

Інтерфейс SmartDataSource повинен бути реалізований класами, які можуть забезпечити підключення до реляційної бази даних. Він розширює інтерфейс DataSource, дозволяючи класам, які його використовують, робити запит щодо того, чи слід закривати з'єднання після певної операції. Таке використання є ефективним, якщо ти знаєш, що тобі потрібно повторно використовувати з'єднання.

Розширення AbstractDataSource

AbstractDataSource — це abstract базовий клас для реалізацій DataSource із Spring. Він реалізує код, який є спільним для всіх реалізацій DataSource. Тобі слід розширювати клас AbstractDataSource, якщо ти пишеш власну реалізацію DataSource.

Використання SingleConnectionDataSource

Клас SingleConnectionDataSource є реалізацією інтерфейсу SmartDataSource, який обертає одне Connection, яке не закривається після кожного використання. Він не підтримує багатопотоковість.

Якщо будь-який клієнтський код викликає close, виходячи з припущення, що існує пул з'єднань (як при використанні інструментів підтримки постійного зберігання даних), слід встановити властивість suppressClose у true. Цей параметр повертає проксі, який пригнічує закриття, який обгортає фізичне з'єднання. Зверни увагу, що більше не можна перетворити його на власний Connection з Oracle або аналогічний об'єкт.

SingleConnectionDataSource — це перш за все тестовий клас. Як правило, він дозволяє легко тестувати код поза сервером додатків у поєднанні з простим оточенням JNDI. На відміну від DriverManagerDataSource, він постійно використовує те саме з'єднання, уникаючи надмірного створення фізичних з'єднань.

Використання DriverManagerDataSource

Клас DriverManagerDataSource є реалізацією стандартного інтерфейсу DataSource, який конфігурує звичайний драйвер JDBC через властивості біна і щоразу повертає нове Connection.

Ця реалізація корисна для тестових і автономних оточень поза контейнером Java EE, як біном DataSource в IoC-контейнері Spring, так і в поєднанні з простим оточенням JNDI. Виклики Connection.close() закривають з'єднання, тому будь-який код постійного зберігання даних, сумісний з DataSource, повинен працювати. Однак використовувати пули з'єднань на основі JavaBean (таких як commons-dbcp) настільки просто, навіть у тестовому оточенні, що майже завжди такий пул з'єднань кращий, ніж DriverManagerDataSource.

Використання TransactionAwareDataSourceProxy

TransactionAwareDataSourceProxy — це проксі для цільового DataSource. Проксі огортає цільовий DataSource,, щоб підвищити рівень сумісності з транзакціями, які управляються Spring. У цьому відношенні він схожий на транзакційний DataSource з JNDI, який надається сервером Java EE.

Використання цього класу не є бажаним за винятком тих випадків, коли необхідно викликати вже існуючий код та передати йому стандартну реалізацію інтерфейсу DataSource з JDBC. У цьому випадку можна зберегти можливість використання цього коду і водночас забезпечити участь цього коду в транзакціях, керованих Spring. Зазвичай краще писати новий код, використовуючи абстракції вищого рівня для управління ресурсами, такі як JdbcTemplate або DataSourceUtils.

Детальнішу інформацію див. у javadoc TransactionAwareDataSourceProxy.

Використання DataSourceTransactionManager

Клас DataSourceTransactionManager — це реалізація PlatformTransactionManager для окремих джерел даних JDBC. Він прив'язує JDBC-з'єднання із зазначеного джерела даних до чинного потоку, що потенційно дозволяє використовувати одне потокове з'єднання для кожного джерела даних.

Код програми повинен отримувати JDBC-з'єднання через DataSourceUtils.getConnection(DataSource) замість стандартного DataSource.getConnection у Java EE. Він генерує неперевірені винятки org.springframework.dao замість SQLExceptions. Усі класи фреймворку (наприклад, JdbcTemplate) використовують цю стратегію неявно. Якщо стратегія пошуку не використовується з цим менеджером транзакцій, то вона поводиться так само, як і звичайна. Таким чином, у будь-якому випадку її можна використовувати.

Клас DataSourceTransactionManager підтримує кастомні рівні ізоляції та значення часу очікування, які застосовуються як відповідні значення часу очікування запитів JDBC-інструкцій. Щоб забезпечити підтримку останнього варіанту, код програми повинен або використовувати JdbcTemplate, або викликати метод DataSourceUtils.applyTransactionTimeout(..) для кожної створеної інструкції.

Можна використовувати цю реалізацію замість JtaTransactionManager у разі використання єдиного ресурсу, оскільки вона не вимагає від контейнера підтримки JTA. Переключення між ними – це просто питання конфігурації, за умови, що ти дотримуєшся необхідного шаблону пошуку з'єднань. JTA не підтримує кастомно створені рівні ізоляції.