talosのプログラミング教室

【Java】ページングでWebアプリのパフォーマンスを向上させる

スポンサーリンク

こんにちは。talosです。

Webアプリのパフォーマンスを大きく左右する要因としてDB処理が挙げられます。

扱うデータが多くなればなるほど時間がかかるため、一覧表示などは工夫しないとユーザーへの負担が大きくなります。

そこで今回は、ページングを使って一覧表示をすることで、Webアプリのパフォーマンスを向上させる方法を解説します。

ページングとは

そもそもページングとはなにかというと、検索結果などが多いときに一度にすべて表示せず、1ページには検索結果の一部のみを表示して次ページボタンや前ページボタンで他のページに移ることができるようにする処理です。

一度にすべての検索結果を取得せず1ページに表示するデータのみを取得するため、ページアクセスの際にかかるDB処理の時間を短くすることができます。

f:id:talosta:20201128094439p:plain
Google検索のページング

環境

言語:Java
フレームワークJSF

解説

src/holder/Page.javaから見ていきましょう。

/**
 * 1ページ中に表示される最大件数
 */
public static final int OUTPUT_NUM = 10;

/**
 * 全レコードの件数
 */
private long recordNum;

/**
 * 現在のページ
 */
private int currentPage = 1;

/**
 * 全レコードの件数から算出されるページ数(recordNum/outputNum)
 */
private long maxPage;

まずはフィールドです。

ここはコメントの通りです。

/**
 * ページングに使う変数を設定する
 * @param recordNum
 */
public void config(Long recordNum) {
	this.recordNum = recordNum;
	this.maxPage = this.recordNum / OUTPUT_NUM;

	if (this.recordNum % OUTPUT_NUM != 0
			|| this.recordNum == 0) {
		++this.maxPage;
	}
}

config()はページングに使う変数を設定するメソッドです。

maxPageは(全レコードの件数/1ページに表示する最大件数)で算出します。

ですが、例えば全レコードの件数が35件の場合、整数型だと35/10=3となるため残りの5件を表示できなくなってしまいます。

そのため、

this.recordNum % OUTPUT_NUM != 0

の場合はmaxPageをインクリメントします。

また、全レコードの件数が0の場合、maxPageは0/10=0となります。

ですが、レコードがなくても1ページは表示しなくてはなりません。

そのため、

this.recordNum == 0

の場合もmaxPageをインクリメントします。

/**
 * 前ページボタンを表示するか
 * @return
 */
public boolean prevPageDisplay() {
	return currentPage > 1;
}

/**
 * 次ページボタンを表示するか
 * @return
 */
public boolean nextPageDisplay() {
	return currentPage < maxPage;
}

この2つは前ページボタンと次ページボタンを表示するかどうかを制御するメソッドです。

1ページ目にいるときに前ページボタンが押されたら0ページ目へ遷移することになります。

ですが0ページ目はありません。

そのようなことを避けるためのメソッドです。

/**
 * 最初のページへ
 */
public void topPage() {
	currentPage = 1;
}

/**
 * 最後のページへ
 */
public void lastPage() {
	currentPage = (int) maxPage;
}

/**
 * 前のページへ
 */
public void prevPage() {
	if (prevPageDisplay()) {
		currentPage--;
	}
}

/**
 * 次のページへ
 */
public void nextPage() {
	if (nextPageDisplay()) {
		currentPage++;
	}

これらはページの遷移を行うメソッドです。

最初のページへ遷移するときはcurrentPageに1を代入し、最後のページに遷移するときは遷移し得る最大のページをcurrentPageを代入します。

前のページに遷移するときにはcurrentPageを-1し、次のページに遷移するときにはcurrentPageを+1します。

/**
 * 何番目のレコードから表示するか
 * @return
 */
public Integer offset() {
	return (currentPage - 1) * OUTPUT_NUM;
}

何件目から表示するかを計算をするメソッドです。

画面に一覧を表示する際、1ページ目であれば1件目から表示すれば良いですが、2ページ目では11件目から表示する必要があります。

言うまでもないですが、リストのインデックスは0からなのでcurrentPageから1引いています。


次に、src/master/UserMaster.javaを見てみましょう。

/**
 * 登録されたユーザーのリスト
 */
private ArrayList<User> userList;

{
	userList = new ArrayList<User>();
	for (int i = 0; i < 35; i++) {
		userList.add(new User("user" + i, "test"));
	}
}

本来はDBなどからレコードを取得してきますが、今回はサンプルのためここでデータを用意します。

35人のIDとパスワードをリストに登録しておきます。

/**
 * offsetからnum件のユーザーリストを返す
 * @param page
 * @return
 */
public ArrayList<User> getUserList(Page page) {
	page.config((long) userList.size());
	ArrayList<User> userList = new ArrayList<>();
	int offset = page.offset();
	int num = Page.OUTPUT_NUM;

	// recordNum % OUTPUT_NUM != 0の場合リストの外を参照する
	try {
		for (int i = offset; i < offset + num; i++) {
			userList.add(this.userList.get(i));
		}
	} catch (IndexOutOfBoundsException e) {
		e.printStackTrace();
	}
	return userList;
}

getUserList()は表示するレコードだけを返すメソッドです。

最初にpage.config()を呼んでいます。引数は全レコードの件数です。

全レコードの件数も本来であればSELECT COUNT(*)を投げて件数を取得します。

for文のところはoffset件目から10件取得してリストに追加しています。

LIMIT句で取得する件数を制限してSELECT文を投げるところを模しています。


スポンサーリンク



実行

実行するとこのような画面が開きます。

f:id:talosta:20201128171707p:plain

1~10件目が表示され、最初のページへのボタンと前ページへのボタンが非活性化されています。

次ページへのボタンを押すと、

f:id:talosta:20201128171716p:plain

11~20件目が表示されます。

最後のページへのボタンを押すと、

f:id:talosta:20201128171724p:plain

最後の5件が表示され、次ページへのボタンと最後のページへのボタンが非活性化されました。

おわりに

今回はページングの実装方法を説明しました。

不明点がありましたら、コメントお願いします。