Как обрабатывать транзакцию в J2EE 1.4 с помощью простого JDBC

Я разрабатываю сайт для бронирования отелей. Это веб-приложение J2EE 1.4 и использование JSP и JDBC.

У меня есть один метод, который отвечает за бронирование номеров в отеле.

booking()

И из этого метода я вызываю другие четыре метода

bookRooms()
makePayment()
confirmUserByMail()
confirmUserBySMS()

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

Как я должен обрабатывать транзакцию, чтобы избежать этой проблемы concurrency?

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

Ответ 1

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

Если количество пользователей не слишком велико, вы можете использовать очередь сообщений. Визуализация из пользователя инкапсулируется в объект, помещает объект в очередь и ждет процесса по потокам. Это будет стоить много места в памяти, если слишком много пользователей.

Я слышал еще один способ, называемый Optimistic lock, но я никогда не использовал его на практике, вы можете попробовать.

ps Если количество пользователей очень велико, возможно, вы должны использовать кеш, как Memcached.Manipulate datas в памяти, и скопировать эти изменения в базу данных позже

Ответ 2

Ваш псевдокод подразумевает, что к моменту, когда этот метод называется, детали отеля/детали оплаты уже были захвачены пользователем. Обычно существует два способа разработки таких систем. Это потребует принятия решения о том, действительно ли блокировать комнату, когда пользователь выбрал комнату, и занимает время при заполнении других деталей, таких как платежи и т.д. Это похоже на пессимистическую блокировку в программных языках/базах данных. Вам нужно будет показать пользователю в пользовательском интерфейсе, что он должен завершить транзакцию в В противном случае ему придется начать все заново. В приложении/базе данных вы должны убедиться, что выбранные комнаты заблокированы и не могут быть выбраны кем-либо иначе для этого конкретного интервала времени. В вашей модели данных (схеме) вы можете добавить флаг, чтобы указать, выбраны ли комнаты, чтобы другой пользователь искал комнаты эта конкретная комната (комнаты) не отображается и не может быть выбрана. Другой подход заключается в использовании Оптимистической блокировки, когда во время окончательной фиксации логической транзакции (когда вы вызываете метод book()) - вы проверяете наличие комнаты и, если уже забронирован другой логической транзакцией - возьмите пользователя в начале процесса бронирования. Обычно это не нравится клиентам таких сайтов, как бронирование гостиниц и первый метод, как правило, хороший пользовательский опыт.

Ответ 3

Предположим, что базовая база данных - это Oracle 11g

booking(){
start Trasaction;
...

bookRooms()
{
  • Поддержание одной таблицы Room_Bookings с номером комнаты и временным интервалом как unique key.
  • Когда пользователь выбирает комнату и продолжает двигаться дальше, вставьте данные оома в таблицу Room_Bookings.
  • Если несколько пользователей одновременно выбирают одну и ту же комнату; Oracle будет исключать

исключительное исключение нарушения ограничений.

4. Когда вы получаете Exception в Java-коде, throw RuntimeException

  1. для успешного пользователя, вызов остальных методов (т.е. makePayment()..)

    (Редактирование точки # 6, как описано ниже)

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

    }
    если получена транзакция возврата RuntimeException с настроенным сообщением;    else совершить транзакцию; конец транзакции. }

Кроме того, для перспективы технологии вы можете использовать транзакцию, управляемую контейнером EJB, с двухфазным протоколом фиксации; 1 для ресурса базы данных, 1 для EJB. И ваш метод может выглядеть ниже -

@TransactionAttribute (TransactionAttributeType.REQUIRED)

бронирование() {

.. ..

}

Ответ 4

Используйте @TransactionAttribute для декларативного управления транзакциями в вашей службе.

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

Итак, вы можете создать контроллер, который будет вызывать транзакцию BookingService и not-transactional NotificationService:

@WebServlet("/book")
public class BookingServlet extends HttpServlet {

    @Resource
    NotificationService notificationService;
    @Resource
    BookingService bookingService;

    @Override
    public void doPost(...) {
        bookingService.booking(...); 
        notificationService.confirmUserByMail(...);
        notificationService.confirmUserBySMS(...);
    }
}

@Stateless
public class BookingServiceImpl implements BookingService {

    @Resource
    private DataSource dataSource;

    @TransactionAttribute(REQUIRED)
    @Override
    public void booking(...) {
        bookRooms(...); 
        makePayment(...);
    }

    private void bookRooms(...) {
        //use dataSource here
    }

    private void makePayment(...) {
        //use dataSource here
    }
}

@Stateless
public class NotificationServiceImpl implements NotificationService {

    @Override
    public void notifyUserByMail(...) { 
       ...
    }
    @Override
    public void notifyUserBySMS(...) { 
       ...
    }
} 

Ответ 5

Я предполагаю, что вы используете простой JDBC-код, и здесь не задействован инструмент ORM. Вы можете поместить

conn.setAutoCommit(ложь);

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

Ответ 6

Я бы не использовал блокировку в базе данных, вместо этого я бы вложил ограничение UNIQUE в таблицу резервирования базы данных (ROOM, DAY), если это возможно.

Тогда я бы использовал транзакцию в моем коде, как это

Connection con = somehowGetYourConnection();

try {
    con.setAutoCommit(false);

    //query1
    //...

    //some more stuff
    //...

    //query 2, inserting into the table of the reservation
    //...

    con.commit();

}
catch(SQLException e)
{
    //log somehow
    //...

    con.rollback();

    //you will need somehow to tell your user that the reservation failed
    //...

}

Теперь, когда вы собираетесь сделать вставку в таблицу резервирования и зафиксировать ее, если есть еще одна резервация на тот же день и в той же комнате, будет запущен SQLException, и вся транзакция резервирования будет откат.

Таким образом, вы уверены, что в конце дня у вас не будет двойного резервирования в той же комнате.

Ответ 7

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

boolean roomIsFull = false;
if (roomIsFull)
{
    //Sir this room is already booked. Please Pick Another.
}
else
{
    //Do your stuff.
    roomIsFull = true;
}

Затем вы можете использовать свой текущий код.

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

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

Источник: http://www.vogella.com/tutorials/JavaConcurrency/article.html#concurrency_overview

Ответ 8

Вы можете сделать arraylist

ArrayList<String> roomFilled = new ArrayList<String>();

тогда, когда люди регистрируются, вы проверяете, заполнена ли комната или нет, делая

if(roomFilled.contains("roomNameHere"){
//Say that the room already has owner AND don't allow the user to book the room!
}else{
//Allow the user to get the room
}

И каждый раз, когда кто-то покупает комнату, вы "помещаете" комнату как заполненную, делая

roomFilled.add("roomNameHere");

Также не забудьте добавить этот

roomFilled.remove("roomNameHere");

Когда дата подписки закончилась.

Ответ 9

В этом случае вы можете использовать синхронный метод, подобный этому

public synchronized void  booking()
{
    Start Transaction;
}

Здесь мы достигли решения, потому что Synchronized имеет свою собственную природу, где он позволяет только одному пользователю за один раз, после того, как пользователь завершит задание, он позволит выполнять следующую команду

Ответ 10

Чтобы решить ваш случай использования concurrency, вам нужно использовать сеанс пользователя для хранения или получения соединения с помощью механизма JDBC.

  • Решение EJB Session Container может быть вариантом (см. решение выше), которое является более сложным, поскольку вы используете EJB-уровень вашего сервера (если существует)...

  • Если вам нужно более простое решение, вы можете использовать объект HTTPSession для хранения вашего соединения. Задача здесь - установить/ограничить пул подключений JDBC в соответствии с вашим сценарием/сценарием развертывания и протестировать его. Это возможно с помощью набора параметров конфигурации для механизма JDBC или во время инициализации.

В конфигурации web.xml имеется соответствующий раздел для обращения к JDBC-соединению в качестве ресурса приложения. Я бы использовал объект прослушивателя контекста сервлета, чтобы инициировать соединение с соответствующей конфигурацией и объектом HTTP-прослушивателя для обработки управления подключением к сеансу и ведения журнала.