ThreadLocal + java.sql.Connection + сервлет-фильтр = 2009? - PullRequest
4 голосов
/ 11 июня 2009

Я пишу некоторые сервлеты с простыми старыми шаблонами JDBC. Я понял, что у меня есть несколько объектов, которые хотели бы совместно использовать одну транзакцию, и я бы хотел, чтобы одна транзакция HTTP = одна транзакция базы данных.

Я думаю, что могу сделать это, передавая Connection в переменной ThreadLocal, а затем используя фильтр сервлета, обрабатывающий создание / принятие / откат указанного Connection.

Существует ли существующая структура, которая делает это, к которой я не причастен, или это разумный способ сделать что-то в конце 00 года?

Ответы [ 5 ]

4 голосов
/ 11 июня 2009

Spring управление транзакциями делает именно то, что вы описываете, на первый взгляд это может быть немного затруднительно, но все, что вам понадобится (для простейшего случая):

org.springframework.jdbc.datasource.DataSourceTransactionManager org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy org.springframework.transaction.support.TransactionTemplate

Подключите существующий DataSource и оберните его в TransctionAwareDataSourceProxy, затем создайте DataSourceTransactionManager с обернутым источником данных, сохраните их в ServletContext. Затем для каждой транзакции создайте TransactionTemplate, передаваемый в диспетчере транзакций, и вызовите метод execute (TransactionCallback) для запуска вашего кода. например:

new TransactionTemplate(transactionManager).execute(new TransactionCallback(){
    public void doInTransaction(TransactionStatus ts){
        // run your code here...use the dataSource to get a connection and run stuff
        Connection c = dataSourceProxy.getConnection();
        // to rollback ... throw a RuntimeException out of this method or call 
        st.setRollbackOnly();
    }
});

Соединение будет привязано к локальному потоку, поэтому, если вы всегда получаете соединение с одним и тем же источником данных, т.е. с обернутым, вы получите одно и то же соединение в одной и той же транзакции.

Обратите внимание, что это простейшая установка пружинных транзакций ... не обязательно самая лучшая или рекомендуемая, для этого посмотрите справочные документы по пружинам или прочитайте пружину в действии.

... поэтому я думаю, что в качестве прямого ответа да это разумная вещь, это то, что весенние рамки делали в течение долгого времени.

1 голос
/ 12 июня 2009

Большинство сегодняшних appServer поддерживают JTA (Java Transaction Api): транзакция, охватывающая несколько открытых / закрытых соединений jdbc. Он делает для вас "threadLocal" и поддерживает J2EE. Вы используете это в своем фильтре следующим образом:

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException {
    UserTransaction transaction = null;
    try {
        transaction = (UserTransaction)new InitialContext().lookup("java:comp/UserTransaction");
        transaction.begin();
        chain.doFilter(request, response);
        transaction.commit();
    } catch (final Exception errorInServlet) {
        try {
            transaction.rollback();
        } catch (final Exception rollbackFailed) {
            log("No ! Transaction failed !",rollbackFailed);
        }
        throw new ServletException(errorInServlet);
    }
}

На сервере приложений объявите источник данных с именем jndi и используйте его в своем коде для получения соединения (НЕ делайте cx.commit (), cx.rollback () или cx.setAutocommit (), это будет мешать JTA). Вы можете открывать и закрывать ваше соединение несколько раз в одной транзакции HTTP, JTA позаботится об этом:

public void doingDatabaseStuff() throws Exception {
    DataSource datasource = (DataSource)new InitialContext().lookup("/path/to/datasource");
    Connection connection = datasource.getConnection();
    try {
        // doing stuff
    } finally {
        connection.close();
    }
}
0 голосов
/ 12 июня 2009

Если вы не можете полагаться на «настоящий» сервер приложений и хотите избежать не такой уж легкой нагрузки на Spring, используя фильтр для обеспечения соединения, оставьте его в потоке и закройте его в конце запроса это действительно практичное и разумное решение.

Вам понадобится некоторый (по существу статический) класс доступа, который позволяет получить () соединение и setRollbackOnly ().

По завершении запроса, с точки зрения фильтра, убедитесь, что перехватывает исключения (для которых вы должны войти в систему и установите только откат) и фиксирует / откатывает, соответственно закрывайте транзакцию.

В большинстве приложений и веб-контейнеров (а JTA обычно делает аналогичные предположения) запрос будет обрабатываться ровно одним потоком, и связывание одного соединения с базой данных с потоком для повторного использования между слоями во время запроса является правильным решением. делаем.

0 голосов
/ 11 июня 2009

Наличие фильтра для управления транзакциями - это хороший подход к управлению собственным управлением транзакциями.

Спецификация Java EE обеспечивает управление транзакциями, а альтернативные платформы, такие как Spring, предоставляют аналогичную поддержку (хотя это не одобрение; Spring не обязательно делает это хорошо).

Однако использование ThreadLocal может создать проблемы. Например, нет никаких гарантий, что в запросе используется один поток, все может получить доступ к Connection через глобальную переменную, и тестирование может стать более сложным, если вы зависите от какого-либо глобального состояния, которое нужно настроить. Я хотел бы рассмотреть возможность использования контейнера внедрения зависимостей для явной передачи Connection объектам, которым он нужен.

0 голосов
/ 11 июня 2009

Обычно лучше передать объект с «Параметризацией сверху», а обработать - с помощью ThreadLocal. В случае ServletFilter, атрибут ServletRequest будет очевидным местом. Интерфейс для не зависящего от сервлета кода может извлечь Connection в значимый контекст.

...