2010-05-05 11 views
28

Quiero implementar la paginación usando JDBC. Lo real que quiero saber es "¿Cómo puedo obtener primeros 50 y luego próximos 50 registros de la base de datos de la página 1 y 2, respectivamente,"Paginación JDBC

Mi consulta es Select * from data [tabla de datos contiene 20.000 filas]

Por página # 1 Obtengo 50 registros y para la página # 2 quiero obtener los próximos 50 registros. ¿Cómo puedo implementarlo de manera eficiente en JDBC?

He buscado y he encontrado que rs.absolute(row) es la forma de saltear los registros de la primera página pero lleva bastante tiempo en conjuntos de resultados de gran tamaño y no quiero soportar esta cantidad de tiempo. Además, no quiero utilizar rownum y limit + offset en la consulta porque estos no son buenos para usar en la consulta, no sé por qué, aún así no quiero usarlo en la consulta.

¿Alguien puede ayudarme a obtener la ResultSet limitada para la paginación o hay alguna forma que nos dé JDBC?

Respuesta

24

No hay manera eficaz de haciendo esto simplemente usando JDBC. Debe formular el límite de en n filas y para comenzar desde el i-ésimo elemento clams directamente en el SQL para que sea eficiente.Dependiendo de la base de datos, esto podría ser bastante fácil (ver la palabra clave LIMIT de MySQL), en otras bases de datos como Oracle puede ser un poco más complicado (implica la subconsulta y el uso de pseudocolumna rownum).

Ver esta paginación Tutorial JDBC: http://java.avdiel.com/Tutorials/JDBCPaging.html

1

Comprendo implícitamente que no desea que la conexión JDBC tenga un único conjunto de resultados gigantesco que mantenga abierto durante mucho tiempo y navegue cuando sea necesario.

El enfoque habitual es agregar el SQL necesario para obtener solo un subconjunto de la solicitud completa, que desafortunadamente es diferente de la base de datos a la base de datos, y hará que sus declaraciones SQL sean específicas del proveedor. Si recuerdo correctamente, LIMIT se usa con MySQL. Pregunte por el rango apropiado para cada solicitud.

También creo que Hibernate contiene funcionalidad que le permite hacer esto para HQL, pero no estoy familiarizado con él.

2

Si está utilizando MySQL o PostgreSQL limite y offset son sus palabras clave. MSSqlServer y Oracle tienen características similares, pero parece ser un poco más doloroso.

Para MySQL y PostgreSQL echar un vistazo aquí:

http://www.petefreitag.com/item/451.cfm

Para Oracle echar un vistazo aquí:

http://www.oracle-base.com/forums/viewtopic.php?f=2&t=8635

+0

Gracias por su respuesta.¿Hay alguna forma de lograr esta funcionalidad sin usar palabras clave SQL? – Zeeshan

+0

@Zeeshan: no utiliza las API estándar de JDBC. –

+0

estoy usando API estándar de JDBC con oracle 10g – Zeeshan

1

¿Está utilizando algún tipo de ORM Framework como Hibernate o incluso Java Persistence API o simplemente SQL?

Mi respuesta a continuación: uso LIMIT y OFFSET http://www.petefreitag.com/item/451.cfm

O ir a través de ROWNUM operador Es necesario un contenedor cerca de su SQL entonces, pero, básicamente, es

select * from (select bla.*, ROWNUM rn from (
    <your sql here> 
) bla where rownum < 200) where rn >= 150' 
+0

estoy usando API estándar de JDBC con Oracle 10g – Zeeshan

17

Usted debe consultar única los datos que realmente necesita mostrar en la página actual. No transporte todo el conjunto de datos a la memoria de Java y luego filté allí. Solo haría las cosas innecesariamente más lentas.

Si tiene dificultades para implementar esto correctamente y/o calcular la consulta SQL para la base de datos específica, eche un vistazo a my answerhere.

Actualización: ya que usted está utilizando Oracle, aquí está un extracto-dirigida de Oracle desde la respuesta antes mencionada:

En Oracle necesita una sub consulta con rownum cláusula que debe ser similar:

private static final String SQL_SUBLIST = "SELECT id, username, job, place FROM" 
    + " (SELECT id, username, job, place FROM contact ORDER BY id)" 
    + " WHERE ROWNUM BETWEEN %d AND %d"; 

public List<Contact> list(int firstrow, int rowcount) { 
    String sql = String.format(SQL_SUBLIST, firstrow, firstrow + rowcount); 

    // Implement JDBC. 
    return contacts; 
} 
+0

¿Qué pasaría si después de los criterios de selección los resultados fueran grandes? Es por eso que estoy analizando condiciones extremas. y es posible que el conjunto de resultados contenga 100,000 filas :) de cualquier manera gracias por su respuesta. – Zeeshan

+3

Deje esas 100.000 filas en la base de datos. Consulta ** solo ** aquellos que ** necesitas ** para mostrar por página. Google tampoco consulta esos trillones de filas a la vez para mostrar solo las primeras diez en la página de resultados. – BalusC

+0

cómo hacer una consulta? No quiero que mi proveedor de consultas sea específico? – Zeeshan

1

Oracle admite la función de ventana estándar ROW_NUMBER() desde 8i para que pueda usar eso. Puede hacerlo como una consulta parametrizada, de modo que solo necesita establecer los números de fila de inicio y fin. P.ej.

SELECT * 
    FROM (SELECT *, ROW_NUMBER() ORDER BY (sort key) AS rowNumber FROM <your table name>) AS data 
WHERE 
    rowNumber>=:start AND 
    rowNumber<:end 

(Si no se está usando parámetros con nombre, reemplace: start /: '?' Extremo con el parámetro de posición de marcador de posición)

Ver SELECT SQL, Window Functions en la wikipedia. El artículo también enumera los otros DB que admiten la función de ventana estándar ROW_NUMBER().

1
PreparedStatement pStmt = // ... however you make it 
pStmt.setFetchSize(/* desired number of records to get into memory */); 

Nota, setFetchSize(int)is only a hint - última vez que lo estaba usando con MySQL, por ejemplo, no fue apoyada. Al mirar brevemente la documentación de Oracle, parece que su JDBC sí lo admite. No me citaría sobre eso, pero vale la pena intentarlo al menos; y sí, esta respuesta es frágil, pero podría ser suficiente menos dolor de cabeza que la implementación de la solución robusta.

Esencialmente, puede emitir la solicitud de todo, y solo obtiene el tamaño de búsqueda en la memoria a la vez (siempre que no esté reteniendo los resultados anteriores). Por lo tanto, debe establecer el tamaño de su búsqueda en 50, hacer su conexión/consulta, mostrar los primeros 50 resultados (lo que hace que la otra busque el siguiente bocado de su consulta) y así sucesivamente.

+0

también, creo que las soluciones LIMIT + OFFSET son mejores, ya que la robustez vale la pena las disputas extra suaves, pero quería señalar una alternativa. – Carl

+0

El problema con el intento de confiar en setFetchSize (int) es que debe permanecer conectado mientras realiza la búsqueda. La paginación puede llevar minutos (a medida que los usuarios navegan hacia atrás y hacia el fuerte). Mantener una conexión por tanto tiempo no es razonable. El uso de la sintaxis de SQL (específica del proveedor) para limitar/determinar el alcance del resultado es más escalable. Extraiga una conexión del grupo y ejecute una instrucción SQL paginativa (parametrizada en * donde * está en la paginación), devuelva la conexión al grupo y visualice los resultados. Si el usuario navega fuera, vuelva a parametrizar la instrucción SQL y repita. –

7

responsabilidad: This blog post on SQL pagination & JDBC pagination is posted by me.

Caso omiso de hibernación paginación, podemos utilizar SQL paginación paginación/JDBC

SQL paginación

Hay dos enfoques básicos:

  1. operan fragmentario conjunto de resultados (Nueva consulta para cada página)
  2. operando en consecuencia el sistema completo del

La manera de hacerlo es SQL específica

para MySQL/muchos otros LSQ que se puede hacer con límite y compensar

PostgreSQL: http://microjet.ath.cx/WebWiki/ResultPaginationWithPostgresql.html

En Oracle, usa la misma forma que para manejar la "consulta Top-N", por ejemplo ¿quiénes son los 5 empleados mejor pagados, que está optimizado

select * from (select a.*, rownum rnum 

from (YOUR_QUERY_GOES_HERE -- including the order by) a 

where rownum <= MAX_ROWS) 

where rnum >= MIN_ROWS 

Here is a very detailed explanation on ROW-NUM

Similar SO Thread

JDBC paginación

La pregunta viene a la mente es: cuando ejecuto el SQL, cómo es el resultado que se carga? ¿Inmediatamente o bajo pedido? mismo que este SO hilo

primer lugar tenemos que entender algunas basics of JDBC, as from Oracle

por javadoc: Statement.execute()

execute: Returns true if the first object that the query returns is a ResultSet object. Use this method if the query could return one or more ResultSet objects. Retrieve the ResultSet objects returned from the query by repeatedly calling Statement.getResutSet. 

Tenemos acceso a los datos en el conjunto de resultados a través de un cursor. Tenga en cuenta que este cursor es diferente del DB mientras que es un puntero inicialmente ubicado antes de la primera fila de datos.

Los datos se obtienen a petición. mientras que cuando haces la ejecución() estás buscando por primera vez.

Entonces, ¿cuántos datos se cargan? Es configurable Se puede usar el método java API setFetchSize() en ResultSet para controlar cuántas filas se extraen de DB cada vez por el controlador, qué tan grande son los bloques que recupera a la vez.

Por ejemplo, supongamos que el resultado total es 1000. Si el tamaño de búsqueda es 100, ir a la primera fila cargará 100 filas de DB y la 2ª a la 100ª fila se cargará desde la memoria local. Para consultar la 101ª fila, otras 100 filas serán cargar en la memoria.

De JavaDoc

Gives the JDBC driver a hint as to the number of rows that should be fetched from the database when more rows are needed for ResultSet objects genrated by this Statement. If the value specified is zero, then the hint is ignored. The default value is zero. 

Nota la palabra "pista" - que puede ser de anulación por la implementación específica del conductor.

Esto es también en lo que se basa la función "Limitar filas a 100" en clientes como SQL.

completar toda la solución, para desplazarse resultados, se deben considerar los tipos de ResultSet y ScrollableCursor en API

Uno puede encontrar un ejemplo de implementación de esta entrada en el oráculo

el cual es del libro de Oracle Toplink Guía del desarrollador Ejemplo 112 del controlador JDBC de Fetch Tamaño

ReadAllQuery query = new ReadAllQuery(); 

query.setReferenceClass(Employee.class); 

query.setSelectionCriteria(new ExpressionBuilder.get("id").greaterThan(100)); 

// Set the JDBC fetch size 

query.setFetchSize(50); 

// Configure the query to return results as a ScrollableCursor 

query.useScrollableCursor(); 

// Execute the query 

ScrollableCursor cursor = (ScrollableCursor) session.executeQuery(query); 

// Iterate over the results 

while (cursor.hasNext()) { 

System.out.println(cursor.next().toString()); 

} 

cursor.close(); 

.....................

Después de todo, las preguntas hierven a

¿Cuál es la mejor manera de hacer la paginación?

Nota SQL debe ser ORDEN por tener sentido en el enfoque de SQL,

lo contrario, es posible mostrar algunas filas de nuevo en la página siguiente.

A continuación se muestra algunos puntos de la documentación de PostgreSQL en el controlador JDBC y otras respuestas para

En primer lugar, la consulta original tendría que tener una cláusula ORDER BY con el fin de hacer el trabajo de paginación solución razonable. De lo contrario, sería perfectamente válido para Oracle devolver las mismas 500 filas para la primera página, la segunda página y la enésima página

La principal diferencia es para la forma JDBC, se requiere para mantener la conexión durante el atractivo. Esto puede no ser adecuado en la aplicación web sin estado, por ejemplo.

Para SQL manera

la sintaxis SQL es específico y puede no ser fácil de mantener. para JDBC manera

  • La conexión con el servidor debe estar utilizando el protocolo V3. Esto es el valor predeterminado para (y solo es compatible con) las versiones de servidor 7.4 y posteriores.
  • La conexión no debe estar en modo de confirmación automática. El motor de fondo cierra los cursores al final de las transacciones, por lo que en el modo de confirmación automática el servidor habrá cerrado el cursor antes de que se pueda obtener cualquier cosa de él.
  • La declaración debe crearse con un tipo ResultSet de ResultSet.TYPE_FORWARD_ONLY. Este es el valor predeterminado, por lo que no es necesario volver a escribir el código para aprovechar esto, pero también significa que no se puede desplazar hacia atrás o saltear el ResultSet.
  • La consulta proporcionada debe ser una sola instrucción, no múltiples declaraciones encadenadas con punto y coma.

Algunos Lectura adicional

This post is about performance tuning with optical fetch size

1

de entrada:

  1. Información Ejemplo de pedido (A2 o D3) (A/D ascendente/descendente) + columna
  2. Orden Ejemplo de información (A2 o D3) (A/D ascendente/descendente) + columna
  3. valor de filtro
  4. fila inicio
  5. fila inicio
  6. nuber máximo de filas

Resultado:

  • valores seleccionados
  • página seleccionada
  • Índice de la fila en este pedido
  • Cuenta de los datos disponibles. (Guardar una segunda consulta)

ventaja única de consulta para:

  • suma de las columnas disponibles con este filtro
  • solamente transferir la página seleccionada de db
  • ordenados correctamente sin SQL dinámico

Desventaja:

  • Oracle dependend

    seleccione x. * A partir de ( seleccione c.pk_field, c.numeric_a, c.char_b, c.char_c ROW_NUMBER() sobre (ORDER BY decodificación (?, 'A1', to_char (c.numeric_a, 'FM00000000'), 'A2', c.char_b, 'A3', c.char_c, 'A') asc, decodificar (?, 'D1', to_char (c.numeric_a, 'FM00000000 '),' D2 ', c.char_b,' D3 ', c.char_c,' A ') desc, c.pk_field asc
    ) AS "idx", COUNT (*) OVER (ORDER BY 1) " cnt "de myTable c donde c.haystack =? ) x donde x. "Idx" entre mayor (nvl (?, 1), 1) y nvl (?, 1) -1+?

-2

Los siguientes códigos Java funciona bien:

package paginationSample; 

import java.sql.Connection; 
import java.sql.DriverManager; 
import java.sql.ResultSet; 
import java.sql.ResultSetMetaData; 
import java.sql.SQLException; 
import java.sql.Statement; 

/** 
* 
* @ Nwaeze Emmanuel (FUNAI, 2016) 
*/ 
public class PaginationSample extends javax.swing.JFrame { 
public void getRows() { 
Connection con2; 
Statement stmt2; 
ResultSet rs2; 
int j=0; 
String sz=""; 

    try { 
// Load MS accces driver class 
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); 
String url = "jdbc:odbc:Driver={Microsoft Access Driver (*.mdb, 
    *.accdb)};DBQ=" + "C:\\Database2.mdb"; 
try{ 
con2 = DriverManager.getConnection(url, "user", ""); 
System.out.println("Connection Succesfull"); 

try{ 
    stmt2=con2.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, 
    ResultSet.CONCUR_UPDATABLE); 
    String sql=""; 

    if (txtpage.getText().trim().equals("") || 
     txtpagesize.getText().trim().equals("")){ 

    }else{ 
    int pagesize=Integer.parseInt(txtpagesize.getText()); 
    int page=Integer.parseInt(txtpage.getText()); 
    sql="SELECT * FROM FSRegistration100L WHERE SN >= " + 
    (pagesize*page-pagesize+1) + " AND " + "SN <= " + (pagesize*page); 
    rs2=stmt2.executeQuery(sql); 
    if (rs2.wasNull()){ 

    }else{ 
     while (rs2.next()){ 
     sz=sz + rs2.getString("RegNo") + "\n"; 

      j++; 

     } 
     txta.setText(sz); 
     txta.append(" Total rows =" + Integer.toString(j)); 
    } 
    } 
     stmt2.close(); 
     con2.close(); 
    } 
    catch (NullPointerException s){ 
    System.err.println("Got an exception166! "); 
    System.out.println(s.getMessage()); 
    } 
} catch (SQLException e1) { 
System.err.println("Got an exception1! "); 
System.err.println(e1.getMessage()); 
} 
} catch (ClassNotFoundException e2) { 
System.err.println("Got an exception2! "); 
System.err.println(e2.getMessage()); 

} 
} 



    private void jButton1ActionPerformed(java.awt.event.ActionEvent 
     evt)  {           
     // TODO add your handling code here: 
     getRows(); 
    }           

    // Variables declaration - do not modify      
    private javax.swing.JButton jButton1; 
    private javax.swing.JLabel jLabel1; 
    private javax.swing.JLabel jLabel2; 
    private javax.swing.JScrollPane jScrollPane1; 
    private javax.swing.JTextArea txta; 
    private javax.swing.JTextField txtpage; 
    private javax.swing.JTextField txtpagesize; 
    // End of variables declaration 
2

Sé que esta pregunta es viejo, pero esta es la forma en que he implementado paginación, espero que ayude a alguien

int pageNo = ....;  
String query = "SELECT * FROM data LIMIT "; 

switch (pageNo) { 
case 0: // no pagination, get all rows 
    query += Integer.MAX_VALUE; // a big value to get all rows 
    break; 
case 1: // get 1st page i.e 50 rows 
    query += 50; 
    break; 
default: 
    query += String.valueOf((pageNo-1)*50 + 1) ", " + String.valueOf(50); 
    break; 
} 

PreparedStatement ps = connection.prepareStatement(query); 
.... 

hacer la valor 50 una constante llamada pageSize, por lo que se puede cambiar a cualquier número