Vejledning til JDBC ResultSet-interface

1. Oversigt

Java Database Connectivity (JDBC) API giver adgang til databasen fra et Java-program. Vi kan bruge JDBC til at oprette forbindelse til enhver database, så længe den understøttede JDBC-driver er tilgængelig.

Det ResultSet er en tabel over data genereret ved udførelse af databaseforespørgsler. I denne vejledning vil vi se nærmere på ResultSet API.

2. Generering af en ResultSet

Først henter vi en ResultSet ved at ringe executeQuery () på ethvert objekt, der implementerer Udmelding interface. Både PreparedStatement og CallableStatement er undergrænseflader til Udmelding:

PreparedStatement pstmt = dbConnection.prepareStatement ("vælg * blandt medarbejdere"); ResultSet rs = pstmt.executeQuery ();

Det ResultSet objekt opretholder en markør, der peger på den aktuelle række i resultatsættet. Vi bruger Næste() på vores ResultSet at gentage gennem optegnelserne.

Derefter skal vi brug getX () metoder, mens det gentages gennem resultaterne for at hente værdierne fra databasekolonnerne, hvor x er kolonnens datatype. Faktisk giver vi databasekolonnavne til getX () metoder:

while (rs.next ()) {String name = rs.getString ("name"); Heltal empId = rs.getInt ("emp_id"); Dobbelt løn = rs.getDouble ("løn"); Strengposition = rs.getString ("position"); } 

Ligeledes, kolonnens indeksnummer kan bruges sammen med getX () metoder i stedet for kolonnenavnet. Indeksnummeret er rækkefølgen af ​​kolonnerne i SQL select-sætningen.

Hvis det valgte udsagn ikke viser kolonnenavne, er indeksnummeret rækkefølgen af ​​kolonner i tabellen. Kolonneindeksnummereringen starter fra en:

Heltal empId = rs.getInt (1); String name = rs.getString (2); Strengposition = rs.getString (3); Dobbelt løn = rs.getDouble (4); 

3. Henter MetaData fra ResultSet

I dette afsnit vil vi se, hvordan man henter oplysninger om kolonneegenskaber og typer i en ResultSet.

Lad os først bruge getMetaData () metode på vores ResultSet for at opnå ResultatSetMetaData:

ResultSetMetaData metaData = rs.getMetaData ();

Lad os derefter få antallet af kolonner, der er i vores ResultSet:

Heltal columnCount = metaData.getColumnCount ();

Desuden kan vi bruge en af ​​nedenstående metoder på vores metadataobjekt til at hente egenskaber for hver kolonne:

  • getColumnName (int columnNumber) for at få navnet på kolonnen
  • getColumnLabel (int columnNumber) for at få adgang til kolonnens etiket, som er angivet efter SOM i SQL-forespørgslen
  • getTableName (int columnNumber) for at få det tabelnavn, som denne kolonne tilhører
  • getColumnClassName (int columnNumber) for at erhverve Java-datatypen for kolonnen
  • getColumnTypeName (int columnNumber) for at hente datatypen for kolonnen i databasen
  • getColumnType (int columnNumber) for at få SQL-datatypen for kolonnen
  • isAutoIncrement (int columnNumber) angiver, om kolonnen er automatisk forøgelse
  • isCaseSensitive (int columnNumber) angiver, om søjlesagen betyder noget
  • kan søges (int columnNumber) foreslår, om vi kan bruge kolonnen i hvor klausul i SQL-forespørgslen
  • isCurrency (int columnNumber) signalerer, hvis kolonnen indeholder en kontant værdi
  • isNullable (int columnNumber) vender tilbage nul hvis kolonnen ikke kan være nul, en hvis kolonnen kan indeholde en nulværdi, og to hvis kolonnens ugyldighed er ukendt
  • isSigned (int columnNumber) vender tilbage rigtigt hvis værdier i kolonnen er underskrevet, returneres ellers falsk

Lad os gentage gennem kolonnerne for at få deres egenskaber:

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

4. Navigering i ResultSet

Når vi får en ResultSet, markørens position er før første række. Desuden er som standard ResultSet bevæger sig kun i fremadgående retning. Men vi kan bruge en rullbar ResultSet for andre navigationsmuligheder.

I dette afsnit vil vi diskutere de forskellige navigationsmuligheder.

4.1. ResultSet Typer

ResultSet type angiver, hvordan vi styrer datasættet:

  • TYPE_FORWARD_ONLY - standardindstillingen, hvor markøren bevæger sig fra start til slut
  • TYPE_SCROLL_INSENSITIVE - vores markør kan bevæge sig gennem datasættet i både retning fremad og bagud; hvis der er ændringer i de underliggende data, mens de bevæger sig gennem datasættet, ignoreres de; datasættet indeholder data fra det tidspunkt, hvor databaseforespørgslen returnerer resultatet
  • TYPE_SCROLL_SENSITIVE - svarende til den ufølsomme rulle-type, men for denne type afspejler datasættet straks eventuelle ændringer i de underliggende data

Ikke alle databaser understøtter alle ResultSet typer. Så lad os kontrollere, om typen understøttes ved hjælp af supportsResultSetType på vores DatabaseMetaData objekt:

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

4.2. Scrollable ResultSet

For at få en rullbar ResultSet, vi er nødt til videregive nogle yderligere parametre, mens du forbereder Udmelding.

For eksempel ville vi få en rullbar ResultSet ved hjælp af en af ​​dem TYPE_SCROLL_INSENSITIVE eller TYPE_SCROLL_SENSITIVE som en ResultSet type:

PreparedStatement pstmt = dbConnection.prepareStatement ("vælg * fra medarbejdere", ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); ResultSet rs = pstmt.executeQuery (); 

4.3. Navigationsmuligheder

Vi kan bruge en af ​​nedenstående muligheder på en rullbar ResultSet:

  • Næste() - fortsætter til næste række fra den aktuelle position
  • Tidligere() - krydser til forrige række
  • første () - navigerer til den første række i ResultSet
  • sidste () - springer til sidste række
  • beforeFirst () - bevæger sig til starten; ringer Næste() på vores ResultSet efter at have kaldt denne metode returnerer den første række fra vores ResultSet
  • afterLast () - spring til slutningen ringer forrige () på vores ResultSet efter udførelse af denne metode returnerer den sidste række fra vores ResultSet
  • relativ (int numOfRows) - gå frem eller tilbage fra den aktuelle position ved at numOfRows
  • absolut (int rowNumber) - springer til rækkeNummer angivet

Lad os se nogle eksempler:

PreparedStatement pstmt = dbConnection.prepareStatement ("vælg * fra medarbejdere", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); ResultSet rs = pstmt.executeQuery (); mens (rs.next ()) {// gentager resultaterne fra første til sidste} rs.beforeFirst (); // springer tilbage til startpunktet, før første række rs.afterLast (); // springer til slutningen af ​​resultatsæt rs.first (); // navigerer til første række rs.last (); // går til den sidste række rs.absolute (2); // springer til 2. række rs.relativ (-1); // springer til forrige række rs.relative (2); // springer to rækker fremad, mens (rs.previous ()) {// itererer fra den aktuelle række til den første række i bagudgående retning} 

4.4. ResultSet Rækketælling

Lad os bruge getRow () for at få det aktuelle række nummer på vores ResultSet.

Først navigerer vi til den sidste række i ResultSet og brug derefter getRow () for at få antallet af poster:

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

5. Opdatering af data i en ResultSet

Som standard er ResultSet er skrivebeskyttet. Vi kan dog bruge en opdaterbar ResultSet for at indsætte, opdatere og slette rækkerne.

5.1. ResultSet Samtidighed

Samtidig tilstand angiver, om vores ResultSet kan opdatere dataene.

Det CONCUR_READ_ONLY option er standard og skal bruges, hvis vi ikke har brug for at opdatere data ved hjælp af vores ResultSet.

Men hvis vi har brug for at opdatere dataene i vores ResultSet, derefter CONCUR_UPDATABLE mulighed skal bruges.

Ikke alle databaser understøtter alle samtidige tilstande for alle ResultSet typer. Derfor skal vi kontrollere, om vores ønskede type og samtidighedstilstand understøttes ved hjælp af supportsResultSetConcurrency () metode:

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

5.2. Opnåelse af en opdaterbar ResultSet

For at få en opdaterbar ResultSet, skal vi videregive en ekstra parameter, når vi forbereder Udmelding. Lad os bruge det til det CONCUR_UPDATABLE som den tredje parameter, mens du opretter en erklæring:

PreparedStatement pstmt = dbConnection.prepareStatement ("vælg * fra medarbejdere", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); ResultSet rs = pstmt.executeQuery ();

5.3. Opdatering af en række

I dette afsnit opdaterer vi en række ved hjælp af opdateringen ResultSet oprettet i det foregående afsnit.

Vi kan opdatere data i træk ved at ringe updateX () metoder, videregive kolonnenavne og værdier, der skal opdateres. Vi kan bruge enhver understøttet datatype i stedet for x i updateX () metode.

Lad os opdatere "løn" kolonne, som er af typen dobbelt:

rs.updateDouble ("løn", 1100.0);

Bemærk, at dette bare opdaterer dataene i ResultSet, men ændringerne er endnu ikke gemt tilbage i databasen.

Lad os endelig ringe updateRow () til gem opdateringerne i databasen:

rs.updateRow (); 

I stedet for kolonnenavnene kan vi videregive kolonneindekset til updateX () metoder. Dette svarer til at bruge kolonneindekset til at få værdierne ved hjælp af getX () metoder. Videregiver enten kolonnenavnet eller indekset til updateX () metoder giver det samme resultat:

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

5.4. Indsættelse af en række

Lad os nu indsætte en ny række ved hjælp af vores opdaterbare ResultSet.

Først bruger vi moveToInsertRow () for at flytte markøren for at indsætte en ny række:

rs.moveToInsertRow ();

Derefter skal vi ringe updateX () metoder til at tilføje oplysningerne til rækken. Vi skal levere data til alle kolonnerne i databasetabellen. Hvis vi ikke leverer data til hver kolonne, bruges standardkolonneværdien:

rs.updateString ("navn", "Venkat"); rs.updateString ("position", "DBA"); rs.updateDouble ("løn", 925.0);

Lad os så ringe insertRow () at indsætte en ny række i databasen:

rs.insertRow ();

Lad os endelig bruge moveToCurrentRow (). Dette tager markørpositionen tilbage til den række, vi var i, før vi begyndte at indsætte en ny række ved hjælp af moveToInsertRow () metode:

rs.moveToCurrentRow ();

5.5. Sletning af en række

I dette afsnit sletter vi en række ved hjælp af vores opdaterbare ResultSet.

Først navigerer vi til den række, vi vil slette. Så ringer vi til deleteRow () metode til at slette den aktuelle række:

rs.absolut (2); rs.deleteRow ();

6. Holdbarhed

Holdbarheden bestemmer, om vores ResultSet vil være åben eller lukket i slutningen af ​​en databasetransaktion.

6.1. Holdbarhedstyper

Brug CLOSE_CURSORS_AT_COMMIT hvis den ResultSet er ikke påkrævet efter transaktionen er begået.

Brug HOLD_CURSORS_OVER_COMMIT for at skabe en holdbar ResultSet. En holdbar ResultSet lukkes ikke, selv efter at databasetransaktionen er begået.

Ikke alle databaser understøtter alle holdbarhedstyper.

Så lad os Kontroller, om holdbarhedstypen understøttes ved brug af supportsResultSetHoldability () på vores DatabaseMetaData objekt. Derefter får vi standardholdbarheden af ​​databasen ved hjælp af getResultSetHoldability ():

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

6.2. Kan holdes ResultSet

At skabe en holdbar ResultSet, skal vi specificere holdbarhed skriv som den sidste parameter, mens du opretter en Udmelding. Denne parameter er angivet efter samtidighedstilstand.

Bemærk, at hvis vi bruger Microsoft SQL Server (MSSQL), skal vi indstille holdbarhed på databaseforbindelsen snarere end på ResultSet:

dbConnection.setHoldability (ResultSet.HOLD_CURSORS_OVER_COMMIT);

Lad os se dette i aktion. Lad os først oprette en Udmelding, indstiller holdbarheden til HOLD_CURSORS_OVER_COMMIT:

Erklæring pstmt = dbConnection.createStatement (ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE, ResultSet.HOLD_CURSORS_OVER_COMMIT)

Lad os nu opdatere en række, mens vi henter dataene. Dette svarer til opdateringseksemplet, vi diskuterede tidligere, bortset fra at vi fortsætter med at gentage gennem ResultSet efter at have begået opdateringstransaktionen til databasen. Dette fungerer fint på både MySQL- og MSSQL-databaser:

dbConnection.setAutoCommit (false); ResultSet rs = pstmt.executeQuery ("vælg * blandt medarbejdere"); while (rs.next ()) {if (rs.getString ("name"). equalsIgnoreCase ("john")) {rs.updateString ("name", "John Doe"); rs.updateRow (); dbConnection.commit (); }} rs.last (); 

Det er værd at bemærke, at MySQL kun understøtter HOLD_CURSORS_OVER_COMMIT. Så selvom vi bruger CLOSE_CURSORS_AT_COMMIT, det vil blive ignoreret.

MSSQL-databasen understøtter CLOSE_CURSORS_AT_COMMIT. Dette betyder, at ResultSet lukkes, når vi begår transaktionen. Som et resultat, et forsøg på at få adgang til ResultSet efter at have begået, resulterer transaktionen i en 'Fejl ved markør er ikke åben'. Derfor kan vi ikke hente flere poster fra ResultSet.

7. Hent størrelse

Normalt når data indlæses i en ResultSet, beslutter databasedrivere antallet af rækker, der skal hentes fra databasen. På en MySQL-database f.eks ResultSet indlæser normalt alle poster i hukommelsen på én gang.

Nogle gange er vi dog muligvis nødt til at håndtere et stort antal poster, der ikke passer ind i vores JVM-hukommelse. I dette tilfælde kan vi bruge egenskaben fetch size enten på vores Udmelding eller ResultSet objekter for at begrænse antallet af poster, der oprindeligt blev returneret.

Når der kræves yderligere resultater, ResultSet henter en anden batch af poster fra databasen. Ved hjælp af egenskaben fetch size kan vi give et forslag til databasedriveren om antallet af rækker, der skal hentes pr. databasetur. Den hentningsstørrelse, vi angiver, vil blive anvendt på de efterfølgende databaseture.

Hvis vi ikke angiver hentestørrelsen for vores ResultSet, derefter hentningsstørrelsen på Udmelding anvendes. Hvis vi ikke angiver hentestørrelse for hverken Udmelding eller den ResultSet, så bruges databasestandarden.

7.1. Brug af hentestørrelse til Udmelding

Lad os nu se hentningsstørrelsen på Udmelding i aktion. Vi indstiller hentningsstørrelsen på Udmelding til 10 poster. Hvis vores forespørgsel returnerer 100 poster, er der 10 database rundrejser, der indlæser 10 poster hver gang:

PreparedStatement pstmt = dbConnection.prepareStatement ("vælg * fra medarbejdere", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); pstmt.setFetchSize (10); ResultSet rs = pstmt.executeQuery (); mens (rs.next ()) {// gentages gennem resultatsættet}

7.2. Brug af hentestørrelse til ResultSet

Lad os nu ændre hentningsstørrelsen i vores tidligere eksempel ved hjælp af ResultSet.

Først bruger vi hentestørrelsen på vores Udmelding. Dette tillader vores ResultSet til oprindeligt at indlæse 10 poster efter udførelse af forespørgslen.

Derefter ændrer vi hentningsstørrelsen på ResultSet. Dette tilsidesætter den hentningsstørrelse, vi tidligere har angivet på vores Udmelding. Så alle efterfølgende ture indlæser 20 poster, indtil alle poster er indlæst.

Som et resultat vil der kun være 6 databaseture for at indlæse alle poster:

PreparedStatement pstmt = dbConnection.prepareStatement ("vælg * fra medarbejdere", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); pstmt.setFetchSize (10); ResultSet rs = pstmt.executeQuery (); rs.setFetchSize (20); mens (rs.next ()) {// gentages gennem resultatsættet}

Endelig vil vi se, hvordan du ændrer hentningsstørrelsen på ResultSet mens det gentager resultaterne.

I lighed med det foregående eksempel indstiller vi først hentningsstørrelsen til 10 på vores Udmelding. Så vores første 3 databaseture indlæser 10 poster pr. Tur.

Og så ændrer vi hentningsstørrelsen på vores ResultSet til 20, mens du læser den 30. post. Så de næste 4 ture indlæser 20 poster pr. Tur.

Derfor har vi brug for 7 databaseture for at indlæse alle 100 poster:

PreparedStatement pstmt = dbConnection.prepareStatement ("vælg * fra medarbejdere", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); pstmt.setFetchSize (10); ResultSet rs = pstmt.executeQuery (); int rowCount = 0; mens (rs.next ()) {// gentages gennem resultatsættet, hvis (rowCount == 30) {rs.setFetchSize (20); } rækkeCount ++; }

8. Konklusion

I denne artikel så vi, hvordan man bruger ResultSet API til at hente og opdatere data fra en database. Flere af de avancerede funktioner, vi diskuterede, afhænger af den database, vi bruger. Derfor er vi nødt til at kontrollere supporten til disse funktioner, før vi bruger dem.

Som altid er koden tilgængelig på GitHub.