Gids voor de JDBC ResultSet-interface

1. Overzicht

De Java Database Connectivity (JDBC) API biedt toegang tot de database vanuit een Java-applicatie. We kunnen JDBC gebruiken om verbinding te maken met elke database, zolang het ondersteunde JDBC-stuurprogramma beschikbaar is.

De ResultSet is een tabel met gegevens die wordt gegenereerd door het uitvoeren van databasequery's. In deze tutorial gaan we dieper in op de ResultSet API.

2. Genereren van een ResultSet

Eerst halen we een ResultSet door te bellen executeQuery () op elk object dat het Uitspraak koppel. Beide PreparedStatement en de Oproepbare verklaring zijn subinterfaces van Uitspraak:

PreparedStatement pstmt = dbConnection.prepareStatement ("selecteer * van medewerkers"); ResultSet rs = pstmt.executeQuery ();

De ResultSet object behoudt een cursor die naar de huidige rij van de resultatenset wijst. We zullen gebruiken De volgende() op onze ResultSet om de records te herhalen.

Vervolgens zullen we gebruik de getX () methoden terwijl u door de resultaten bladert om de waarden uit de databasekolommen op te halen, waar X is het datatype van de kolom. In feite zullen we databasekolomnamen aan de getX () methoden:

while (rs.next ()) {String naam = rs.getString ("naam"); Geheel getal empId = rs.getInt ("emp_id"); Dubbel salaris = rs.getDouble ("salaris"); String position = rs.getString ("position"); } 

Hetzelfde, het indexnummer van de kolom kan worden gebruikt met de getX () methoden in plaats van de kolomnaam. Het indexnummer is de volgorde van de kolommen in de SQL select-instructie.

Als de select-instructie geen kolomnamen vermeldt, is het indexnummer de reeks kolommen in de tabel. De nummering van de kolomindex begint vanaf één:

Geheel getal empId = rs.getInt (1); String naam = rs.getString (2); Tekenreekspositie = rs.getString (3); Dubbel salaris = rs.getDouble (4); 

3. MetaData ophalen uit het ResultSet

In dit gedeelte zien we hoe u informatie kunt ophalen over de kolomeigenschappen en typen in een ResultSet.

Laten we eerst de getMetaData () methode op onze ResultSet om het ResultSetMetaData:

ResultSetMetaData metaData = rs.getMetaData ();

Laten we vervolgens het aantal kolommen in onze ResultSet:

Geheel getal columnCount = metaData.getColumnCount ();

Bovendien kunnen we een van de onderstaande methoden voor ons metadata-object gebruiken om eigenschappen van elke kolom op te halen:

  • getColumnName (int columnNumber) om de naam van de kolom te krijgen
  • getColumnLabel (int columnNumber) om toegang te krijgen tot het label van de kolom, dat wordt gespecificeerd na NET ZO in de SQL-query
  • getTableName (int columnNumber) om de tabelnaam te krijgen waartoe deze kolom behoort
  • getColumnClassName (int columnNumber) om het Java-gegevenstype van de kolom te verkrijgen
  • getColumnTypeName (int columnNumber) om het gegevenstype van de kolom in de database op te halen
  • getColumnType (int columnNumber) om het SQL-gegevenstype van de kolom op te halen
  • isAutoIncrement (int columnNumber) geeft aan of de kolom automatisch wordt verhoogd
  • isCaseSensitive (int columnNumber) specificeert of de kolom case belangrijk is
  • isSearchable (int columnNumber) suggereert of we de kolom in de waar clausule van de SQL-query
  • isCurrency (int columnNumber) geeft aan of de kolom een ​​contante waarde bevat
  • isNullable (int columnNumber) geeft terug nul als de kolom niet nul kan zijn, een als de kolom een ​​null-waarde kan bevatten, en twee als de nullabiliteit van de kolom onbekend is
  • isSigned (int columnNumber) geeft terug waar als waarden in de kolom zijn ondertekend, retourneert anders false

Laten we de kolommen doorlopen om hun eigenschappen te krijgen:

voor (int columnNumber = 1; columnNumber <= columnCount; columnNumber ++) {String catalogName = metaData.getCatalogName (columnNumber); String className = metaData.getColumnClassName (columnNumber); String label = metaData.getColumnLabel (columnNumber); Stringnaam = metaData.getColumnName (columnNumber); String typeName = metaData.getColumnTypeName (columnNumber); int type = metaData.getColumnType (columnNumber); String tableName = metaData.getTableName (columnNumber); String schemaName = metaData.getSchemaName (columnNumber); boolean isAutoIncrement = metaData.isAutoIncrement (columnNumber); boolean isCaseSensitive = metaData.isCaseSensitive (columnNumber); boolean isCurrency = metaData.isCurrency (columnNumber); boolean isDefiniteWritable = metaData.isDefinitelyWritable (columnNumber); boolean isReadOnly = metaData.isReadOnly (columnNumber); boolean isSearchable = metaData.isSearchable (columnNumber); boolean isReadable = metaData.isReadOnly (columnNumber); boolean isSigned = metaData.isSigned (columnNumber); boolean isWritable = metaData.isWritable (columnNumber); int nullable = metaData.isNullable (columnNumber); }

4. Navigeren door het ResultSet

Wanneer we een ResultSet, de positie van de cursor is voor de eerste rij. Bovendien is standaard het ResultSet beweegt alleen in voorwaartse richting. Maar we kunnen een scrollbaar gebruiken ResultSet voor andere navigatie-opties.

In deze sectie bespreken we de verschillende navigatie-opties.

4.1. ResultSet Types

ResultSet type geeft aan hoe we door de dataset zullen sturen:

  • TYPE_FORWARD_ONLY - de standaardoptie, waarbij de cursor van begin naar eind beweegt
  • TYPE_SCROLL_INSENSITIVE - onze cursor kan zowel voorwaarts als achterwaarts door de dataset bewegen; als er wijzigingen zijn in de onderliggende gegevens tijdens het doorlopen van de gegevensset, worden deze genegeerd; de dataset bevat de gegevens vanaf het moment dat de databasequery het resultaat retourneert
  • TYPE_SCROLL_SENSITIVE - vergelijkbaar met het scroll-ongevoelige type, maar voor dit type weerspiegelt de dataset onmiddellijk eventuele wijzigingen in de onderliggende data

Niet alle databases ondersteunen alle ResultSet types. Laten we dus controleren of het type wordt ondersteund door de supportsResultSetType op onze DatabaseMetaData voorwerp:

DatabaseMetaData dbmd = dbConnection.getMetaData (); boolean isSupported = dbmd.supportsResultSetType (ResultSet.TYPE_SCROLL_INSENSITIVE);

4.2. Schuifbare ResultSet

Om een ​​scrollbaar te krijgen ResultSet, we moeten geef enkele aanvullende parameters door tijdens het voorbereiden van het Uitspraak.

We zouden bijvoorbeeld een scrolbaar ResultSet door een van beide te gebruiken TYPE_SCROLL_INSENSITIVE of TYPE_SCROLL_SENSITIVE als een ResultSet type:

PreparedStatement pstmt = dbConnection.prepareStatement ("selecteer * uit werknemers", ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); ResultSet rs = pstmt.executeQuery (); 

4.3. Navigatie-opties

We kunnen elk van de onderstaande opties gebruiken om te scrollen ResultSet:

  • De volgende() - gaat door naar de volgende rij vanaf de huidige positie
  • vorige () - gaat naar de vorige rij
  • eerste() - navigeert naar de eerste rij van de ResultSet
  • laatste() - springt naar de laatste rij
  • beforeFirst () - beweegt naar de start; roeping De volgende() op onze ResultSet na het aanroepen van deze methode retourneert de eerste rij van onze ResultSet
  • afterLast () - springt naar het einde; roeping vorige () op onze ResultSet na het uitvoeren van deze methode retourneert de laatste rij van onze ResultSet
  • relatief (int numOfRows) - ga vooruit of achteruit vanaf de huidige positie met de aantalRijen
  • absoluut (int rowNumber) - springt naar de rij nummer gespecificeerd

Laten we een paar voorbeelden bekijken:

PreparedStatement pstmt = dbConnection.prepareStatement ("selecteer * van werknemers", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); ResultSet rs = pstmt.executeQuery (); while (rs.next ()) {// doorloop de resultaten van de eerste naar de laatste} rs.beforeFirst (); // springt terug naar het startpunt, vóór de eerste rij rs.afterLast (); // springt naar het einde van de resultatenset rs.first (); // navigeert naar de eerste rij rs.last (); // gaat naar de laatste rij rs.absolute (2); // springt naar de 2e rij rs.relative (-1); // springt naar de vorige rij rs.relative (2); // springt twee rijen vooruit terwijl (rs.previous ()) {// itereert van de huidige rij naar de eerste rij in achterwaartse richting} 

4.4. ResultSet Aantal rijen

Laten we gebruiken getRow () om het huidige rijnummer van onze te krijgen ResultSet.

Eerst navigeren we naar de laatste rij van de ResultSet en gebruik dan getRow () om het aantal records te krijgen:

rs.last (); int rowCount = rs.getRow ();

5. Gegevens bijwerken in een ResultSet

Standaard is het ResultSet is alleen-lezen. We kunnen echter een bij te werken ResultSet om de rijen in te voegen, bij te werken en te verwijderen.

5.1. ResultSet Gelijktijdigheid

De concurrency-modus geeft aan of onze ResultSet kan de gegevens bijwerken.

De CONCUR_READ_ONLY optie is de standaardoptie en moet worden gebruikt als we de gegevens niet hoeven bij te werken met onze ResultSet.

Als we de gegevens in ons ResultSet, dan de CONCUR_UPDATABLE optie moet worden gebruikt.

Niet alle databases ondersteunen alle gelijktijdigheidsmodi voor iedereen ResultSet types. Daarom moeten we controleren of ons gewenste type en gelijktijdigheidsmodus worden ondersteund met behulp van de supportsResultSetConcurrency () methode:

DatabaseMetaData dbmd = dbConnection.getMetaData (); boolean isSupported = dbmd.supportsResultSetConcurrency (ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); 

5.2. Verkrijgen van een bij te werken ResultSet

Om een ​​bij te werken ResultSet, moeten we een extra parameter doorgeven wanneer we het Uitspraak. Laten we daarvoor gebruiken CONCUR_UPDATABLE als de derde parameter bij het maken van een instructie:

PreparedStatement pstmt = dbConnection.prepareStatement ("selecteer * uit werknemers", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); ResultSet rs = pstmt.executeQuery ();

5.3. Een rij bijwerken

In deze sectie werken we een rij bij met behulp van het updatable ResultSet gemaakt in de vorige sectie.

We kunnen gegevens achter elkaar bijwerken door te bellen updateX () methoden, door de kolomnamen en waarden door te geven die moeten worden bijgewerkt. We kunnen elk ondersteund gegevenstype gebruiken in plaats van X in de updateX () methode.

Laten we het "salaris" kolom, die van het type is dubbele:

rs.updateDouble ("salaris", 1100.0);

Merk op dat dit alleen de gegevens in het ResultSet, maar de wijzigingen zijn nog niet terug in de database opgeslagen.

Laten we tot slot bellen updateRow () naar sla de updates op in de database:

rs.updateRow (); 

In plaats van de kolomnamen kunnen we de kolomindex doorgeven aan de updateX () methoden. Dit is vergelijkbaar met het gebruik van de kolomindex om de waarden op te halen met getX () methoden. De kolomnaam of index doorgeven aan het updateX () methodes levert hetzelfde resultaat op:

rs.updateDouble (4, 1100.0); rs.updateRow (); 

5.4. Een rij invoegen

Laten we nu een nieuwe rij invoegen met behulp van onze update ResultSet.

Eerst gebruiken we moveToInsertRow () om de cursor te verplaatsen om een ​​nieuwe rij in te voegen:

rs.moveToInsertRow ();

Vervolgens moeten we bellen updateX () methoden om de informatie aan de rij toe te voegen. We moeten gegevens verstrekken aan alle kolommen in de databasetabel. Als we niet voor elke kolom gegevens verstrekken, wordt de standaard kolomwaarde gebruikt:

rs.updateString ("naam", "Venkat"); rs.updateString ("positie", "DBA"); rs.updateDouble ("salaris", 925.0);

Laten we dan bellen insertRow () om een ​​nieuwe rij in de database in te voegen:

rs.insertRow ();

Laten we tot slot gebruiken moveToCurrentRow (). Dit brengt de cursorpositie terug naar de rij waar we waren voordat we begonnen met het invoegen van een nieuwe rij met de moveToInsertRow () methode:

rs.moveToCurrentRow ();

5.5. Een rij verwijderen

In deze sectie zullen we een rij verwijderen met behulp van onze update ResultSet.

Eerst gaan we naar de rij die we willen verwijderen. Dan bellen we de Verwijder rij() methode om de huidige rij te verwijderen:

rs.absolute (2); rs.deleteRow ();

6. Houdbaarheid

De houdbaarheid bepaalt of onze ResultSet zal aan het einde van een databasetransactie open of gesloten zijn.

6.1. Houdbaarheidstypen

Gebruik CLOSE_CURSORS_AT_COMMIT als het ResultSet is niet vereist nadat de transactie is vastgelegd.

Gebruik HOLD_CURSORS_OVER_COMMIT om een ​​holdable te creëren ResultSet. Een houdbaar ResultSet wordt niet gesloten, zelfs niet nadat de databasetransactie is vastgelegd.

Niet alle databases ondersteunen alle typen houdbaarheid.

Dus laten we controleer of het houdbaarheidstype wordt ondersteund gebruik makend van supportsResultSetHoldability () op onze DatabaseMetaData voorwerp. Vervolgens krijgen we de standaardhoudbaarheid van de database met behulp van getResultSetHoldability ():

boolean isCloseCursorSupported = dbmd.supportsResultSetHoldability (ResultSet.CLOSE_CURSORS_AT_COMMIT); boolean isOpenCursorSupported = dbmd.supportsResultSetHoldability (ResultSet.HOLD_CURSORS_OVER_COMMIT); boolean defaultHoldability = dbmd.getResultSetHoldability ();

6.2. Houdbaar ResultSet

Om een ​​holdable te creëren ResultSet, moeten we de houdbaarheid typ als de laatste parameter tijdens het maken van een Uitspraak. Deze parameter wordt gespecificeerd na de gelijktijdigheidsmodus.

Merk op dat als we Microsoft SQL Server (MSSQL) gebruiken, we de houdbaarheid op de databaseverbinding moeten instellen in plaats van op de ResultSet:

dbConnection.setHoldability (ResultSet.HOLD_CURSORS_OVER_COMMIT);

Laten we dit in actie zien. Laten we eerst een Uitspraak, de houdbaarheid instellen op HOLD_CURSORS_OVER_COMMIT:

Verklaring pstmt = dbConnection.createStatement (ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE, ResultSet.HOLD_CURSORS_OVER_COMMIT)

Laten we nu een rij bijwerken terwijl we de gegevens ophalen. Dit is vergelijkbaar met het updatevoorbeeld dat we eerder hebben besproken, behalve dat we het ResultSet na het doorvoeren van de updatetransactie naar de database. Dit werkt prima op zowel MySQL- als MSSQL-databases:

dbConnection.setAutoCommit (false); ResultSet rs = pstmt.executeQuery ("selecteer * van werknemers"); while (rs.next ()) {if (rs.getString ("naam"). equalsIgnoreCase ("john")) {rs.updateString ("naam", "John Doe"); rs.updateRow (); dbConnection.commit (); }} rs.last (); 

Het is vermeldenswaard dat MySQL alleen ondersteunt HOLD_CURSORS_OVER_COMMIT. Dus zelfs als we CLOSE_CURSORS_AT_COMMIT, wordt het genegeerd.

De MSSQL-database ondersteunt CLOSE_CURSORS_AT_COMMIT. Dit betekent dat de ResultSet wordt gesloten wanneer we de transactie plegen. Als gevolg hiervan probeert een poging om toegang te krijgen tot het ResultSet na het plegen van de transactie resulteert in een ‘Cursor is niet open fout’. Daarom kunnen we geen verdere records ophalen uit het ResultSet.

7. Ophaalgrootte

Wanneer u gegevens laadt in een ResultSet, beslissen de databasestuurprogramma's over het aantal rijen dat uit de database moet worden opgehaald. Op een MySQL-database, bijvoorbeeld de ResultSet laadt normaal gesproken alle records tegelijk in het geheugen.

Soms hebben we echter te maken met een groot aantal records die niet in ons JVM-geheugen passen. In dit geval kunnen we de eigenschap fetch size gebruiken op onze Uitspraak of ResultSet objecten om het aantal aanvankelijk geretourneerde records te beperken.

Wanneer aanvullende resultaten nodig zijn, ResultSet haalt nog een batch records op uit de database. Met behulp van de eigenschap ophaalgrootte kunnen we geef het databasestuurprogramma een suggestie over het aantal op te halen rijen per databasetrip. De ophaalgrootte die we specificeren, wordt toegepast op de volgende databasetrips.

Als we de ophaalgrootte voor onze ResultSeten vervolgens de ophaalgrootte van het Uitspraak is gebruikt. Als we geen ophaalgrootte specificeren voor de Uitspraak of de ResultSet, dan wordt de standaard database gebruikt.

7.1. Ophaalgrootte gebruiken aan Uitspraak

Laten we nu eens kijken naar de ophaalgrootte Uitspraak in actie. We stellen de ophaalgrootte van het Uitspraak tot 10 records. Als onze zoekopdracht 100 records retourneert, zijn er 10 database-retourvluchten, waarbij elke keer 10 records worden geladen:

PreparedStatement pstmt = dbConnection.prepareStatement ("selecteer * uit werknemers", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); pstmt.setFetchSize (10); ResultSet rs = pstmt.executeQuery (); while (rs.next ()) {// doorloop de resultatenset}

7.2. Ophaalgrootte gebruiken aan ResultSet

Laten we nu de ophaalgrootte in ons vorige voorbeeld wijzigen met de ResultSet.

Eerst gebruiken we de ophaalgrootte op onze Uitspraak. Hierdoor kunnen onze ResultSet om in eerste instantie 10 records te laden na het uitvoeren van de query.

Vervolgens wijzigen we de ophaalgrootte op het ResultSet. Dit zal de ophaalgrootte overschrijven die we eerder hebben gespecificeerd op onze Uitspraak. Alle volgende ritten laden dus 20 records totdat alle records zijn geladen.

Als gevolg hiervan zullen er slechts 6 databasetrips zijn om alle records te laden:

PreparedStatement pstmt = dbConnection.prepareStatement ("selecteer * uit werknemers", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); pstmt.setFetchSize (10); ResultSet rs = pstmt.executeQuery (); rs.setFetchSize (20); while (rs.next ()) {// doorloop de resultatenset}

Ten slotte zullen we zien hoe we de ophaalgrootte van het ResultSet terwijl de resultaten worden herhaald.

Net als in het vorige voorbeeld, stellen we eerst de ophaalgrootte in op 10 op onze Uitspraak. Onze eerste 3 databasetrips laden dus 10 records per rit.

En dan wijzigen we de ophaalgrootte op onze ResultSet tot 20 tijdens het lezen van het 30e record. De volgende 4 trips laden dus 20 records per trip.

Daarom hebben we 7 databasetrips nodig om alle 100 records te laden:

PreparedStatement pstmt = dbConnection.prepareStatement ("selecteer * uit werknemers", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); pstmt.setFetchSize (10); ResultSet rs = pstmt.executeQuery (); int rowCount = 0; while (rs.next ()) {// doorloop de resultatenset if (rowCount == 30) {rs.setFetchSize (20); } rowCount ++; }

8. Conclusie

In dit artikel hebben we gezien hoe u de ResultSet API voor het ophalen en bijwerken van gegevens uit een database. Verschillende van de geavanceerde functies die we hebben besproken, zijn afhankelijk van de database die we gebruiken. Daarom moeten we de ondersteuning voor die functies controleren voordat we ze gebruiken.

Zoals altijd is de code beschikbaar op GitHub.