Tech Blog

การเก็บรักษาเงื่อนไขการค้นหาข้ามหน้าด้วย @SessionAttributes ของ Spring MVC

by Tech Writer
Spring Boot Thymeleaf Spring MVC

บทนำ

เมื่อสร้าง admin panel คุณจะเจอสถานการณ์นี้:

  1. กรองตาม category “Action” ในรายการภาพยนตร์
  2. ไปที่หน้าแก้ไขภาพยนตร์เฉพาะ
  3. บันทึกและกลับมาที่รายการ… ตัวกรองถูก reset

นี่คือเรื่องราวที่เราแก้ปัญหา “ตัวกรองหายไปหลังกลับมา” ด้วย @SessionAttributes


โครงสร้างของปัญหา

Spring MVC controllers สร้าง instance ใหม่ต่อ request
แม้จะใส่ค่า form input ใน Model พวกมันก็หายไปใน request ถัดไป

@Controller
@RequestMapping("/admin/films")
public class FilmController {
    
    @GetMapping
    public String list(@ModelAttribute FilmSearchForm form, Model model) {
        // form เป็น instance ใหม่ทุกครั้ง → เงื่อนไขการค้นหาหาย
        List<Film> films = filmService.search(form);
        model.addAttribute("films", films);
        return "admin/films/list";
    }
}

วิธีใช้ @SessionAttributes

การเพิ่ม @SessionAttributes ที่ controller class จะบันทึก objects ที่ระบุใน session

@Controller
@RequestMapping("/admin/films")
@SessionAttributes("filmSearchForm")  // ← เพิ่มนี้
public class FilmController {
    
    @ModelAttribute("filmSearchForm")
    public FilmSearchForm initSearchForm() {
        return new FilmSearchForm();  // ค่าเริ่มต้นเมื่อไม่อยู่ใน 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 ไปยัง GET หลัง POST (PRG pattern)
        return "redirect:/admin/films";
    }
}

คำอธิบาย Flow

  1. การเข้าครั้งแรก: method @ModelAttribute สร้าง FilmSearchForm และบันทึกไว้ใน session
  2. ส่ง search form: ค่า form ถูกอัปเดตใน session
  3. ไปหน้าอื่นและกลับมา: form ถูก restore จาก session → เงื่อนไขการค้นหายังคงอยู่

การล้าง Session

เพื่อ implement ปุ่ม “reset เงื่อนไขการค้นหา” ให้ลบค่า session

@PostMapping("/search/reset")
public String resetSearch(SessionStatus sessionStatus) {
    sessionStatus.setComplete();  // ลบ session attributes
    return "redirect:/admin/films";
}

การเรียก SessionStatus#setComplete() จะล้าง session attributes ทั้งหมดที่ controller นี้จัดการ


Template Thymeleaf

<!-- 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="ค้นหาชื่อ">
    
    <select name="categoryId">
        <option value="">ทุก Category</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">ค้นหา</button>
    <button type="submit" formaction="/admin/films/search/reset">Reset</button>
</form>

th:value="${filmSearchForm.keyword}" แสดงค่าที่ restore จาก session ใน form


หมายเหตุ

① Sessions แยกตาม Controller

@SessionAttributes เป็น per controller
filmSearchForm ที่บันทึกใน FilmController ไม่สามารถมองเห็นได้จาก ActorController

② ปัญหา Session สะสม

ถ้าเปิดหลาย tab หรือ operate นานโดยไม่ปิด browser objects จำนวนมากอาจสะสมใน session
รวม timing สำหรับการเรียก SessionStatus#setComplete() ไว้ใน design ของคุณ

③ ความขัดแย้งใน Multi-Tab

ถ้าเปิดสอง tab ในบราวเซอร์เดียวกันและค้นหาด้วยเงื่อนไขต่างกันในแต่ละ tab พวกมัน share session ซึ่งอาจทำให้เกิดพฤติกรรมที่ไม่คาดคิด
นี่ไม่น่าจะเป็นปัญหาสำหรับกรณีใช้งาน admin panel แต่ควรระวัง


เปรียบเทียบกับ localStorage (กรณี Vue 3)

ในแอพฝั่งลูกค้า (Vue 3) เราเก็บสถานะตัวกรองด้วย localStorage

วิธีการเก็บสถานการณ์ที่เหมาะ
@SessionAttributesSpring MVC + Thymeleaf, การจัดการ state ฝั่ง server
localStorageSPA (Vue/React), การจัดการ state ฝั่ง client
Pinia (Vue 3)การจัดการ global state ใน SPA (หายเมื่อ reload หน้า)
Pinia + localStorageSPA ที่ต้องการเก็บ state หลัง reload หน้า

สรุป

@SessionAttributes เป็นวิธีมาตรฐานสำหรับการเก็บเงื่อนไขการค้นหาและค่า input ข้าม page transition ใน Spring MVC + Thymeleaf

  • @SessionAttributes("formName") — ประกาศสิ่งที่จะบันทึกใน session ที่ controller
  • @ModelAttribute("formName") — สร้างค่าเริ่มต้นเมื่อไม่อยู่ใน session
  • SessionStatus#setComplete() — การ reset เพื่อล้าง session

ถ้า implement แบบง่ายๆ ปัญหา “หายไปหลังกลับมา” เกิดขึ้นง่าย — การสร้างมันเข้าไปใน design ตั้งแต่แรกช่วยให้ไม่มีปัญหาในภายหลัง


แผนที่บทความของ Series นี้

การสร้างแอพ DVD Rental สำหรับผู้ใช้ควบคู่กับหน้าจัดการ — ภาพรวมโครงสร้าง Vue 3 + Spring Boot

ส่งข้อความได้ตามสบาย

กรุณาส่งข้อความ หากมีคำปรึกษาด้านเทคนิค ความคิดเห็น หรือคำถาม