Struts2 Autocomplete with Database using jQuery UI
This tutorial implements a live autocomplete search box in a Struts2 application backed by a MySQL database. When the user types, jQuery UI Autocomplete sends an AJAX request to a Struts2 action that queries the database and returns matching suggestions as JSON. This is the modern approach — the original <s:autocompleter> tag used the deprecated Dojo-based Struts2 AJAX plugin and should no longer be used.
How It Works
User types 3+ characters
↓
jQuery UI Autocomplete sends GET /suggest?term=Mah
↓
Struts2 SuggestAction.execute()
↓
DAO: SELECT name FROM state WHERE name LIKE 'Mah%'
↓
Action serializes List<String> to JSON
↓
jQuery UI renders dropdown of matching suggestions
MySQL Table
CREATE TABLE state (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
INSERT INTO state (name) VALUES
('Andhra Pradesh'), ('Arunachal Pradesh'), ('Assam'),
('Bihar'), ('Chhattisgarh'), ('Goa'), ('Gujarat'),
('Haryana'), ('Himachal Pradesh'), ('Jharkhand'),
('Karnataka'), ('Kerala'), ('Madhya Pradesh'),
('Maharashtra'), ('Manipur'), ('Meghalaya'), ('Mizoram'),
('Nagaland'), ('Odisha'), ('Punjab'), ('Rajasthan'),
('Sikkim'), ('Tamil Nadu'), ('Telangana'), ('Tripura'),
('Uttar Pradesh'), ('Uttarakhand'), ('West Bengal');
Maven Dependencies (pom.xml)
<dependencies>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.5.33</version>
</dependency>
<!-- struts2-json-plugin converts action fields to JSON automatically -->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-json-plugin</artifactId>
<version>2.5.33</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.3.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
StateDAO.java
package com.java9r.dao;
import com.java9r.db.DBConnection;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class StateDAO {
public List<String> searchByPrefix(String prefix) {
List<String> results = new ArrayList<>();
String sql = "SELECT name FROM state WHERE name LIKE ? ORDER BY name LIMIT 10";
try (Connection con = DBConnection.getConnection();
PreparedStatement ps = con.prepareStatement(sql)) {
ps.setString(1, prefix + "%"); // starts-with match
ResultSet rs = ps.executeQuery();
while (rs.next()) {
results.add(rs.getString("name"));
}
} catch (SQLException e) {
e.printStackTrace();
}
return results;
}
}
SuggestAction.java
The action extends ActionSupport and returns a List<String> field. The JSON plugin serializes all getter fields to JSON automatically — so getSuggestions() becomes {"suggestions":["Maharashtra","Manipur"]}.
package com.java9r.action;
import com.java9r.dao.StateDAO;
import com.opensymphony.xwork2.ActionSupport;
import java.util.Collections;
import java.util.List;
public class SuggestAction extends ActionSupport {
private static final long serialVersionUID = 1L;
private String term; // jQuery UI sends "term" as the parameter name
private List<String> suggestions = Collections.emptyList();
private final StateDAO dao = new StateDAO();
@Override
public String execute() {
if (term != null && term.length() >= 1) {
suggestions = dao.searchByPrefix(term.trim());
}
return SUCCESS;
}
public String getTerm() { return term; }
public List<String> getSuggestions() { return suggestions; }
public void setTerm(String term) { this.term = term; }
}
struts.xml
The suggest action uses the json package (from struts2-json-plugin) instead of struts-default. The type="json" result serializes the action's fields to a JSON response.
<?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="default" namespace="/" extends="struts-default">
<!-- Main page -->
<action name="index">
<result>/index.jsp</result>
</action>
</package>
<!-- JSON package — extends json-default, not struts-default -->
<package name="json" namespace="/" extends="json-default">
<action name="suggest"
class="com.java9r.action.SuggestAction">
<result type="json">
<!-- Only serialize the "suggestions" field — skip other getters -->
<param name="includeProperties">suggestions.*</param>
</result>
</action>
</package>
</struts>
index.jsp – Autocomplete Page
jQuery UI Autocomplete uses the source option with a function that receives the typed term and a response callback. The function calls the Struts2 action via AJAX and passes the matching suggestions to the callback.
<%@ page contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>State Autocomplete</title>
<!-- jQuery UI CSS -->
<link rel="stylesheet"
href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css">
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
</head>
<body>
<div class="container mt-5">
<h3>Struts2 Autocomplete – Select a State</h3>
<hr>
<div class="col-md-4">
<label for="stateName">State Name:</label>
<input type="text" id="stateName" class="form-control"
placeholder="Start typing...">
<div class="mt-3" id="selectedValue"></div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"></script>
<script>
$('#stateName').autocomplete({
minLength: 1, // start suggesting after 1 character
// source: function — called on each keystroke
source: function (request, response) {
$.ajax({
url: 'suggest',
dataType: 'json',
data: { term: request.term }, // "term" is the typed text
success: function (data) {
// data.suggestions is the List<String> from SuggestAction
response(data.suggestions);
},
error: function () {
response([]); // show nothing if request fails
}
});
},
// Called when user selects an item from the dropdown
select: function (event, ui) {
$('#selectedValue').html('Selected: <strong>' + ui.item.value + '</strong>');
}
});
</script>
</body>
</html>
How the JSON Response Looks
// GET /suggest?term=Mah
// Response:
{
"suggestions": [
"Madhya Pradesh",
"Maharashtra"
]
}
jQuery UI Autocomplete accepts a plain array of strings for its response() callback. data.suggestions is exactly that — the serialized List<String> from the action.
Old Approach vs New Approach
| Feature | Old: <s:autocompleter> |
New: jQuery UI + JSON action |
|---|---|---|
| Plugin required | struts2-dojo-plugin (deprecated, unmaintained) | struts2-json-plugin (maintained) |
| UI library | Dojo Toolkit (very old) | jQuery UI (widely used, documented) |
| Customization | Limited — only Dojo widget options | Full control — any jQuery UI option |
| Response format | Struts2 proprietary | Standard JSON |
| Still works in Struts2 2.5+ | No — dojo plugin removed from core | Yes |
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Summary
Struts2 autocomplete with a database has two parts: a Struts2 action that uses the JSON plugin to return a List<String> as JSON, and a jQuery UI Autocomplete widget on the front end that calls that action via AJAX and renders the dropdown. The term parameter is sent automatically by jQuery UI — the action simply reads it, queries the database with a LIKE prefix% search using PreparedStatement, and returns the results. The includeProperties parameter in struts.xml restricts the JSON output to only the suggestions field, avoiding the serialization of unintended action fields.
Comments