Preserving Search Conditions Across Page Transitions with Spring MVC's @SessionAttributes
Introduction
When building an admin panel, you encounter this situation:
- Filter by “Action” category in the film list
- Navigate to a specific film’s edit page
- Save and return to the list… the filter has been reset
This is the story of how we solved the “filter disappeared after going back” problem with @SessionAttributes.
The Structure of the Problem
Spring MVC controllers create a new instance per request.
Even if you put form input values in the Model, they disappear on the next request.
@Controller
@RequestMapping("/admin/films")
public class FilmController {
@GetMapping
public String list(@ModelAttribute FilmSearchForm form, Model model) {
// form is a new instance every time → search conditions disappear
List<Film> films = filmService.search(form);
model.addAttribute("films", films);
return "admin/films/list";
}
}
How to Use @SessionAttributes
Adding @SessionAttributes to the controller class saves specified objects in the session.
@Controller
@RequestMapping("/admin/films")
@SessionAttributes("filmSearchForm") // ← Add this
public class FilmController {
@ModelAttribute("filmSearchForm")
public FilmSearchForm initSearchForm() {
return new FilmSearchForm(); // Initial value when not in session
}
@GetMapping
public String list(
@ModelAttribute("filmSearchForm") FilmSearchForm form,
Model model) {
List<Film> films = filmService.search(form);
model.addAttribute("films", films);
return "admin/films/list";
}
@PostMapping("/search")
public String search(
@ModelAttribute("filmSearchForm") FilmSearchForm form,
BindingResult result) {
if (result.hasErrors()) {
return "admin/films/list";
}
// Redirect to GET after POST (PRG pattern)
return "redirect:/admin/films";
}
}
Explanation of the Flow
- First access:
@ModelAttributemethod createsFilmSearchFormand saves it to the session - Search form submission: The
formvalues are updated in the session - Navigate to another page and return:
formis restored from session → search conditions are maintained
Clearing the Session
To implement a “reset search conditions” button, delete the session value.
@PostMapping("/search/reset")
public String resetSearch(SessionStatus sessionStatus) {
sessionStatus.setComplete(); // Delete session attributes
return "redirect:/admin/films";
}
Calling SessionStatus#setComplete() clears all session attributes managed by this controller.
Thymeleaf Template
<!-- list.html -->
<form method="post" action="/admin/films/search">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<input type="text" name="keyword"
th:value="${filmSearchForm.keyword}"
placeholder="Title Search">
<select name="categoryId">
<option value="">All Categories</option>
<option th:each="cat : ${categories}"
th:value="${cat.categoryId}"
th:text="${cat.name}"
th:selected="${cat.categoryId == filmSearchForm.categoryId}">
</option>
</select>
<button type="submit">Search</button>
<button type="submit" formaction="/admin/films/search/reset">Reset</button>
</form>
th:value="${filmSearchForm.keyword}" displays the value restored from the session in the form.
Notes
① Sessions Are Separate Per Controller
@SessionAttributes is per controller.
The filmSearchForm saved in FilmController is not visible from ActorController.
② Session Accumulation Problem
If you open multiple tabs or operate for a long time without closing the browser, large amounts of objects can accumulate in the session.
Include the timing for calling SessionStatus#setComplete() in your design.
③ Multi-Tab Conflicts
If you open two tabs in the same browser and search with different conditions in each, they share the session, which can cause unexpected behavior.
This is unlikely to be a problem for admin panel use cases, but be aware of it.
Comparison with localStorage (Vue 3 Case)
In the customer-facing app (Vue 3), we preserve filter state with localStorage.
| Preservation Method | Suitable Scenario |
|---|---|
@SessionAttributes | Spring MVC + Thymeleaf, server-side state management |
localStorage | SPA (Vue/React), client-side state management |
Pinia (Vue 3) | Global state management in SPA (disappears on page reload) |
Pinia + localStorage | SPA where you want to preserve state after page reload |
Summary
@SessionAttributes is the standard approach for preserving search conditions and input values across page transitions in Spring MVC + Thymeleaf.
@SessionAttributes("formName")— Declare what to save in session on the controller@ModelAttribute("formName")— Create initial values when not in sessionSessionStatus#setComplete()— Reset processing to clear the session
If you implement it naively, the “disappeared after going back” problem easily occurs — building it into your design from the start saves you headaches later.