Search code examples
javamysqljspweb-applicationsdata-entry

Prevent Multiple Submit Button Clicks Without Using JavaScript or JQuery in Web App?


My simple web-app is as follows: I have a JSP form (MyForm.JSP) that takes user input and passes it to my first servlet ("/myfirstservlet").

This servlet handles the SQL insert of the user input values into my Fruits table and then redirects the user to my results servlet ("/results").

My results servlet then checks for the an "ADD" parameter and if "true" (i.e. it equals "success") it finally redirects the user to my Results JSP (Results.JSP) which is stored inside the path: WEB-INF/MyFolder/Results.jsp.

My JSP form (MyForm.JSP) is also stored inside the path: WEB-INF/MyFolder/MyForm.jsp

I did this way to prevent the user from re-submitting the form by clicking the refresh button on the Results JSP page so as to avoid having multiple entries of the same data that was just previously entered into the database.

My issue now is: How do I prevent the user from clicking the submit button multiple times on my form (MyForm.JSP) thereby preventing multiple rows of the same data getting entered into my database WITHOUT using JavaScript or JQuery?

Basically, I want to validate in my server, not client-side, that the form has been submitted only ONCE.

My JSP form (MyForm.JSP):

<form action="myfirstservlet" do="POST">
   <input type="text" name="fruit"><br>
   <input type="text" name="color"><br>
   <input type="submit" value="Submit">
</form>

My first servlet ("/myfirstservlet"):

protected void doPost(...){
   String fruit = request.getParameter("fruit");
   String color = request.getParameter("color");

   String sql = "INSERT INTO fruits (fruit, color) VALUES" + "(\"" + fruit +  "\", \""  + color +  "\");";

   utilitySQL.sqlInsert(sql); // My utility class that handles sql inserts

   response.sendRedirect("results?ADD=SUCCESS");
}

My results servlet ("/results"):

protected void doPost(...){

   response.setContentType("text/html");    

   if (request.getParameter("ADD").equals("SUCCESS"))
      request.getRequestDispatcher("WEB-INF/MyFolder/Results.jsp").forward(request, response);

}

My Results JSP (Results.JSP):

<body>
<h1>Results JSP</h1>


  //Reads data from MySQL database and prints it as an Array List.

</body>

EDIT: Fixed my prepared statemnt in my first servlet:

protected void doPost(...){


       String fruit = request.getParameter("fruit");
       String color = request.getParameter("color");

       try 
        {
            String sql2 = "INSERT INTO practice (fruit, color) VALUES (?, ?);";
            Connection connect = SQLHelperClass.connectOnly();
            PreparedStatement pstmt;
            pstmt = connect.prepareStatement(sql2);
            pstmt.setString(1, fruit);
            pstmt.setString(2, color);

            pstmt.execute();

            response.sendRedirect("results?ADD=success");
        } 

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

}

Solution

  • If you had an id field for a logged in user, this would be easier because you could just create a table for the results a specific user has submitted and before entering it into the fruits table, check whether the user has already submitted the same data.

    From the looks of it, it doesn't look like you have any user identification fields, so a hacky way of preventing duplicates might be to make use of sessions.

    A session is unique to the user who is currently using your application/website. Each person who visits your website/application gets their own unique session id. (they are stored as a cookie)

    So for example:

    protected void doPost(...){
       String fruit = request.getParameter("fruit");
       String color = request.getParameter("color");
    
       //unless you wanna complicate things, i would create a string out of the two parameters and store it into an arraylist of strings
       String value = fruit+color; 
    
       HttpSession session = (request.getSession()); //get session
       if(null == session.getAttribute("duplicates")){ //if session variable empty then we know that user has not submitted anything yet so we let them insert into db
    
         insertFruit(fruit,color); //add to db
    
         ArrayList<String> duplicates = new ArrayList<String>(); //create arraylist
         duplicates.add(value); //add our unique value
         session.setAttribute("duplicates", duplicates); //set as session variable
    
        }else{
         //here the session variable is not empty so that means the user has already submitted something so lets check the arraylist and make sure the value does not already exist
    
         ArrayList<String> duplicates = (ArrayList<String>) session.getAttribute("duplicates");
    
         if(!duplicates.contains(value)){
          //if arraylist does not contain the same value, then it's safe to add
           insertFruit(fruit,color); //add to db
    
          //forgot this part
          duplicates.add(value);
          session.setAttribute("duplicates", duplicates); //update the variable
         }
    
    
        }
    
    
       response.sendRedirect("results?ADD=SUCCESS");
    }
    
    public void insertFruit(String fruit, String color){
    
           try(Connection connect = SQLHelperClass.connectOnly()){
             PreparedStatement pst = connect.prepareStatement("INSERT INTO practice (fruit, color) VALUES (?, ?);");
    
            pst.setString(1, fruit);
            pst.setString(2, color);
    
            pst.executeUpdate();
    
              }catch (SQLException e) {
                e.printStackTrace();
              }
    
    }
    

    EDIT 1:

    In regards to the comment about not repeating the database operation for each servlet. You need to separate out the logic. The way people normally do it is by creating a seperate class for all your database operations.

    For example...

    create a class called FruitDao, here you keep all your fruit related database operations

    public class FruitDao{

    public void insertFruit(String fruit, String color){
    
           try(Connection connect = SQLHelperClass.connectOnly()){
             PreparedStatement pst = connect.prepareStatement("INSERT INTO practice (fruit, color) VALUES (?, ?);");
    
            pst.setString(1, fruit);
            pst.setString(2, color);
    
            pst.executeUpdate();
    
              }catch (SQLException e) {
                e.printStackTrace();
              }
    
    }
    

    To call this from your servlet just do:

    protected void doPost(...){
       FruitDao fdao = new FruitDao(); // get the db class for fruits
       String fruit = request.getParameter("fruit");
       String color = request.getParameter("color");
    
       //unless you wanna complicate things, i would create a string out of the two parameters and store it into an arraylist of strings
       String value = fruit+color; 
    
       HttpSession session = (request.getSession()); //get session
       if(null == session.getAttribute("duplicates")){ //if session variable empty then we know that user has not submitted anything yet so we let them insert into db
    
         fdao.insertFruit(fruit,color); //add to db
    
         ArrayList<String> duplicates = new ArrayList<String>(); //create arraylist
         duplicates.add(value); //add our unique value
         session.setAttribute("duplicates", duplicates); //set as session variable
    
        }else{
         //here the session variable is not empty so that means the user has already submitted something so lets check the arraylist and make sure the value does not already exist
    
         ArrayList<String> duplicates = (ArrayList<String>) session.getAttribute("duplicates");
    
         if(!duplicates.contains(value)){
          //if arraylist does not contain the same value, then it's safe to add
            fdao.insertFruit(fruit,color); //add to db
    
          //forgot this part
          duplicates.add(value);
          session.setAttribute("duplicates", duplicates); //update the variable
         }
    
    
        }
    
    
       response.sendRedirect("results?ADD=SUCCESS");
    }