首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Spring + Hibernate + Struts2 分頁查詢完整架構設計

     編輯:Java 相關知識

為什麼分頁是分層架構的痛點?

在單層 JSP 時代,分頁只是 LIMIT offset, size 的事。但引入 DAO / Service / Action 三層後,分頁資訊(總筆數、總頁數、當前頁、每頁筆數)必須跨層傳遞,稍有不慎就會產生「Service 層塞 SQL 參數」「Action 直接操作 Hibernate Session」等反模式。本文梳理一套乾淨、可複用的分頁協定,適用於 SSH 專案,也能輕鬆移植到 Spring Boot + MyBatis/JPA。

核心協定:PageBean 統一承載分頁元資料

public class PageBean<T> implements Serializable {
    private List<T> list;          // 當頁資料
    private int totalCount;         // 總筆數
    private int pageSize;           // 每頁筆數
    private int currentPage;        // 當前頁碼 (1-based)
    // 衍生屬性:總頁數、是否首尾頁、上下頁頁碼...
    public int getTotalPages() { return (int) Math.ceil((double) totalCount / pageSize); }
    public boolean hasPrev() { return currentPage > 1; }
    public boolean hasNext() { return currentPage < getTotalPages(); }
    // getter/setter 略
}

DAO 層:只負責「切頁 SQL」與「查總數」

public interface MemberDao {
    List<Member> findByPage(String hql, int offset, int pageSize);
    long countAll(String hql);
}

@Repository
public class MemberDaoImpl extends HibernateDaoSupport implements MemberDao {
    @Override
    public List<Member> findByPage(String hql, int offset, int pageSize) {
        return getHibernateTemplate().execute(session -> {
            Query q = session.createQuery(hql);
            q.setFirstResult(offset);
            q.setMaxResults(pageSize);
            return q.list();
        });
    }

    @Override
    public long countAll(String hql) {
        // 將 select ... 轉 select count(*) 以避免全表掃描
        String countHql = "select count(*) " + hql.substring(hql.indexOf("from"));
        return getHibernateTemplate().execute(session -> 
            ((Number) session.createQuery(countHql).uniqueResult()).longValue()
        );
    }
}

Service 層:組裝 PageBean,對 Action 隱藏 offset 計算

@Service
@Transactional(readOnly = true)
public class MemberServiceImpl implements MemberService {
    @Autowired MemberDao memberDao;

    @Override
    public PageBean<Member> queryPage(int pageNo, int pageSize) {
        String hql = "from Member order by id desc";
        long total = memberDao.countAll(hql);
        int offset = (pageNo - 1) * pageSize;
        List<Member> rows = memberDao.findByPage(hql, offset, pageSize);

        PageBean<Member> pb = new PageBean<>();
        pb.setList(rows);
        pb.setTotalCount(total);
        pb.setPageSize(pageSize);
        pb.setCurrentPage(pageNo);
        return pb;
    }
}

Struts2 Action:只管參數綁定與結果導向

@Controller
public class ListMemberAction extends ActionSupport {
    @Autowired MemberService memberService;
    private int page = 1;              // Struts2 自動注入 ?page=2
    private PageBean<Member> pageBean; // 供 JSP 讀取

    @Override
    public String execute() {
        pageBean = memberService.queryPage(page, 10);
        return SUCCESS;
    }
    // getter/setter 略
}

JSP 前端:使用 Struts2 標籤渲染分頁導航

<s:iterator value="pageBean.list" var="m">
  <tr>
    <td><s:property value="#m.name" /></td>
    <td><s:property value="#m.dept" /></td>
    <td>
      <a href="edit.action?id=<s:property value='#m.id'/>">編輯</a>
      <a href="delete.action?id=<s:property value='#m.id'/>" onclick="return confirm('確定刪除?')">刪除</a>
    </td>
  </tr>
</s:iterator>

<div class="pagination">
  <s:if test="pageBean.hasPrev()">
    <a href="list.action?page=1">首頁</a>
    <a href="list.action?page=<s:property value='pageBean.currentPage-1'/>">上頁</a>
  </s:if>
  <s:else>首頁 上頁</s:else>

  第 <s:property value="pageBean.currentPage"/> 頁 / 共 <s:property value="pageBean.totalPages"/> 頁

  <s:if test="pageBean.hasNext()">
    <a href="list.action?page=<s:property value='pageBean.currentPage+1'/>">下頁</a>
    <a href="list.action?page=<s:property value='pageBean.totalPages'/>">尾頁</a>
  </s:if>
  <s:else>下頁 尾頁</s:else>
</div>

遷移到現代技術棧的提示

  • Spring Boot + MyBatis:用 PageHelper.startPage(pageNo, pageSize) 自動攔截 SQL,回傳 PageInfo<T> 直接對應 PageBean。
  • Spring Data JPA:Page<Member> page = repo.findAll(PageRequest.of(pageNo-1, pageSize)) 一行搞定。
  • 前後端分離:Action 改為 @RestController 回傳 JSON,前端用 Vue/React + Ant Design Table + Pagination 元件。

總結

分頁的本質是「資料切片」+「元資料傳遞」。只要 DAO 只管切頁、Service 只管組裝 PageBean、Action/Controller 只管參數轉換,三層職責清晰,未來換框架也只需替換 DAO 實作,業務邏輯完全不動。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved