Free Online Courses for Software Developers - MrBool
× Please, log in to give us a feedback. Click here to login
×

You must be logged to download. Click here to login

×

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

×

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

Handling multiple transaction in J2EE application using JDBC

This tutorial is about how to handle multiple transaction and concurrency in a simple bank application using.

This Bank Application will allow user to see his balance, transfer balance to other account, and see his transaction and other details. On the other side, an admin of the app will create and delete user accounts.

So there are two separate roles admin and user. Right now we will focus on transfer funds functionality.

First of all let us identify our model classes.

Model classes will be: User, Account, Transaction, and TransactionType

User class will have all the basic information about user like name, address, email,password.

Account class will have the balance, and userId who is the account holder, it’s a foreign key constraint to user table it will also have reference to transaction table to have all the details related to transaction.

Transaction will have the accountId, transaction type [debit or credit], comment and time.

So these are our model classes:

Listing 1: Model Classes

 
package com.bankapp.model;

import java.math.BigDecimal;
import java.util.List;

public class Account
{
	private int id;
	private String type;
	private BigDecimal balance;
	private User user;
	private List<Transaction> transactions;
	public List<Transaction> getTransactions()
	{
		return transactions;
	}
	public void setTransactions(List<Transaction> transactions)
	{
		this.transactions = transactions;
	}
	public int getId()
	{
		return id;
	}
	public void setId(int id)
	{
		this.id = id;
	}
	public String getType()
	{
		return type;
	}
	public void setType(String type)
	{
		this.type = type;
	}
	public BigDecimal getBalance()
	{
		return balance;
	}
	public void setBalance(BigDecimal balance)
	{
		this.balance = balance;
	}
	public User getUser()
	{
		return user;
	}
	public void setUser(User user)
	{
		this.user = user;
	}
}
package com.bankapp.model;

import java.math.BigDecimal;
import java.sql.Timestamp;

public class Transaction
{

	private int id;
	private BigDecimal amount;
	private Account account;
	private TransactionType type;
	private String comment;
	private Timestamp date;

	public Account getAccount()
	{
		return account;
	}
	public void setAccount(Account account)
	{
		this.account = account;
	}
	public int getId()
	{
		return id;
	}
	public void setId(int id)
	{
		this.id = id;
	}
	public BigDecimal getAmount()
	{
		return amount;
	}
	public void setAmount(BigDecimal amount)
	{
		this.amount = amount;
	}
	public TransactionType getType()
	{
		return type;
	}
	public void setType(TransactionType type)
	{
		this.type = type;
	}
	public String getComment()
	{
		return comment;
	}
	public void setComment(String comment)
	{
		this.comment = comment;
	}
	public Timestamp getDate()
	{
		return date;
	}
	public void setDate(Timestamp date)
	{
		this.date = date;
	}

}
package com.bankapp.model;

public class TransactionType
{
	private int id;
	private String type;
	public int getId()
	{
		return id;
	}
	public void setId(int id)
	{
		this.id = id;
	}
	public String getType()
	{
		return type;
	}
	public void setType(String type)
	{
		this.type = type;
	}
}
package com.bankapp.model;

public class User
{
	private int id;
	private String name;
	private String address;
	private String email;
	public String getEmail()
	{
		return email;
	}
	public void setEmail(String email)
	{
		this.email = email;
	}
	public int getId()
	{
		return id;
	}
	public void setId(int id)
	{
		this.id = id;
	}
	public String getName()
	{
		return name;
	}
	public void setName(String name)
	{
		this.name = name;
	}
	public String getAddress()
	{
		return address;
	}
	public void setAddress(String address)
	{
		this.address = address;
	}
	public String getPassword()
	{
		return password;
	}
	public void setPassword(String password)
	{
		this.password = password;
	}
	private String password;
}

Now we will create a separate DAO layer for our database transactions in bank app.

We will create basic interfaces for DAO. The reason for creating this extra layer is, if in future you want to use some framework like hibernate instead of jdbc, you can do it with minimal changes, because everywhere we will use interface type, instead of concrete implementations.

Now, if users transfer from one account to other, we have to insert following entries in tables:

  1. Balance of two accounts involved in transfer needs to be updated.
  2. Add records in transaction table.

So here are our DAO interfaces (skipping implementation as this is not our main topic).

Listing 2: DAO Interfaces

package com.bankapp.dao;

import java.sql.SQLException;
import com.bankapp.model.Account;
import com.bankapp.model.User;

public interface AccountDAO
{
	public Account get(int accountId) throws SQLException;
	public Account get(User user) throws SQLException;
	public boolean create(Account account) throws SQLException;
	public boolean update(Account account) throws SQLException;
	public boolean delete(int accountId) throws SQLException;
}
package com.bankapp.dao;

import java.sql.SQLException;
import com.bankapp.model.User;

public interface UserDAO
{
	public User get(int userId) throws SQLException;
	public boolean create(User user) throws SQLException;
	public boolean update(User user) throws SQLException;
	public boolean delete(int userId) throws SQLException;
	public User get(String email) throws SQLException;
}

package com.bankapp.dao;

import java.sql.SQLException;
import java.util.List;

import com.bankapp.model.Account;
import com.bankapp.model.Transaction;

public interface TransactionDAO
{
	public boolean create(Transaction transaction) throws SQLException;
	public List<Transaction> getTransaction(Account ac,int numberOFTransaction) throws SQLException;
}

Now this is the Servlet class which will get the input from jsp and convert it in to model objects.

Our servlet here is acting as a controller. Model, view and controller all are separated, so we can change the implementation any time.

When the user logs in, we set the user Object in the request session.

Listing 3: Setting the user Object in the request session

package com.bankapp.servlets;

import java.io.IOException;
import java.math.BigDecimal;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.bankapp.exceptions.InsufficientBalanceException;
import com.bankapp.exceptions.InvalidAccountNumberException;
import com.bankapp.model.User;
import com.bankapp.service.Service;

/**
 * Servlet implementation class TransferFunds
 */
public class TransferFunds extends HttpServlet
{
	private static final long serialVersionUID = 1L;

	/**
	 * @see HttpServlet#HttpServlet()
	 */
	public TransferFunds()
	{
		super();
		// TODO Auto-generated constructor stub
	}

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException
	{

	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException
	{
		Service service = (Service) getServletContext().getAttribute("service");
		HttpSession session = request.getSession();

		try
		{
			int acNumber = Integer.parseInt(request
					.getParameter("accountNumber"));
			BigDecimal amount = new BigDecimal(request.getParameter("amount"));
			User u = (User) session.getAttribute("user");
			service.transferFunds(acNumber, u, amount);
			request.setAttribute("successMessage",
					"amount transferred successfully");

		}
		catch(NumberFormatException e)
		{
			request.setAttribute("errorMessage", "please enter valid values");
		}
		catch(IllegalArgumentException e)
		{
			request.setAttribute("errorMessage", e.getMessage());
		}
		catch(InvalidAccountNumberException e)
		{
			request.setAttribute("errorMessage",
					"please enter correct ac number");
		}
		catch(InsufficientBalanceException e)
		{
			request.setAttribute("errorMessage", "insufficient balance ");
		}

		catch(Exception e)
		{
			throw new ServletException(e);
		}
		RequestDispatcher dispatcher = request.getRequestDispatcher("view.jsp");
		dispatcher.forward(request, response);

	}

}


Now this servlet is using instance of Service class which will maintain transaction between multiple DAO.

Suppose, when transferring funds, we have debited one account, but for crediting if some exception occurs we have to maintain the previous state of our data.

Listing 4: Maintaining the previous state of our data

package com.bankapp.service;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.sql.Timestamp;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import com.bankapp.dao.AccountDAO;
import com.bankapp.dao.TransactionDAO;
import com.bankapp.dao.UserDAO;
import com.bankapp.dao.jdbc.AccountDAOImpl;
import com.bankapp.dao.jdbc.TransactionDAOImpl;
import com.bankapp.dao.jdbc.TransactionTypeDAOImpl;
import com.bankapp.dao.jdbc.UserDAOImpl;
import com.bankapp.db.ConnectionManager;
import com.bankapp.exceptions.DBException;
import com.bankapp.exceptions.InsufficientBalanceException;
import com.bankapp.exceptions.InvalidAccountNumberException;
import com.bankapp.model.Account;
import com.bankapp.model.Transaction;
import com.bankapp.model.User;

/**
 * Service class to provide transaction support with jdbc 
 * @author abhishek.somani
 *
 */
public class Service
{
	private Map<Integer, Lock> accountLocks = new ConcurrentHashMap<Integer, Lock>();
	private ConnectionManager manager;

	public void setConnectionManager(ConnectionManager manager)
	{
		this.manager = manager;
	}
        public boolean transferFunds(int toACNumber,User fromUser,BigDecimal amount) throws InsufficientBalanceException,DBException,InvalidAccountNumberException
	{
		boolean result = false;
		Connection con = null;

		synchronized (fromUser)
		{
			try
			{
				con = manager.getConnection();
				con.setAutoCommit(false);

				AccountDAO accountDAO = new AccountDAOImpl(con);
				TransactionDAO txDAO = new TransactionDAOImpl(con);
				TransactionTypeDAOImpl transactionTypeDAOImpl = new TransactionTypeDAOImpl(
						con);

				Account from = accountDAO.get(fromUser);

				if (from.getId() == toACNumber)
				{
					throw new IllegalArgumentException(
							"source and destination account are same");
				}

				Lock lock = getLock(toACNumber);
				lock.tryLock();

				Account to = accountDAO.get(toACNumber);

				if (to == null)
				{
					throw new InvalidAccountNumberException(
							"account number is not valid");
				}

				if (from.getBalance().compareTo(amount) == -1)
				{
					throw new InsufficientBalanceException(
							"insuffiecient balance");
				}
				from.setBalance(from.getBalance().subtract(amount));
				result = accountDAO.update(from);
				to.setBalance(to.getBalance().add(amount));
				result = accountDAO.update(to);

				Transaction tn1 = new Transaction();
				tn1.setAccount(from);
				tn1.setAmount(amount);
				tn1.setComment("transferred to A/c number " + to.getId());
				tn1.setDate(new Timestamp(System.currentTimeMillis()));
				tn1.setType(transactionTypeDAOImpl.get("DEBIT"));

				Transaction tn2 = new Transaction();
				tn2.setAccount(to);
				tn2.setAmount(amount);
				tn2.setComment("transferred from A/c number " + from.getId());
				tn2.setDate(new Timestamp(System.currentTimeMillis()));
				tn2.setType(transactionTypeDAOImpl.get("CREDIT"));

				result = txDAO.create(tn1);
				result = txDAO.create(tn2);

				con.commit();

			}
			catch(ClassNotFoundException e)
			{
				throw new DBException(e.getMessage(), e);
			}
			catch(SQLException e)
			{
				rollBack(con);
				throw new DBException(e.getMessage(), e);
			}
			finally
			{
				if (!result)
					rollBack(con);
				Lock lock = removeLock(toACNumber);
				if (lock != null)
					lock.unlock();
				close(con);
			}

		}
		return result;

	}
      private Lock getLock(int accountId)
	{
		Lock lock = accountLocks.get(accountId);
		if (lock == null)
		{
			lock = new ReentrantLock();
			accountLocks.put(accountId, lock);
		}
		return lock;

	}
}

For handling threading issues, we have double lock.

First lock is on user, so if a user logs in and try to do multiple transaction at the same time, only one will execute.

The other is on account, so that this account cannot be included in multiple transactions at the same time.

We are getting connection object from Connection Manager. So now interaction with multiple tables will act as a single unit of work.

If any error occurs, whole transaction will be rolled back.

This is the input jsp by which user will enter details about transfer funds.

Listing 5: JSP input

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/css/style.css" >
<title>Transfer Funds from this account to other account</title>
</head>
<body>

	<form name="transferFunds" action="transferFunds" method="post">
	
			
	<label for="To">Account number</label>
	<input type="text" name="accountNumber">
	<div class="clear"></div>
	
	<label for="amount">Amount</label>
	<input type="text" name="amount">
	<div class="clear"></div>		
						
	<input type="submit" style="margin: -20px 0 0 287px;" class="button" name="commit" value="transfer">
</form>
<a href="view.jsp">back</a>

</body>
</html>

This is the view jsp, where we are redirecting the user along with message {either tranfer succeded or failed}

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
     <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>welcome </title>
<link rel="shortcut icon" href="/favicon.ico"/>
<link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/css/style.css" >
</head>
<body>
Welcome ${user.name}

<table align="center">
<tr><td><a href ="info">View Account Information</a></td></tr>
<tr><td><a href ="transfer.jsp">Transfer Funds</a></td></tr>
<tr><td><a href ="transaction.jsp">View Transaction</a></td></tr>
<tr><td><a href ="${pageContext.request.contextPath}/logOut">Log Out</a></td></tr>
</table>
<div style="color: Red"> ${errorMessage}  </div>
<div> ${successMessage}  </div>
</body>
</html>

Things to remember:

  • Create DAO classes related to single table. Do not mix multiple table interaction in one single DAO.
  • Create a new Service layer to handle and maintain transaction related to multiple table interaction.
  • Make sure not to have any class fields members in case of multithreaded application. If it is necessary, properly synchronize it.
  • Use synchronization wisely, and should not mark the whole method synchronized, instead use objects and locks.
  • Throw different exception to show proper error message to user.

Hope you like this article. For any help you can comment or mail me.

Will see you in the next article with some more interesting code.



Abhishek is working as a senior java developer in a product start up .He has worked on various java related enterprise applications and frameworks. He loves to explore new technologies

What did you think of this post?
Services
[Close]
To have full access to this post (or download the associated files) you must have MrBool Credits.

  See the prices for this post in Mr.Bool Credits System below:

Individually – in this case the price for this post is US$ 0,00 (Buy it now)
in this case you will buy only this video by paying the full price with no discount.

Package of 10 credits - in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download few videos. In this plan you will receive a discount of 50% in each video. Subscribe for this package!

Package of 50 credits – in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download several videos. In this plan you will receive a discount of 83% in each video. Subscribe for this package!


> More info about MrBool Credits
[Close]
You must be logged to download.

Click here to login