My開発メモ

ページネーションをつくる(サーブレット&JSP)

jspファイルにページネーションをつくってみたので、そのメモ。

大まかな流れとしては、

  1. SQL文で LIMIT 指定でそのページ分のデータを取得する。
  2. JSPでページを指定する。

1ページに表示する件数を決める。

今回は、Constクラスに static変数を設置して、そこで指定。「5」と指定した。

util.Constクラス

pacage util;

public class Const {
  public static final int PER_PAGE = 5;
}

データの全件数を取得する。

データベースにアクセスするクラスとして、employeeDAO.java を作成した。

そこに、getSize() というメソッドを作成した。

dao.EmployeeDAOクラス

package dao;

...(略)...

public class EmployeeDAO {
  ...(略)...

  public int getSize() {
    int count = 0;
    
    try (Connection conn = DriverManager.getConnection(
          JDBC_URL, DB_USER, DB_PASS)) {

      String sql = "SELECT COUNT(*) FROM employee";
      PreparedStatement pStmt = conn.prepareStatement(sql);
      ResultSet rs = pStmt.executeQuery();
      rs.next();
      count = rs.getInt(1);  // 1番目のカラムを取得
    } catch (SQLException e) {
      e.printStackTrace();
      return 0;
    }
    return count;
  }

}

SQL文の「COUNT(*)」で件数を数え、rs.getInt(1)で取得している。

この「1」は、1カラム目ということである。

他にやり方としては、

COUNT(*) AS cnt

とし、

rs.getInt("cnt")

で取得する方法もあるみたい。

そして、以下のビジネス・オブジェクトを作成する。

GetSizeLogic.java

public class GetSizeLogic {
  public int execute() {
    EmployeeDAO dao = new EmployeeDAO();
    int dataMax = dao.getSize();
    return dataMax;
  }
}

さて、これでデータ件数は取得できるが、この値をどこに保持しておくかである。

Constクラスに DATA_MAX として定数として保持しようかとも考えたが、 データ件数は刻々と変化するので、サーブレットで処理するごとに GetSizeLogicクラスのexecute()メソッドを実行し、データ件数を取得したほうが よいと考えた。

ページネーションを記述するJSPファイルにページ情報を渡す

ページネーションに必要な情報は、以下である。

page現在のページ
dataMaxデータ全件数
lastラストのページ番号
perPage1ページに出力する件数
first最初のページ番号(1で固定)

これらの数字をサーブレットからリクエストスコープを使ってJSPに 渡せばいい。

今回は、pageInfo というクラスを使った。

PageInfo.java

package model;

public class PageInfo implements Serializable {
	private int dataMax;
	private int perPage = Const.PER_PAGE;
	private int first = 1;
	private int last;
	
	public PageInfo () {}
	
	public PageInfo (int dataMax) {
		this.dataMax = dataMax;
		last = (int) Math.ceil((double) this.dataMax / (double) this.perPage);
	}

    // ゲッターセッター というか、ゲッターだけでいい。
}

1ページ分のデータをデータベースから取得する。

表示したいページ番号を page とする。

1ページに表示するデータ件数を

int per_page = 10;

スキップするデータ件数を

int skip = (page - 1) * per_page

とすると、必要な SQL文は以下である。

SELECT * FROM employee LIMIT skip, per_page

であるから、EmployeeDAO.java の findAll()メソッドは以下のようになる。

EmployeeDAO.java

...(略)...

  public List<Employee> findAll(int skip, int perPage) {
    List<Employee> empList = new ArrayList<>();
    
    try (Connection conn = 
      DriverManager.getConnection(JDBC_URL, DB_USER, DB_PASS)) {
      
      String sql = "SELECT id, name, age FROM employee LIMIT ?, ?";
      PreparedStatement pStmt = conn.prepareStatement(sql);
      pStmt.setInt(1, skip);
      pStmt.setInt(2, perPage);
      ResultSet rs = pStmt.executeQuery();
      
      while (rs.next()) {
        String id = rs.getString("id");
        String name = rs.getString("name");
        int age = rs.getInt("age");
          Employee employee = new Employee(id, name, age);
        empList.add(employee);
      }
    } catch (SQLException e) {
      e.printStackTrace();
      return null;
    }
    return empList;
  }

したがって、GetEmployeeListLogic は以下となる。

GetEmployeeListLogic.java

public class GetEmployeeListLogic {
  public List<Employee> execute(int skip, int perPage) {
    EmployeeDAO dao = new EmployeeDAO();
    List<Employee> empList = dao.findAll(skip, perPage);
    return empList;
  }
}

サーブレットでJSPにわたす各情報を作成する。

サーブレットは、”/<contextPath>/list” というURLで動作するようにするが、 “/<contextPath>/list?page=2” などというURLにも対応できるようにしておく。

つまり、”?page=2″ などというクエリ文字列が無い場合、”?page=1″ が指定された ものとする。

GetEmployeeListServlet.java

@WebServlet("/list")
public class EmployeeListServlet extends HttpServlet {
  ...(略)...

  protected void doGet(HttpServletRequest request,
                       HttpServletResponse response)
                 throws ServletException, IOException {

    // データ全件数を取得。
    GetEmployeeSizeLogic sizeLogic = new GetEmployeeSizeLogic();
    int dataMax = sizeLogic.execute();

    // 全件数がわかれば、JSPにわたすページ情報を作成できる。
    PageInfo pageInfo = new PageInfo(dataMax);
    request.setAttribute("pageInfo", pageInfo);

    // この時点では page は 0 あるいは 総ページ数より多いかもしれない。
    int page = myParseInt(request.getParameter("page"));
    if (page < 1) { page = 1; }
    if (page > pageInfo.getLast()) { page = pageInfo.getLast(); }
    // スキップするデータ数
    int skip = (page - 1) * Const.PER_PAGE;
    
    // 1ページ分のデータを取得する。
    GetEmployeeListLogic listLogic = new GetEmployeeListLogic();
    List<Employee> empList = listLogic.execute(skip, Const.PER_PAGE);
    request.setAttribute("empList", empList);
    request.setAttribute("page", page);
    
    String url = "/WEB-INF/jsp/empList.jsp";
    RequestDispatcher dispatcher = request.getRequestDispatcher(url);
    dispatcher.forward(request, response);
  }
  
  private int myParseInt(String _page) {
    int page = 0;
    try {
      page = Integer.parseInt(_page);
    } catch (NumberFormatException e) {
      // 不正な文字なら 1 とする
      page = 1;
    }
    return page;
  }
}

JSPのページネーション部分

JSPは長くなるので、ページネーション部分だけ載せておく。

empList.jsp

...(略)...

<div id="pagenation">
  <ul>
    <li><a href="<%=request.getContextPath() %>/list?page=${
                 page - 1 < 1 ? 1 : page - 1}"><</a></li>
    <c:forEach var="c" begin="${pageInfo.first}" end="${pageInfo.last}" step="1">
      <c:choose>
        <c:when test="${c == page}">
          <li style="font-weight: bold;">
            <a href="<%=request.getContextPath() %>/list?page=${c}">${c}</a>
          </li>
        </c:when>
        <c:otherwise>
          <li><a href="<%=request.getContextPath() %>/list?page=${c}">${c}</a></li>
        </c:otherwise>
      </c:choose>
    </c:forEach>
    <li><a href="<%=request.getContextPath() %>/list?page=${
      page + 1 > pageInfo.last ? pageInfo.last : page + 1}">></a></li>
  </ul>
</div>

動作環境

Java11 / Tomcat9 / Servlet 4.0

カテゴリー: Java, memo

タグ: jsp, pagenation, servlet

カウント: 315