Reputation: 8596
I am developing a hotel booking site. It is a J2EE 1.4 web application and using JSP and JDBC.
I have one method which is responsible to book the hotel rooms.
booking()
And from this method I am calling other four methods
bookRooms()
makePayment()
confirmUserByMail()
confirmUserBySMS()
I am aware that two users can try to book the same room at the same time and in my current system it is possible that two users might end up with the same room.
How should I handle the transaction to avoid this concurrency issue?
It might be very common scenario but I never handled this kind of scenario before, so please guide me.
Upvotes: 11
Views: 883
Reputation: 246
To solve your concurrency use case you need to use the user's session to store or acquire the connection from the JDBC engine.
EJB Session Container solution can be an option (see solution above) that is more complex as you use the EJB tier of your server (if exists) ...
In case you want a more simple solution you can use the HTTPSession object to store your connection. The challenge here is to set / limit your JDBC connection pool according to your deployment scenario / use case and test it. This is possible by a set of configuration parameters for the JDBC engine or during initialization.
There is a relevant section in the web.xml configuration for referring the JDBC Connection as an application resource. I would use a Servlet Context Listener object to init the connection with the relevant configuration and HTTP Session Listener object to handle session related connection management and logging.
Upvotes: 0
Reputation: 1305
I would not use a lock on the database, instead I would insert a UNIQUE constraint on the database table of reservations for (ROOM, DAY) if it is possible.
Then I would use transaction in my code like this
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
//...
}
Now when you are going to make the insert into the table of reservations and commit it, if there is already another reservation for the same day and the same room a SQLException is going to be launched and the whole reservation transaction is going to be rollbacked.
In this way you are sure at the end of the day that you won't have double reservations for the same room.
Upvotes: 1
Reputation: 395
i am assuming you are using plain JDBC code and no ORM tool is involved here .you can put
conn.setAutoCommit(false);
so before committing transaction you can use locking mechanism as mention above not sure if locking is avail with J2EE 1.4 or not , if not please create queue for each hotel room or group of hotel rooms. once committed redirect all waiting request to other queue.
Upvotes: 1
Reputation: 4604
Assume underlying database is Oracle 11g
booking(){
start Trasaction;
...
bookRooms()
{
Room_Bookings
table with Room number and Timing slot as unique key
.Room_Bookings
table. unique constraint violation exception.
4.When you receive Exception in Java code, throw RuntimeException
for successful user, call rest other methods ( i.e. makePayment()..
)
(Editing point # 6 as per below discussion)
When this successful user checked-out Or timing slot ends-- i.e. period for which room booked; remove room details entry from 'Room_Bookings' table so that it will be available for further bookings.
}
if received RuntimeException rollback transaction with customized message;
else commit transaction;
end transaction.
}
Also, for technology perspective, you may use EJB container managed transaction with 2 - phase commit protocol; 1 for database resource , 1 for EJB. And your method may looks like below--
@TransactionAttribute(TransactionAttributeType.REQUIRED)
booking(){
.. ..
}
Upvotes: 1
Reputation: 100
In that case you can use Synchronized Method like this
public synchronized void booking()
{
Start Transaction;
}
Here we achieved a solution because Synchronized has it own nature where it allows only one user at a time ,after that user finish the job it will allow next to perform
Upvotes: 0
Reputation: 985
Use @TransactionAttribute
for declarative transaction management in your service.
Keep in mind, that user confirmations by email and SMS are not transactional by nature and should be only performed after successful completion of the transaction.
So, you may create a controller which will call a transactional BookingService and non-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(...) {
...
}
}
Upvotes: 1
Reputation: 9102
Your pseudocode implies that by the time this method is called the hotel details / payment details have already been captured by the user. There are typically two ways to go about designing such systems. This would require a decision whether to actually lock a room when a user has selected a room and is taking time while filling other details like payments etc. This is similar to Pessimistic lock in programmic languages / databases. You would need to show your user on the UI that he has to complete the transaction in x time else he would have to start again. In the application/database you have to make sure the selected rooms are locked and cannot be selected by anyone else for that particular time interval. In your data model (schema) you can add a flag to indicate if the rooms are selected, so that when another user searches for rooms this particular room(s) do not show up and are not selectable. Another approach is to use Optimistic locking where at the time of final logical transaction commit ( when you booking() method is called) - you check for the availability of the room and if already booked by another logical transaction - take the user to the beginning of the booking process. This is usually not like by customers of sites like hotel booking and the first method is generally a good user experience.
Upvotes: 1
Reputation: 11
Easiest way is add locks in your code or use locks provided by database.You can use volatile or other concurrent tools in java.
If the amount of user is not too large,you can use message queue.Visit from user is encapsulated into object, put the object into the queue and wait for process by threads. It's gonna cost a lot of memory space if there are too many users.
I've heard another way called Optimistic lock,but I've never used it in practice,you can try.
ps If the amount of user is very large,perhaps you should use cache like Memcached.Manipulate datas in the memory and map these changes to database later
Upvotes: 1
Reputation: 317
I'm sure there are high level ways to do this but I would just use a simple boolean isFull and have it become true when a booking is made.
boolean roomIsFull = false;
if (roomIsFull)
{
//Sir this room is already booked. Please Pick Another.
}
else
{
//Do your stuff.
roomIsFull = true;
}
Then you can still use your current code.
To ensure that a room is only booked once one can use the synchronized keyword to ensure that the method can be used once( not the best since two different rooms can't be booked at once)
Other option is to use volatile keyword so if a room object is being accessed other methods can see that.
Source: http://www.vogella.com/tutorials/JavaConcurrency/article.html#concurrency_overview
Upvotes: 0