Question

I'm performing CRUD operations using Servlet and JSP. The following class is used to retrieve a connection from a connection pool maintained by a server (Tomcat).

public final class DatabaseConnection {

    private static final DataSource dataSource;

    static {
        try {
            Context initContext = new InitialContext();
            Context context = (Context) initContext.lookup("java:/comp/env");
            dataSource = (DataSource) context.lookup("jdbc/assignment_db");
        } catch (NamingException e) {
            Logger.getLogger(DatabaseConnection.class.getName()).log(Level.SEVERE, null, e);
            throw new ExceptionInInitializerError("DataSource not initialized.");
        }
    }

    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
}

And methods in the following class (DAO) perform CRUD operations.

public final class CountryDao {

    public Long getCurrentRow(Long id) throws SQLException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            connection = DatabaseConnection.getConnection();
            preparedStatement = connection.prepareStatement("select rownum from (select @rownum:=@rownum+1 as rownum, tbl.country_id from country_tbl tbl, (select @rownum:=0)t order by tbl.country_id desc)t where country_id=?");
            preparedStatement.setLong(1, id);
            resultSet = preparedStatement.executeQuery();
            return resultSet.next() ? resultSet.getLong("rownum") : 1;
        } finally {
            if (connection != null) {connection.close();}
            if (resultSet != null) {resultSet.close();}
            if (preparedStatement != null) {preparedStatement.close();}
        }
    }

    public Long rowCount() throws SQLException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            connection = DatabaseConnection.getConnection();
            preparedStatement = connection.prepareStatement("select count(*) as cnt from country_tbl");
            resultSet = preparedStatement.executeQuery();
            resultSet.next();
            return resultSet.getLong("cnt");
        } finally {
            if (connection != null) {connection.close();}
            if (resultSet != null) {resultSet.close();}
            if (preparedStatement != null) {preparedStatement.close();}
        }
    }

    public List<CountryBean> getData(Long currentPage, Long pageSize) throws SQLException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        List<CountryBean> countryBeans = new ArrayList<CountryBean>();

        try {
            connection = DatabaseConnection.getConnection();
            preparedStatement = connection.prepareStatement("select * from country_tbl order by country_id desc limit ?,?");

            //preparedStatement.setMaxRows(pageSize);
            preparedStatement.setLong(1, currentPage);
            preparedStatement.setLong(2, pageSize);
            resultSet = preparedStatement.executeQuery();

            while (resultSet.next()) {
                CountryBean countryBean = new CountryBean();
                countryBean.setCountryId(resultSet.getLong("country_id"));
                countryBean.setCountryName(resultSet.getString("country_name"));
                countryBean.setCountryCode(resultSet.getString("country_code"));
                countryBeans.add(countryBean);
            }
        } finally {
            if (connection != null) {connection.close();}
            if (resultSet != null) {resultSet.close();}
            if (preparedStatement != null) {preparedStatement.close();}
        }

        return countryBeans;
    }

    public boolean delete(Long id) throws SQLException {
        boolean status = false;
        Connection connection = null;
        PreparedStatement preparedStatement = null;

        try {
            connection = DatabaseConnection.getConnection();
            preparedStatement = connection.prepareStatement("delete from country_tbl where country_id=?");
            preparedStatement.setLong(1, id);

            if (preparedStatement.executeUpdate() == 1) {
                status = true;
            }
        } finally {
            if (connection != null) {connection.close();}
            if (preparedStatement != null) {preparedStatement.close();}
        }
        return status;
    }

    public boolean delete(Long[] ids) throws SQLException {
        boolean status = false;
        Connection connection = null;
        PreparedStatement preparedStatement = null;

        try {
            connection = DatabaseConnection.getConnection();
            connection.setAutoCommit(false);
            preparedStatement = connection.prepareStatement("delete from country_tbl where country_id=?");
            int len = ids.length;

            for (int i = 0; i < len; i++) {
                preparedStatement.setLong(1, ids[i]);
                preparedStatement.addBatch();
            }

            preparedStatement.executeBatch();
            connection.commit();
            status = true;
        } finally {
            if (connection != null) {connection.close();}
            if (preparedStatement != null) {preparedStatement.close();}
        }
        return status;
    }

    public boolean insert(String countryName, String countryCode) throws SQLException {
        boolean status = false;
        Connection connection = null;
        PreparedStatement preparedStatement = null;

        try {
            connection = DatabaseConnection.getConnection();
            preparedStatement = connection.prepareStatement("insert into country_tbl(country_name, country_code)values(?,?)");
            preparedStatement.setString(1, countryName);
            preparedStatement.setString(2, countryCode);
            preparedStatement.executeUpdate();
            status = true;
        } finally {
            if (connection != null) {connection.close();}
            if (preparedStatement != null) {preparedStatement.close();}
        }
        return status;
    }

    public boolean update(Long countryId, String countryName, String countryCode) throws SQLException {
        boolean status = false;
        Connection connection = null;
        PreparedStatement preparedStatement = null;

        try {
            connection = DatabaseConnection.getConnection();
            preparedStatement = connection.prepareStatement("update country_tbl set country_name=?, country_code=? where country_id=?");
            preparedStatement.setString(1, countryName);
            preparedStatement.setString(2, countryCode);
            preparedStatement.setLong(3, countryId);
            preparedStatement.executeUpdate();
            status = true;
        } finally {
            if (connection != null) {connection.close();}
            if (preparedStatement != null) {preparedStatement.close();}
        }

        return status;
    }
}

These methods are called appropriately from a Servlet after validation is performed. The Servlet in turn, interacts with a JSP (along with JSTL/EL).

There is only one question. Curretly, connection, preparedStatement and resultSet are all local variables to specific methods.

Can I declare them only at one place as class members (instance variables)? Can doing so maintain a consistent state precisely?

There is no need concentrate much on the core logic. Please don't just say, It is better to use an MVC framework :)

Was it helpful?

Solution

Can I declare them only at one place as class members (instance variables)?

You can do so, but the class would be threadunsafe. The caller can't reuse the same instance across multiple threads without disturbing the behavior of each individual method caused by a inconsistent state. In case of a servlet as the caller, this way you can't create the instance only once in servlet's init() and reuse it multiple times in the doXxx() methods. You would be forced to re-create the instance in threadlocal scope (so, inside the doXxx() method). This should be clearly documented in the class' javadoc. But, after all, designing a threadunsafe DAO class doesn't make sense. Stick to the current design (or, if you weren't just hobbying around, switch to JPA ;) ).


Can doing so maintain a consistent state precisely?

No! On the contrary, it would be inconsistent. You can't share the one and same statement or resultset across multiple queries. Each query should have its own statement and resultset. Every method call on the instance would change the instance variables, causing the other still-running method calls to work on a corrupted state. Sharing a connection is possible, but this job is normally already done by a connection pool. Given that you're using a datasource, you most likely already have one.


That said, if you hate the repeated code boilerplate but really want to stick to good 'ol JDBC API, look at Execute Around pattern/idiom and/or Java 7's automatic resource management (ARM). With this, it must be possible to create a helper DB class with a bunch of interfaces and end up with a generic abstract base DAO class whose methods only take a SQL query and the parameter values -if any.

OTHER TIPS

You can't declare these variables as instance variables. because this may cause errors in some conditions.

E.g -

If you call two methods of this class on the same object. One method takes two minutes for execution and the other method takes 5 minutes In this case, the first method closes the connection and the 2nd method throws an exception. There have many other reasons ...

Ofcourse you can use connection, statement and resultset as member variable as long as you are not sharing the same objects in two or more different threads.

As in the servlet you will be creating an object of the dao so each time the servlet is being hit a new thread starts and this new thread is going to create a new object of dao to perform some method.

There is only case where it can fail is in the servlet you are creating an object of dao and passing this same object to two different threads manually created by you to execute two methods. in such case it might be possible one thread is reading all the records from the result set but right at the same time other thread executed the get query and trying to reset the result set, which will lead to exception.

Even more funny as stated by @Manjeet first thread is still reading the data from result set and second thread just closed the connection. But he forget to mention it will happen only if you are creating two threads. if you will execute two method in same thread that it will be one after another for sure. as there is no other way to execute two method simultaneously without creating two thread.

However if you intention is to just remove the boilerplate code then you have several otherways. You can create a class called boilerplate like this

class Boilerplate{
  private Connection con;
  private PreparedStatement ps;

  public Boilerplate(String query){
   //initialize connection, ps and resultset
   con=DatabaseConnection.getConnection();
   ps=connection.prepareStatement(query);
  }

  destroy void Boilerplate(){
    if(con!=null)
      con.close()
    if(ps!=null)
      ps.close();
  }
}

then get an instance of it do your work and call it's destroy method.

General things:

As Knowing the terminology is important. Instance variables and class variables are both member variables. They are both member variables because they are both associated with a specific class. But, there are differences between instance variables and class variables.

Instance variables

Instance variables belong to an instance of a class. Another way of saying that is instance variables belong to an object, since an object is an instance of a class. Every object has it’s own copy of the instance variables. Here is what a declaration of an instance variable would look like:

instance variable

class Taxes
    {
      int count;
      /*...*/
    }

Class variables

  class Taxes
    {
      static int count;
      /*...*/
    }
res.setContentType("text/html");

// Ask for a 16K byte response buffer; do not set the content length
res.setBufferSize(16 * 1024);

PrintWriter out = res.getWriter();
out.println("<HTML>");
out.println("<HEAD><TITLE>Hello World</TITLE></HEAD>");
out.println("<BODY>");
out.println("<BIG>Less than 16K of response body</BIG>");
out.println("</BODY></HTML>");

I use your example and want to create a connection. But it shows me a NoInitialContextException when I use junit to test it. How do I do to fix it? Here's my code below.

public final class DataSourceUtil {                     
  private static Context initCtx;
  private static Context envCtx; 
  private static DataSource dataSource;                 

  static {
    try {                                               
      // Initial a datasource for pooled connections.    
      initCtx = new InitialContext();                   
      envCtx = (Context) initCtx.lookup("java:/comp/env");     
      dataSource = (DataSource) envCtx.lookup("jdbc/ServletDB");                                                 
    } catch (NamingException e) {                       
      Logger.getLogger(DataSourceUtil.class.getName()).log(Level.SEVERE, null, e);                               
      throw new ExceptionInInitializerError("DataSource not initialized.");
  }                                                   
}                                           

  public static Connection getConnection() throws SQLException {
    return dataSource.getConnection();
  }
}

And the Test code

public class ConnectionTest() {
  @Test
  public void connectionTest2() {
  try {

    Connection connection = DataSourceUtil.getConnection();

  } catch (Exception e) {e.printStackTrace();}
}

and the error message:

Dec 23, 2015 9:06:46 PM com.wxz.server.dao.DataSourceUtil <clinit>
SEVERE: null
javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file:  java.naming.factory.initial

You can use this java class and create objects and call this st,con and rs respectively

{
    import java.sql.*;

    public class connectDb {

        public Statement st;
        public Connection con;
        public ResultSet rs;

        public ResultSet rs1;
        public Statement st1;

        public connectDb() {

            try {

                String driver="com.mysql.jdbc.Driver";
                String url="jdbc:mysql://localhost:3306/test1db";
                String username="root";
                String password="";
                Class.forName(driver);


                Connection con=DriverManager.getConnection(url,username,password);
                System.out.println("connected to database");
                 st=con.createStatement();
                st1=con.createStatement();


          }catch(Exception e) {
              System.out.println("ERROR" +e);
          }
      }
    }
}
FlightHandler.java
package com.trainee.handler;
import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.trainee.dao.FlightDao;
import com.trainee.flight.Flight;

public class FlightHandler extends HttpServlet {
       private static String INSERT = "/user.jsp";
        private static String Edit = "/edit.jsp";
        private static String FlightRecord = "/listFlight.jsp";
        private FlightDao dao;

        public FlightHandler() {
            super();
            dao = new FlightDao();
        }

        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            String redirect="";
            String Id = request.getParameter("id"); 
            String TotalSeat =  request.getParameter("totalseat");
            String action = request.getParameter("action");
            if(!((Id)== null) && action.equalsIgnoreCase("insert"))
            {
                int id = Integer.parseInt(Id);
                Flight f = new Flight();
                f.setId(id);
                f.setName(request.getParameter("name"));
                int totalseat = Integer.parseInt(TotalSeat);
                f.setTotalSeat(totalseat);
                f.setCity(request.getParameter("city"));
                f.setStatus(request.getParameter("status"));
                dao.addFlight(f);
                redirect = FlightRecord;
                request.setAttribute("users", dao.getAllFlight());    
                System.out.println("Record Added Successfully");
            }



            else if (action.equalsIgnoreCase("delete")){
                String id = request.getParameter("flightId");
                int fid = Integer.parseInt(id);
                dao.removeFlight(fid);
                redirect = FlightRecord;
                request.setAttribute("users", dao.getAllFlight());
                System.out.println("Record Deleted Successfully");
            }

            else if (action.equalsIgnoreCase("editform")){          
                redirect = Edit;            
            } else if (action.equalsIgnoreCase("edit")){
                String fId = request.getParameter("flightId");
                int id = Integer.parseInt(fId);            
                Flight f = new Flight();
                f.setId(id);
                f.setCity(request.getParameter("city"));

                dao.editFlight(f);
                request.setAttribute("user", f);
                redirect = FlightRecord;
                System.out.println("Record updated Successfully");

            }
            else if (action.equalsIgnoreCase("listFlight")){
                redirect = FlightRecord;
                request.setAttribute("users", dao.getAllFlight());
             } else {
                redirect = INSERT;
            }

            RequestDispatcher rd = request.getRequestDispatcher(redirect);
            rd.forward(request, response);
        }

        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top