Потокобезопасность в классе доступа к данным веб-приложения Java - PullRequest
0 голосов
/ 21 февраля 2009

Мой хобби-проект - это веб-приложение на Java. Это простая веб-страница с формой. Пользователь заполняет форму, отправляет и получает некоторые результаты.

Данные поступают через соединение JDBC. Когда пользователь отправляет, я проверяю ввод, строю оператор «CREATE ALIAS», оператор «SELECT» и оператор «DROP ALIAS». Я выполняю их и делаю все, что мне нужно сделать с ResultSet из запроса.

Из-за проблемы с ALIAS для конкретной комбинации базы данных / JDBC, которую я использую, требуется, чтобы при каждом выполнении запроса эти ALIAS создавались с уникальным именем. Я использую int, чтобы гарантировать это, которое увеличивается каждый раз, когда мы идем в базу данных.

Итак, мой класс доступа к данным выглядит примерно так:

private final static Connection connection = // initialized however

private static int uniqueInvocationNumber = 0;

public static Whatever getData(ValidatedQuery validatedQuery) {
    String aliasName = "TEMPALIAS" + String.valueOf(uniqueInvocationNumber);
    // build statements, execute statements, deal with results
    uniqueInvocationNumber++;
}

Это работает. Однако недавно мне стало известно, что я твердо застрял в фазе 0 Джона Скита многопоточность («Полное невежество - игнорируйте любую возможность проблем».) код или поточно-ориентированный код. Я понятия не имею, что может случиться, когда многие пользователи одновременно используют приложение.

Итак, мой вопрос (при условии, что я не наткнулся на безопасность потоков благодаря слепой удаче / магии J2EE):

Как я могу сделать это безопасно?

Я включил сюда информацию, которая, на мой взгляд, является актуальной, но дайте мне знать, если ее недостаточно.

Спасибо, миллион.

РЕДАКТИРОВАТЬ: Это правильное веб-приложение J2EE, использующее платформу Wicket. Я обычно развертываю его в Причале.

РЕДАКТИРОВАТЬ: Длинная история о мотивации для алиасов, для тех, кто заинтересован:

База данных, о которой идет речь, - это DB2 на AS400 (i5, System i, iSeries, как бы IBM ее ни назвала в наши дни), и я использую jt400 .

Хотя DB2 на AS400 похожа на DB2 на любой другой платформе, в таблицах есть понятие «член» из-за устаревших вещей. Член - это как кусок стола. Запрос, который я хочу выполнить:

SELECT thisField FROM thisTable(thisMember)

, который обрабатывает thisMember как отдельную таблицу, поэтому просто предоставляет thisField для всех строк в элементе.

Теперь такие запросы нормально выполняются в интерактивном сеансе SQL, но не работают через JDBC (я не знаю почему). Обходной путь, который я использую, - сделать что-то вроде

CREATE ALIAS tempAlias FOR thisTable(thisMember)

затем

SELECT thisField FROM tempAlias

затем

DROP ALIAS tempAlias

, который работает, но для одной проблемы остановки показа: когда вы делаете это неоднократно с ALIAS, всегда называемым "tempAlias", и в случае, когда thisField имеет разную длину от одного запроса к следующему, набор результатов возвращается искаженным для второго запроса (getString для первой строки - это нормально, в следующем есть определенное количество пробелов, а в следующем - то же количество пробелов - это из памяти, но это что-то вроде этого).

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

Я только что понял (потратив время на то, чтобы раскрыть это объяснение), что я, вероятно, не потратил достаточно времени на обдумывание проблемы, прежде чем приступить к решению проблемы. К сожалению, я еще не осуществил свою мечту о приобретении AS400 для своей спальни;), поэтому я не могу попробовать ничего нового.

Ответы [ 3 ]

8 голосов
/ 21 февраля 2009

Ну, на данный момент я собираюсь игнорировать любые вещи SQL и сосредоточиться только на части uniqueInvocationNumber. Здесь есть две проблемы:

  • Нет гарантии, что поток увидит последнее значение в любой конкретной точке
  • Шаг не атомарный

Самый простой способ исправить это в Java - использовать AtomicInteger :

private static final AtomicInteger uniqueInvocationNumber = new AtomicInteger();

public static Whatever getData(ValidatedQuery validatedQuery) {
    String aliasName = "TEMPALIAS" + uniqueInvocationNumber.getAndIncrement()
    // build statements, execute statements, deal with results
}

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

Другая потенциальная проблема - разделение одного соединения между разными потоками. Как правило, лучший способ справиться с соединениями с базой данных - это использовать пул соединений и «открывать / использовать / закрывать» соединение, где вам нужно (закрытие соединения в блоке finally).

0 голосов
/ 22 февраля 2009

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

Я понимаю, что это просто хобби-проект, но подумайте над тем, чтобы включить свой «список дел», чтобы переключиться на использование пула соединений. Это все часть обучения, которое, я думаю, является частью вашей мотивации для выполнения этого проекта. Пулы соединений - это правильный способ работы с несколькими одновременными пользователями в веб-приложении на основе базы данных.

0 голосов
/ 21 февраля 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...