Использование DataSource

Spring получает соединение с базой данных через DataSource. DataSource является частью спецификации JDBC и представляет собой обобщенную фабрику соединений. Он позволяет контейнеру или фреймворку скрывать проблемы объединения соединений в пул и управления транзакциями от кода приложения. Как разработчику, вам не требуется подробно знать о том, как подключиться к базе данных. За это отвечает администратор, который устанавливает источник данных. Скорее всего, вы выполняете обе роли, поскольку разрабатываете и тестируете код, но вам не обязательно знать, как настроен производственный источник данных.

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

Классы DriverManagerDataSource и SimpleDriverDataSource (входящие в дистрибутив 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 не поддерживает кастомно созданные уровни изоляции.