in Ajax, EJB 3.0, JPQL, jQuery, Struts 2

Autocomplete with jQuery, Struts 2, EJB 3 and JPQL

If you are looking for a way to fill a field and perform a live search, in an asynchronous way, look no further.
jQuery autocompleter is a jQuery plugin that performs real-time autocompletion of a text-field.

You need jQuery and the jQuery plugin autocomplete of course.

Let’s suppose we have a text field where the user needs to enter the number of a person id. If the user types 1, all ids starting with 1 will show up. If the user types 12, all ids starting with 12 will show up.

Here is the code I wrote to asynchronously call a method that will perform a JPQL request in a database whenever the user types a digit in the text field.

First, the main JSP, search.jsp :

<%-- jQuery Libraries--%>

<script type="text/javascript" src="ressources/js/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="ressources/js/jquery-autocomplete/lib/jquery.bgiframe.min.js"></script>
<script type="text/javascript" src="ressources/js/jquery-autocomplete/lib/jquery.ajaxQueue.js"></script>
<script type="text/javascript" src="ressources/js/jquery-autocomplete/lib/thickbox-compressed.js"></script>
<script type="text/javascript" src="ressources/js/jquery-autocomplete/jquery.autocomplete.js"></script>

<link href="ressources/js/jquery-autocomplete/jquery.autocomplete.css" rel="stylesheet" type="text/css" />

<script type="text/javascript">
$().ready(function() {
$("#tag").autocomplete('myWebApp/listPersonsId.action', {
minChars: 1,
max: 15,
multiple: false,
autoFill: true
});
});
</script>

...

<td><s:textfield id='tag' name="tagField" label="tag" /></td>

Then the second JSP, tag.jsp, that will hold the content of the list that will show beneath the text field :

tag.jsp :
<%@ taglib prefix="s" uri="/struts-tags"%>
<s:iterator value="listPersons">
 <s:property value="id" escape="false"/>
</s:iterator>

The struts mapping file search.xml :

 <!--  This action is used to autocomplete the textfield person id -->
 <action name="listPersonsId" method="findTag">
      <result name="success">/pages/search/tag.jsp</result>
 </action>

I have a Person entity bean (with an ID field) and a stateless session bean PersonDAO with a allPersonsById(long PersonId) method :

@SuppressWarnings("unchecked")
 public List<Person> allPersonsById(long PersonId) {
 String param =  String.valueOf(PersonId);
 param = PersonId + "%";
 Query reqPosition = entityManager.createQuery("select p from Person p where TRIM(p.id) like ?1");
 reqPosition.setParameter(1, param);
 List<Person> listPersons = reqPosition.getResultList();
 return listPersons;
 }

Pay special attention to the JPQL request. I have to use the TRIM function because the column ID is a number while the parameter is a String.
Different types throw an exception. TRIM avoids that exception.

Finally here is the Struts 2 action, SearchAction.java:


public class SearchAction extends ActionSupport {
 //q is a parameter used by the jQuery autocomplete.js script
 private String q;

 public String getQ() {
 return q;
 }

 public void setQ(String q) {
 this.q = q;
 }

// This list will be updated every time the user enters a new digit
 public List<Person> listPersons;

 public List<Person> getListPersons() {
 return listPersons;
 }

 public void setListPersons(List<Person> listPersons) {
 this.listPersons = listPersons;
 }

 public String findTag()  throws Exception {

 String qUTF8 = "";
 if (null !=q ){
 try {
 qUTF8 = new String(q.getBytes(), "UTF8");
 } catch (UnsupportedEncodingException e) {
 log.error(e.getMessage(), e);
 }
 }

 PersonDaoLocal facade = (PersonDaoLocal) ServiceLocator.getInstance().getService("myWebApp/PersonDao/local");
 long personId = Long.valueOf(q);;
 listPersons = facade.allPersonsById(personId);
 return SUCCESS;
 }
 }

When a user starts typing in the input box, the autocompleter will request myWebApp/listPersonsId.action with a GET parameter named q that contains the current value of the input box.

Check the plugin documentation for more options.