ThreadLocal + java.sql.Connection + фильтр сервлета = 2009?

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

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

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


person sehugg    schedule 11.06.2009    source источник
comment
Почему вы не можете передать Connection в качестве параметра методов, которые вы вызываете?   -  person Eduardo    schedule 26.08.2011


Ответы (5)


Управление транзакциями 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();
    }
});

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

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

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

person Gareth Davis    schedule 11.06.2009
comment
Я согласен, если вам нужен простой JDBC, это нормально, но не изобретайте колесо, поддержка SpringFrameowrk JDBC может быть встроена очень легко, а JDBC API довольно мал, и вы получите выгоду от повторного использования того, что уже было написано и протестировано. - person adrian.tarau; 12.06.2009
comment
Мне нужно будет проверить это, я пытаюсь избежать больших фреймворков в этом приложении (таким образом, используя простые сервлеты), но если я смогу использовать Spring, не вдаваясь в олл-ин, это может быть решением. - person sehugg; 12.06.2009
comment
Я как бы понял, что вы собираетесь использовать минимальный подход. Используйте модульные jar-файлы spring-transaction.jar, spring-core и т. д., и вам не придется включать uber 2mb spring.jar. - person Gareth Davis; 12.06.2009
comment
Мне нужно было всего 5 .jars, и я преобразовал их в стиль JdbcTemplate. Я все равно ненавижу прямое программирование JDBC;) - person sehugg; 14.06.2009
comment
хороший результат. Если вы используете материал JdbcTemplate, вам не нужно использовать вещь TransactionAwareDataSourceProxy, поскольку JdbcTemplate поддерживает транзакции и сортирует этот материал для вас. - person Gareth Davis; 15.06.2009

Большинство современных серверов приложений поддерживают 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();
    }
}
person Tusc    schedule 11.06.2009
comment
JTA выглядит удобно, интересно, хорошо ли JTA интегрируется с Tomcat/Jetty? Я пытаюсь сделать эту часть приложения как можно более компактной (то есть только сервлеты, минимальные зависимости и т. д.). - person sehugg; 12.06.2009
comment
больше о JTA: stackoverflow.com/questions/840694/ - person sehugg; 12.06.2009
comment
Должен признать, что я использовал JTA только на $$$ серверах приложений. Кажется, что Tomcat и Jetty сами по себе не обеспечивают реализацию JTA. Jboss и JOTM предоставляют автономную реализацию JTA, но они поставляются с поддержкой ejb или кажутся сложными. Ссылка, по которой вы дали баллы на BTM (docs.codehaus.org/display/BTM/Home), который, кажется, соответствует всем вашим потребностям (активное сообщество, бесплатная, легкая, хорошо документированная интеграция и использование). Честно говоря, я не знаю, сработает ли это, но звучит довольно многообещающе. - person Tusc; 13.06.2009
comment
Кстати, взгляните на руководство (docs.codehaus.org/display/BTM/JtaBestPractices), который удивительно лаконичен и ясен, а также немного оптимизирован (docs.codehaus.org/ display/BTM/LastResourceCommit13) - person Tusc; 13.06.2009
comment
С помощью этих двух ссылок (ниже) и двух последних: интеграция с Tomcat (docs. codehaus.org/display/BTM/Tomcat13), интеграция с Jetty (docs.codehaus .org/display/BTM/Jetty13), я думаю, вы сможете быстро создать доказательство концепции. - person Tusc; 13.06.2009

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

person Tom Hawtin - tackline    schedule 11.06.2009

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

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

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

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

person Community    schedule 12.06.2009

Использование фильтра для управления транзакциями — хороший подход к развертыванию собственного управления транзакциями.

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

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

person erickson    schedule 11.06.2009