title: JQuery DataTables with ServerSide Processing in Spring (with Java and Thymeleaf)
body: <p>I have a Spring Boot application using Java and Thymeleaf. In it, I have a page with a JQuery DataTable. This table has thousands of rows in it, and so while I currently just put it all on the page and let JQuery handle it entirely, I want to switch to using ServerSide processing for the paging, sorting, etc.</p>
  
  <p>I am set up to do this on the back-end, where I can pass a start and end row number and sort information dynamically to my SQL query. However, my issue is that I just can't figure out exactly how DataTables is trying to notify me via controller when the user clicks the 'next page' button, or a sort button. <strong>Where/how can my controller pick up this information, so that I can plug it into my SQL query and return what is necessary?</strong></p>
  
  <p>So, lets say I have an example <strong>object</strong>, like a "Person".</p>
  
  <pre><code>public class Person {
  
      private static String PERSON_IMAGE = "&lt;img th:src=\"@{/images/personimage.png}\" alt=\"icon\"/&gt;";
      private static String PERSON_IMAGE_2 = "&lt;img th:src=\"@{/images/personimage2.png}\" alt=\"icon\"/&gt;";
  
      private String name;
      private String socialSecurity;
      private String birthdate;
      private String gender;
      private String personImage;
  
      public String getName() {
          return name;
      }
  
      public void setName(String name) {
          this.name = name;
      }
  
      public String getSocialSecurity() {
          return socialSecurity;
      }
  
      public void setSocialSecurity(String socialSecurity) {
          this.socialSecurity = socialSecurity;
      }
  
      public String getBirthdate() {
          return birthdate;
      }
  
      public void setBirthdate(String birthdate) {
          this.birthdate = birthdate;
      }
  
      public String getGender() {
          return gender;
      }
  
      public void setGender(String gender) {
          this.gender = gender;
      }
  
      public String getPersonImage() {
          if(null != birthdate) {
              return PERSON_IMAGE;
          } else {
              return PERSON_IMAGE_2;
          }
  
      }
  }
  </code></pre>
  
  <p>So, on my Thymeleaf <strong>HTML</strong> page, I used to have the following: <strong>(Version 1)</strong></p>
  
  <pre><code>&lt;table id="myDataTable" class="dataTable display compact"&gt;
      &lt;thead&gt;
          &lt;tr&gt;
              &lt;th&gt;Name&lt;/th&gt;
              &lt;th&gt;Social Security&lt;/th&gt;                                        
              &lt;th&gt;Birthdate&lt;/th&gt;
              &lt;th&gt;Gender&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr th:each="person : ${peopleList}"&gt;
              &lt;td th:attr="data-order=${person.name}"&gt;&lt;a th:href="|javascript:openPersonDetail('${person.socialSecurity}');|"&gt;&lt;span th:text="${person.name}"&gt;name&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
              &lt;td th:text="${person.socialSecurity}"&gt;socialSecurity&lt;/td&gt;                      
              &lt;td class="dt-body-center" th:text="${person.birthdate}"&gt;birthdate&lt;/td&gt;
              &lt;td class="dt-body-center" th:text="${person.gender}"&gt;gender&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
  &lt;/table&gt;
  </code></pre>
  
  <p>My page <strong>controller</strong> was very simple, and just passed in a full list of people, as follows: <strong>(Version 1)</strong></p>
  
  <pre><code>@GetMapping("/ApplicationName/MyDataTablePage")
  public String myDataTablePage(Model model) {
  
      SearchCriteria searchCriteria = new SearchCriteria();
      searchCriteria.setOrderByString("name");
      searchCriteria.setUsePaging(false);
      searchCriteria.setFIRST_NUMBER(null);
      searchCriteria.setLAST_NUMBER(null);
  
      model.addAttribute("peopleList", myMapperService.getPeople(searchCriteria)); //returns an ArrayList&lt;Person&gt;
  
      return "/pages/MyDataTablePage";
  }
  </code></pre>
  
  <p>And finally, my Javascript used to have the following: <strong>(Version 1)</strong></p>
  
  <pre><code>$(document).ready(function() {
      $('#myDataTable').DataTable({
          "destroy" : true,
          "scrollY" : 300,
          "scrollCollapse" : true,
          "paging" : true,
          "autoWidth" : true,
          "ordering" : true,
          "searching" : false,
          "order" : [ [ 0, 'asc' ] ],
          "pageLength" : 20,
          "lengthChange" : false,
          "pagingType" : "full_numbers",
          "dom" : '&lt;"top"ip&gt;rt&lt;"bottom"fl&gt;&lt;"clear"&gt;'
      });
  });
  </code></pre>
  
  <p>This worked, but I wanted to switch the DataTable to use serverside processing, and so the first step was to (1) change the main page controller to stop appending the list of People to the model, (2) to create a new controller which would return my ArrayList as JSON for the DataTable to call by itself, (3) to change the html of the main page to no longer provide any <em>tbody</em> tag to the DataTable, and (4) to change the javascript where the table is created to put the data in by itself.</p>
  
  <p>=============================================================</p>
  
  <p>That led me to <strong>version 2</strong> of the following elements.</p>
  
  <p>My changed Thymeleaf <strong>HTML</strong> page: <strong>(Version 2)</strong></p>
  
  <pre><code>&lt;table id="myDataTable" class="dataTable display compact"&gt;
      &lt;thead&gt;
          &lt;tr&gt;
              &lt;th&gt;Name&lt;/th&gt;
              &lt;th&gt;Social Security&lt;/th&gt;                                        
              &lt;th&gt;Birthdate&lt;/th&gt;
              &lt;th&gt;Gender&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
  &lt;/table&gt;
  </code></pre>
  
  <p>My changed page <strong>controller</strong>: <strong>(Version 2)</strong></p>
  
  <pre><code>@GetMapping("/ApplicationName/MyDataTablePage")
  public String myDataTablePage(Model model) {
  
      return "/pages/MyDataTablePage";
  }
  </code></pre>
  
  <p>My new DataTables <strong>controller</strong>:</p>
  
  <pre><code>@RequestMapping(path = "/ApplicationName/Data/Person", method = RequestMethod.GET, produces = "application/json")
  @ResponseBody
  public List&lt;Person&gt; getPersonData() {
      System.out.println("I was called!");
  
      SearchCriteria searchCriteria = new SearchCriteria();
      searchCriteria.setOrderByString("name");
      searchCriteria.setUsePaging(false);
      searchCriteria.setFIRST_NUMBER(null);
      searchCriteria.setLAST_NUMBER(null);
  
      return myMapperService.getPeople(searchCriteria); //returns an ArrayList&lt;Person&gt;
  }
  </code></pre>
  
  <p>My changed (much more complicated) Javascript: <strong>(Version 2)</strong></p>
  
  <pre><code>$(document).ready(function () {
       $('#myDataTable').DataTable({ 
           'destroy' : true,
           'serverSide' : true,
           'sAjaxSource': '/ApplicationName/Data/Person',
           'sAjaxDataProp': '',
           'order': [ [ 0, 'asc' ] ],
           'columns': 
           [ 
              {  'data': 'name',
                  'render': function(data, type, row, meta){ 
                      if(type === 'display'){ 
                          data = '&lt;a href="javascript:openPersonDetail(&amp;apos;'+ row.socialSecurity +'&amp;apos;);"&gt;' + data + '&lt;/a&gt;' 
                      }  
                      return data; 
                  } 
              } ,
              { 'data': 'socialSecurity'} ,
              { 'data': 'birthdate'} ,
              { 'data': 'gender'} 
           ],
           'scrollY' : 300,
           'scrollCollapse' : true,
           'paging' : true,
           'autoWidth' : true,
           'ordering' : true,
           'searching' : false,
           'pageLength' : 20,
           'lengthChange' : false,
           'pagingType' : 'full_numbers',
           'dom' : '&lt;"top"ip&gt;rt&lt;"bottom"fl&gt;&lt;"clear"&gt;' 
       }); 
   });
  </code></pre>
  
  <p>=============================================================</p>
  
  <p>And that works, too. Especially if I remove the "'serverSide' : true," configuration - the DataTable works exactly like it used to before I made the above changes, but now it calls my new Data JSON controller instead of using a model attribute. </p>
  
  <p><strong>Problem:</strong></p>
  
  <p>However, with the "'serverSide' : true," configuration added to the DataTable, each time I go to a new 'page' of the table, it just recalls my "/Clinic/Detail/Data/Patient" controller each time. </p>
  
  <p>But, I need DataTables to pass my controller information about what page the user has selected, or what column the user tried to sort on. I also need to pass the table (dynamically) the total number of rows there are (count) so that DataTables can properly set up the page numbers for the user. </p>
  
  <p>Is DataTables sending some additional parameters to my controller that I can pick up? Something like a @PathVariable("whatever")?</p>
  
  <p>I feel like I am so close to getting this working, but I'm stuck at this point.</p>
  
  <p>I would really appreciate any help!</p>