Java Struts2

Struts2 – Fetch Records Without Page Refresh Using jQuery AJAX

Struts2 – Fetch Records Without Page Refresh Using jQuery AJAX

This tutorial shows how to load data from a MySQL database into a Struts2 page without reloading — using jQuery AJAX. The user types a name in a search box; jQuery sends an AJAX GET request to a Struts2 action, which queries the database and returns an HTML fragment; jQuery inserts that fragment into a <div> on the current page without any full-page reload.

How It Works

User types in search box
    ↓
jQuery $.get("searchStudent?name=Alice")
    ↓
Struts2 action: StudentSearchAction.execute()
    ↓
DAO queries MySQL with PreparedStatement
    ↓
Results stored in action field
    ↓
Struts2 forwards to studentResults.jsp (HTML fragment only — no <html> or <body>)
    ↓
jQuery receives HTML string and inserts it into #results div

MySQL Table

CREATE TABLE student (
    id    BIGINT       AUTO_INCREMENT PRIMARY KEY,
    name  VARCHAR(255),
    class VARCHAR(255)
);

INSERT INTO student (name, class) VALUES
    ('Alice',   'Class A'),
    ('Bob',     'Class B'),
    ('Arun',    'Class A'),
    ('Charlie', 'Class C');

StudentBean.java

package com.java9r.model;

public class StudentBean {
    private long   id;
    private String name;
    private String studentClass;

    public long   getId()           { return id;           }
    public String getName()         { return name;         }
    public String getStudentClass() { return studentClass; }

    public void setId(long id)                       { this.id = id;                     }
    public void setName(String name)                 { this.name = name;                 }
    public void setStudentClass(String studentClass) { this.studentClass = studentClass; }
}

StudentDAO.java

The original post used string concatenation in SQL — a SQL injection vulnerability. This rewrite uses PreparedStatement with a LIKE pattern so partial name matches work safely.

package com.java9r.dao;

import com.java9r.db.DBConnection;
import com.java9r.model.StudentBean;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class StudentDAO {

    public List<StudentBean> searchByName(String keyword) {
        List<StudentBean> list = new ArrayList<>();

        // LIKE search — % wildcard on both sides for partial match
        String sql = "SELECT id, name, class FROM student WHERE name LIKE ? ORDER BY name";

        try (Connection con = DBConnection.getConnection();
             PreparedStatement ps = con.prepareStatement(sql)) {

            ps.setString(1, "%" + keyword + "%");   // safe — no SQL injection
            ResultSet rs = ps.executeQuery();

            while (rs.next()) {
                StudentBean s = new StudentBean();
                s.setId(rs.getLong("id"));
                s.setName(rs.getString("name"));
                s.setStudentClass(rs.getString("class"));
                list.add(s);
            }

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

StudentSearchAction.java

package com.java9r.action;

import com.java9r.dao.StudentDAO;
import com.java9r.model.StudentBean;
import com.opensymphony.xwork2.ActionSupport;

import java.util.Collections;
import java.util.List;

public class StudentSearchAction extends ActionSupport {

    private static final long serialVersionUID = 1L;

    private String            keyword;           // bound from ?keyword=... request param
    private List<StudentBean> students = Collections.emptyList();

    private final StudentDAO dao = new StudentDAO();

    @Override
    public String execute() {
        if (keyword != null && !keyword.trim().isEmpty()) {
            students = dao.searchByName(keyword.trim());
        }
        return SUCCESS;
    }

    public String  getKeyword()  { return keyword;  }
    public void setKeyword(String keyword) { this.keyword = keyword; }

    public List<StudentBean> getStudents() { return students; }
}

struts.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
    "http://struts.apache.org/dtds/struts-2.5.dtd">

<struts>
    <constant name="struts.devMode" value="false"/>

    <package name="student" namespace="/" extends="struts-default">

        <!-- Main search page -->
        <action name="index"
                class="com.java9r.action.StudentSearchAction">
            <result name="success">/index.jsp</result>
        </action>

        <!-- AJAX endpoint — returns an HTML fragment, not a full page -->
        <action name="searchStudent"
                class="com.java9r.action.StudentSearchAction">
            <result name="success">/studentResults.jsp</result>
        </action>

    </package>
</struts>

index.jsp – Search Page with jQuery AJAX

<%@ page contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <title>Student Search</title>
    <link rel="stylesheet"
          href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
</head>
<body>
<div class="container mt-4">

    <h3>Student Search (no page reload)</h3>
    <hr>

    <div class="col-md-4 mb-3">
        <label for="searchBox">Search by Name:</label>
        <input type="text" id="searchBox" class="form-control"
               placeholder="Type a name...">
    </div>

    <!-- AJAX results are injected here -->
    <div id="results"></div>

</div>

<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
    // Fire AJAX on every keystroke (debounced to avoid hammering the server)
    var searchTimer;

    $('#searchBox').on('input', function () {
        clearTimeout(searchTimer);
        var keyword = $(this).val().trim();

        if (keyword.length === 0) {
            $('#results').empty();
            return;
        }

        // Wait 300ms after the user stops typing before sending the request
        searchTimer = setTimeout(function () {
            $.get('searchStudent', { keyword: keyword })
                .done(function (html) {
                    $('#results').html(html);
                })
                .fail(function () {
                    $('#results').html('<p class="text-danger">Error loading results.</p>');
                });
        }, 300);
    });
</script>
</body>
</html>

studentResults.jsp – HTML Fragment (no full page tags)

This JSP is returned as the AJAX response body. It must NOT include <html>, <head>, or <body> tags — jQuery will insert it directly as inner HTML of the #results div.

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<c:choose>
    <c:when test="${empty students}">
        <p class="text-muted">No students found.</p>
    </c:when>
    <c:otherwise>
        <table class="table table-bordered table-sm">
            <thead class="table-primary">
                <tr>
                    <th>ID</th>
                    <th>Name</th>
                    <th>Class</th>
                </tr>
            </thead>
            <tbody>
            <c:forEach var="s" items="${students}">
                <tr>
                    <td>${s.id}</td>
                    <td>${s.name}</td>
                    <td>${s.studentClass}</td>
                </tr>
            </c:forEach>
            </tbody>
        </table>
        <small class="text-muted">${students.size()} result(s) found.</small>
    </c:otherwise>
</c:choose>

How the AJAX Flow Works Step by Step

Step What happens
1. User types "Ali" jQuery input event fires after 300ms debounce
2. $.get('searchStudent', {keyword:'Ali'}) GET request to /searchStudent?keyword=Ali
3. Struts2 routes to StudentSearchAction.execute() Struts2 binds keyword="Ali" via setKeyword()
4. DAO runs SELECT ... WHERE name LIKE '%Ali%' Returns list of matching students
5. Action returns SUCCESS → studentResults.jsp JSP renders an HTML table fragment
6. jQuery receives HTML string $('#results').html(html) inserts it into the page

Original Code Issues Fixed in This Rewrite

Original Fixed
SQL injection: "WHERE name='" + id + "'" PreparedStatement with ps.setString(1, "%" + keyword + "%")
Raw XMLHttpRequest (verbose, IE-era code) jQuery $.get() with debounce — cleaner and cross-browser
Scriptlet code in JSP (<% %>) JSTL <c:forEach> and EL ${s.name}
Exact match only (WHERE name='...') Partial match with LIKE %keyword%
No debounce — request on every character 300ms debounce — waits for user to pause typing

Summary

Struts2 AJAX works by creating a separate action mapping that returns an HTML fragment JSP instead of a full page. jQuery's $.get() calls that URL, receives the HTML string in the callback, and injects it into the DOM with .html(). The key rule: the response JSP must be a fragment (no <html>/<body> tags), since it is embedded inside the already-loaded page. A debounce timeout prevents a database query on every single keystroke, making the search responsive without overloading the server.

Topics: Java Struts2
← Newer Post Older Post →

Comments

https://www.blogger.com/comment/frame/6690124484600543990?po=3111327976678602529&hl=en&saa=85391&origin=https://www.java9r.com