페이징 문제는 거의 모든 개발자가 직면하게 되는 매우 일반적인 문제입니다. 여기서는 페이징을 수행하는 방법을 구체적으로 설명하지 않고 웹 모드에서 페이징의 원리를 설명하겠습니다. 첫 번째는 결과 집합을 얻기 위해 쿼리하는 것입니다(데이터베이스 쿼리에서 얻은 결과로 표시됨). 결과가 많으면 일반적으로 모든 데이터를 한 번에 표시하지 않고 페이징을 사용하여 특정 데이터(예: 20개 항목). HTTP의 상태 비저장 특성으로 인해 각 제출은 새 요청으로 처리됩니다. 페이지가 변경되더라도 마지막 결과는 다음 결과에 영향을 미치지 않습니다.
페이징을 구현하는 세 가지 방법이 있습니다. 다른 방법이 있는지는 모르겠습니다.
1. 매번 쿼리 결과의 모든 데이터를 가져온 다음 페이지 번호에 따라 지정된 레코드를 표시합니다.
2. 페이지를 기반으로 한 페이지의 데이터만 가져온 다음 이 페이지를 표시합니다. 여기에서 SQL 문을 구성해야 합니다.
3. 특정 수의 데이터 페이지를 가져오는 것은 이전 두 가지 사이의 절충안입니다.
여기서도 주목해야 할 점은 데이터가 요청에 배치되는지 아니면 세션에 배치되는지 여부인데, 이에 대해 하나씩 설명하겠습니다.
1. 일반적으로 메모리를 많이 차지하므로 세션에 배치되지 않으므로 요청에 배치해야 합니다.
장점: 구현이 비교적 간단하고 쿼리 속도가 비교적 빠릅니다.
단점: 더 많은 메모리를 차지하고 네트워크를 통해 많은 데이터를 전송합니다.
이 방법은 상대적으로 적은 양의 데이터를 포함하는 쿼리에 더 적합합니다. 여기 어떤 사람들은 페이지를 변경할 때 다시 쿼리할 필요가 없도록 데이터를 세션에 넣습니다. 그러나 이는 매우 좋지 않으며 이런 방식으로 사용하지 않는 것이 좋습니다.
2. 세션에 넣는 것은 의미가 없기 때문에 절대로 세션에 배치되지 않습니다.
장점: 메모리를 적게 차지합니다.
단점: 페이지 수를 알기 위해서는 레코드가 몇 개인지 알아야 하기 때문에 쿼리 결과의 총 개수를 먼저 구해야 하기 때문에 더 번거롭다. 또한 데이터베이스마다 다른 페이징 쿼리 문을 구성해야 합니다.
3. 이 상황은 세션에 배치되어야 합니다. 그렇지 않으면 왜 여러 페이지를 가져오겠습니까? 이 구현은 데이터베이스 쿼리 수를 줄이기 위한 것입니다. 예를 들어, 레코드 1에서 10을 저장하면 페이지를 변경할 때 1과 10개는 세션에서 직접 얻을 수 있습니다. 11페이지로 변경하면 캐시를 11로 재설정할 수 있습니다.
20페이지의 데이터(또는 5~15페이지의 데이터), 이 경우 10개의 변경에 대해 단 한 번의 데이터베이스 쿼리 작업만 필요합니다.
장점: 상대적으로 적은 메모리를 차지하며 평균 쿼리 속도가 향상됩니다.
단점: 구현이 더 복잡하고 더티 데이터가 존재할 수 있으며 캐시 컬렉션을 직접 정의해야 합니다. 쿼리하는 데이터의 양이 상대적으로 많은 경우 이 방법을 사용하는 것을 고려해 볼 수 있습니다.
다음 디자인은 매번 한 페이지의 데이터만 가져오며 매번 총 쿼리 수를 재설정해야 합니다. 이를 직접 구현하는 방법은 비교적 일반적인 페이징 구현입니다.
여기에서 인터페이스를 디자인하세요.
package treeroot.util;import java.util.List;/*** 이 인터페이스는 페이징 기능을 구현하는 데 사용됩니다. 여기에는 수정 기능이 제공되지 않습니다. * @author treerot* @version 1.0* @since 2004-9-30*/public 인터페이스 Pageable{ /** * 데이터 결과 가져오기* @return */ public List getResult() /** * 총 쿼리 수 가져오기; * @return */ public int getCount(); /** * 페이지당 레코드 수를 가져옵니다.* @return */ public int getPageSize() /** * 현재 페이지 번호를 가져옵니다.* @return */ public int getCurrentPage(); /** * 총 페이지 수를 가져옵니다* @return */ public int getPages(); /** * 각 페이지에 표시되는 기본 레코드 수*/ public final static int DEFAULT_PAGESIZE=20;} 이 인터페이스는 매우 간단합니다. 여기에는 결과 목록과 페이징에 필요한 몇 가지 정보가 포함되어 있습니다.
1. 이 인터페이스의 구현은 마지막 쿼리와 아무 관련이 없는 특정 쿼리의 특정 데이터 페이지를 나타냅니다.
2. 이 인터페이스의 구현은 읽기 전용이어야 합니다. 즉, 수정할 수 없습니다.
3. getPages() 메소드는 중복되지만 이 메소드는 여기서 계속 제공됩니다.
추상 구현은 다음과 같습니다.
package treeroot.util;import java.util.List;/*** @author treerot* @version 1.0* @since 2004-9-30*/public 추상 클래스 AbstractPage는 Pageable을 구현합니다{ private int currentPage; private int pageSize; 페이지; protected int count; protected List result; /** * 현재 페이지 지정* @param currentPage * @throws PageException */ public AbstractPage(int currentPage) this(currentPage,Pageable.DEFAULT_PAGESIZE); } /** * 현재 페이지 및 페이지 크기 지정* @param currentPage * @param pageSize * @throws PageException */ public AbstractPage(int currentPage,int pageSize) { this.currentPage=currentPage ; this.pageSize=pageSize; } protected void checkPage(int currentPage)는 PageException을 발생시킵니다. if((currentPage<1)||(currentPage>this.getPages())) throw new PageException("페이지가 범위를 벗어났습니다. 총 페이지 수는 "+this.getPages()+"이고 현재 페이지는 " +currentPage); } /** * 이 메서드는 초기화, 즉 카운트 값과 결과를 계산하기 위해 하위 클래스에 의해 재정의되고 하위 클래스의 생성자에서 호출됩니다. */ 추상 보호 void init()는 PageException을 발생시킵니다. public List getResult() { return result; } public int getPageSize() { return pageSize() { return currentPage; } public int getPages() { if(pages==0) this.pages=(count+pageSize-1)/pageSize return; 페이지; }}이 추상 클래스는 인터페이스의 모든 메서드를 구현하지만 하위 클래스에서 구현해야 하는 추상 메서드 init()를 정의합니다. 위의 인터페이스와 추상 클래스는 비교적 단순해 보이지만 실제로는 구현 측면에서는 아무 것도 하지 않지만 개발에 큰 도움을 줄 수 있습니다. 우리는 필요에 따라 이 추상 클래스를 상속할 수 있으며 데이터는 List를 통해 직접적으로 또는 JDBC, Hibernate 등을 통해 다양한 방법으로 얻을 수 있지만 우리 모두는 결과를 List로 캡슐화해야 합니다. Hibernate를 통해 특히 편리한 것 같습니다.
PageException은 사용자 정의 예외입니다.
package treeroot.util /*** @author treeroot* @version 1.0* @since 2004-9-30*/public class PageException extends Exception{ public PageException(){ super() } public PageException(String message){ super( 메시지); }}