Introduksjon til JDBC

Java Top

Jeg kunngjorde nettopp det nye Lær våren kurs, med fokus på det grunnleggende i vår 5 og vårstøvel 2:

>> KONTROLLER KURSET

1. Oversikt

I denne artikkelen skal vi ta en titt på JDBC (Java Database Connectivity) som er et API for tilkobling og utføring av spørsmål i en database.

JDBC kan arbeide med hvilken som helst database så lenge riktige drivere er gitt.

2. JDBC-drivere

En JDBC-driver er en JDBC API-implementering som brukes til å koble til en bestemt type database. Det finnes flere typer JDBC-drivere:

  • Type 1 - inneholder en kartlegging til et annet API for datatilgang; et eksempel på dette er JDBC-ODBC-driveren
  • Type 2 - er en implementering som bruker klientsidebiblioteker i måldatabasen; også kalt en native-API-driver
  • Type 3 - bruker mellomvare for å konvertere JDBC-anrop til databasespesifikke samtaler; også kjent som en nettverksprotokolldriver
  • Type 4 - koble direkte til en database ved å konvertere JDBC-anrop til databasespesifikke samtaler; kjent som databaseprotokolldrivere eller tynne drivere,

Den mest brukte typen er type 4, da den har fordelen av å være plattformuavhengig. Koble direkte til en databaseserver gir bedre ytelse sammenlignet med andre typer. Ulempen med denne typen drivere er at den er databasespesifikk - gitt at hver database har sin egen spesifikke protokoll.

3. Koble til en database

For å koble til en database, må vi bare initialisere driveren og åpne en databaseforbindelse.

3.1. Registrere sjåføren

For vårt eksempel vil vi bruke en type 4 databaseprotokolldriver.

Siden vi bruker en MySQL-database, trenger vi mysql-connector-java avhengighet:

 mysql mysql-connector-java 6.0.6 

La oss deretter registrere driveren ved hjelp av Class.forName () metode, som dynamisk laster førerklassen:

Class.forName ("com.mysql.cj.jdbc.Driver");

I eldre versjoner av JDBC, før vi fikk en forbindelse, måtte vi først initialisere JDBC-driveren ved å ringe Class.forName metode. Fra og med JDBC 4.0, alle drivere som finnes på klassestien lastes automatisk inn. Derfor trenger vi ikke dette Class.forName del i moderne miljøer.

3.2. Opprette tilkoblingen

For å åpne en forbindelse kan vi bruke getConnection () Metode av DriverManager klasse. Denne metoden krever en tilkoblings-URL String parameter:

prøv (Connection con = DriverManager .getConnection ("jdbc: mysql: // localhost: 3306 / myDb", "user1", "pass")) {// bruk con her}

Siden Forbindelse er en Kan lukkes automatisk ressurs, bør vi bruke den i en prøv-med-ressurser blokkere.

Syntaksen til tilkoblings-URL-en avhenger av hvilken type database som brukes. La oss se på noen få eksempler:

jdbc: mysql: // localhost: 3306 / myDb? user = user1 & password = pass
jdbc: postgresql: // localhost / myDb
jdbc: hsqldb: mem: myDb

For å koble til det angitte myDb database, må vi opprette databasen og en bruker, og legge til nødvendig tilgang:

OPPRETT DATABASIS myDb; OPPRETT BRUKER 'bruker1' IDENTIFISERT MED 'pass'; GRANT ALLE på myDb. * TIL 'bruker1';

4. Utføre SQL-setninger

Send SQL-instruksjonene til databasen, vi kan bruke forekomster av typen Uttalelse, Forberedt uttalelse, eller CallableStatement, som vi kan få tak i ved hjelp av Forbindelse gjenstand.

4.1. Uttalelse

De Uttalelse grensesnittet inneholder de viktigste funksjonene for å utføre SQL-kommandoer.

La oss først lage en Uttalelse gjenstand:

prøv (Statement stmt = con.createStatement ()) {// bruk stmt her}

Igjen, vi skal jobbe med Uttalelses inni a prøv-med-ressurser blokk for automatisk ressursadministrasjon.

Uansett kan du utføre SQL-instruksjoner ved hjelp av tre metoder:

  • executeQuery () for SELECT instruksjoner
  • executeUpdate () for oppdatering av dataene eller databasestrukturen
  • henrette() kan brukes i begge tilfeller ovenfor når resultatet er ukjent

La oss bruke henrette() metode for å legge til en studenter tabell til databasen vår:

String tableSql = "OPPRETT TABELL HVIS IKKE FINNER ansatte" + "(emp_id int PRIMÆRE NØKKEL AUTO_INCREMENT, navn varchar (30)," + "stilling varchar (30), lønn dobbelt)"; stmt.execute (tableSql);

Når du bruker henrette() metode for å oppdatere dataene, deretter stmt.getUpdateCount () metoden returnerer antall berørte rader.

Hvis resultatet er 0, ble ingen rader påvirket, eller det var en oppdateringskommando for databasestruktur.

Hvis verdien er -1, var kommandoen et SELECT-spørsmål; vi kan da oppnå resultatet ved hjelp av stmt.getResultSet ().

La oss deretter legge til en post i tabellen vår ved hjelp av executeUpdate () metode:

String insertSql = "INSERT INTO ansatte (navn, stilling, lønn)" + "VERDIER ('john', 'utvikler', 2000)"; stmt.executeUpdate (insertSql);

Metoden returnerer antall berørte rader for en kommando som oppdaterer rader eller 0 for en kommando som oppdaterer databasestrukturen.

Vi kan hente postene fra tabellen ved hjelp av executeQuery () metode som returnerer et objekt av typen ResultatSett:

String selectSql = "VELG * FRA ansatte"; prøv (ResultSet resultSet = stmt.executeQuery (selectSql)) {// bruk resultSet her}

Vi bør sørge for å lukke ResultatSett tilfeller etter bruk. Ellers kan vi holde den underliggende markøren åpen i en mye lengre periode enn forventet. For å gjøre det, anbefales det å bruke en prøv-med-ressurser blokk, som i eksemplet vårt ovenfor.

4.2. PreparedStatement

PreparedStatement objekter inneholder forhåndskompilerte SQL-sekvenser. De kan ha en eller flere parametere angitt med et spørsmålstegn.

La oss lage en PreparedStatement som oppdaterer poster i ansatte tabell basert på gitte parametere:

Streng updatePositionSql = "OPPDATER ansatte STILLE stilling =? HVOR emp_id =?"; prøv (PreparedStatement pstmt = con.prepareStatement (updatePositionSql)) {// bruk pstmt her}

For å legge til parametere i PreparedStatement, kan vi bruke enkle settere - setX () - der X er typen parameter, og metodeargumentene er rekkefølgen og verdien til parameteren:

pstmt.setString (1, "lead developer"); pstmt.setInt (2, 1);

Uttalelsen utføres med en av de samme tre metodene som er beskrevet tidligere: executeQuery (), executeUpdate (), execute () uten SQL String parameter:

int rowsAffected = pstmt.executeUpdate ();

4.3. CallableStatement

De CallableStatement grensesnitt tillater anrop lagrede prosedyrer.

Å lage en CallableStatement objekt, kan vi bruke prepareCall () Metode av Forbindelse:

String preparedSql = "{ring innsettmedarbeider (?,?,?,?)}"; prøv (CallableStatement cstmt = con.prepareCall (preparedSql)) {// bruk cstmt her}

Innstilling av inngangsparameterverdier for den lagrede prosedyren gjøres som i PreparedStatement grensesnitt, ved hjelp av setX () metoder:

cstmt.setString (2, "ana"); cstmt.setString (3, "tester"); cstmt.setDouble (4, 2000);

Hvis den lagrede prosedyren har utdata-parametere, må vi legge dem til med registerOutParameter () metode:

cstmt.registerOutParameter (1, Types.INTEGER);

La oss så utføre uttalelsen og hente den returnerte verdien ved hjelp av en tilsvarende getX () metode:

cstmt.execute (); int new_id = cstmt.getInt (1);

For eksempel for å jobbe, må vi opprette den lagrede prosedyren i MySql-databasen:

skilletegn // OPPRETT PROSEDYRE insertMedarbeider (UT emp_id int, IN emp_name varchar (30), IN stilling varchar (30), IN lønn dobbelt) BEGIN INSERT INTO workers (name, position, lønn) VERDIER (emp_name, posisjon, lønn); SETT emp_id = LAST_INSERT_ID (); SLUTT // avgrenser;

De sett inn Ansatt prosedyren ovenfor vil sette inn en ny post i ansatte tabellen ved hjelp av de gitte parametrene og returnerer id for den nye posten i emp_id ut parameter.

For å kunne kjøre en lagret prosedyre fra Java, må tilkoblingsbrukeren ha tilgang til den lagrede prosedyrens metadata. Dette kan oppnås ved å gi rettigheter til brukeren på alle lagrede prosedyrer i alle databaser:

TILGJENG ALT PÅ mysql.proc TIL 'bruker1';

Alternativt kan vi åpne forbindelsen til eiendommen noAccessToProcedureBodies satt til ekte:

con = DriverManager.getConnection ("jdbc: mysql: // localhost: 3306 / myDb? noAccessToProcedureBodies = true", "user1", "pass");

Dette vil informere JDBC API om at brukeren ikke har rettighetene til å lese prosedyrens metadata, slik at den vil opprette alle parametere som INOUT String parametere.

5. Analyse av søkeresultater

Etter å ha utført en spørring, blir resultatet representert med a ResultatSett objekt, som har en struktur som ligner på en tabell, med linjer og kolonner.

5.1. ResultatSett Grensesnitt

De ResultatSett bruker neste () metode for å gå til neste linje.

La oss først lage en Ansatt klasse for å lagre våre hentede poster:

offentlig klasse ansatt {privat int id; privat strengnavn; privat strengposisjon; privat dobbeltlønn; // standard konstruktør, getters, setters}

Neste, la oss krysse ResultatSett og lage en Ansatt objekt for hver post:

String selectSql = "VELG * FRA ansatte"; prøv (ResultSet resultSet = stmt.executeQuery (selectSql)) {List ansatte = ny ArrayList (); mens (resultSet.next ()) {Ansattes emp = nyansatt (); emp.setId (resultSet.getInt ("emp_id")); emp.setName (resultSet.getString ("navn")); emp.setPosition (resultSet.getString ("posisjon")); emp.setSalary (resultSet.getDouble ("lønn")); ansatte. legger til (emp); }}

Å hente verdien for hver tabellcelle kan gjøres ved hjelp av typemetoder getX () hvor X representerer typen celledata.

De getX () metoder kan brukes med en int parameter som representerer rekkefølgen på cellen, eller a String parameter som representerer kolonnens navn. Det siste alternativet er å foretrekke hvis vi endrer rekkefølgen på kolonnene i spørringen.

5.2. Oppdaterbar ResultatSett

Implisitt, a ResultatSett objektet kan bare krysses fremover og kan ikke endres.

Hvis vi vil bruke ResultatSett For å oppdatere data og krysse dem i begge retninger, må vi lage Uttalelse objekt med tilleggsparametere:

stmt = con.createStatement (ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);

For å navigere i denne typen ResultatSett, kan vi bruke en av metodene:

  • first (), last (), beforeFirst (), beforeLast () - for å gå til første eller siste linje i en ResultatSett eller til linjen før disse
  • neste (), forrige () - for å navigere frem og tilbake i ResultatSett
  • getRow () - for å få gjeldende radnummer
  • moveToInsertRow (), moveToCurrentRow () - for å flytte til en ny tom rad for å sette inn og tilbake til den nåværende hvis den er på en ny rad
  • absolutt (int rad) - for å flytte til den angitte raden
  • relativ (int nrRows) - for å flytte markøren det gitte antall rader

Oppdaterer ResultatSett kan gjøres ved hjelp av metoder med formatet updateX () hvor X er typen celledata. Disse metodene oppdaterer bare ResultatSett objekt og ikke databasetabellene.

For å vedvare ResultatSett endringer i databasen, må vi videre bruke en av metodene:

  • updateRow () - for å vedvare endringene i gjeldende rad i databasen
  • insertRow (), deleteRow () - for å legge til en ny rad eller slette den gjeldende fra databasen
  • refreshRow () - for å oppdatere ResultatSett med eventuelle endringer i databasen
  • cancelRowUpdates () - for å avbryte endringer i gjeldende rad

La oss se på et eksempel på bruk av noen av disse metodene ved å oppdatere data i ansattes bord:

prøv (Statement updatableStmt = con.createStatement (ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE)) {try (ResultSet updatableResultSet = updatableStmt.executeQuery (selectSql)) {updatableResultInset updatableResultSet.updateString ("navn", "merke"); updatableResultSet.updateString ("posisjon", "analytiker"); updatableResultSet.updateDouble ("lønn", 2000); updatableResultSet.insertRow (); }}

6. Analyse av metadata

JDBC API lar deg slå opp informasjon om databasen, kalt metadata.

6.1. DatabaseMetadata

De DatabaseMetadata grensesnitt kan brukes til å skaffe generell informasjon om databasen, for eksempel tabeller, lagrede prosedyrer eller SQL-dialekt.

La oss se raskt på hvordan vi kan hente informasjon i databasetabellene:

DatabaseMetaData dbmd = con.getMetaData (); ResultSet tablesResultSet = dbmd.getTables (null, null, "%", null); mens (tablesResultSet.next ()) {LOG.info (tablesResultSet.getString ("TABLE_NAME")); }

6.2. ResultatSetMetadata

Dette grensesnittet kan brukes til å finne informasjon om et bestemt ResultatSett, for eksempel nummeret og navnet på kolonnene:

ResultSetMetaData rsmd = rs.getMetaData (); int nrColumn = rsmd.getColumnCount (); IntStream.range (1, nrColumns) .forEach (i -> {try {LOG.info (rsmd.getColumnName (i));} catch (SQLException e) {e.printStackTrace ();}});

7. Håndtering av transaksjoner

Som standard blir hver SQL-setning begått rett etter at den er fullført. Det er imidlertid også mulig å kontrollere transaksjoner programmatisk.

Dette kan være nødvendig i tilfeller der vi ønsker å bevare datakonsistensen, for eksempel når vi bare vil begå en transaksjon hvis en tidligere har fullført.

Først må vi stille inn autoCommit tilhører Forbindelse til falsk, bruk deretter begå() og tilbakeføring () metoder for å kontrollere transaksjonen.

La oss legge til en ny oppdateringserklæring for lønn kolonne etter den ansatte posisjon kolonneoppdatering og pakk dem begge inn i en transaksjon. På denne måten vil lønnen bare oppdateres hvis stillingen ble oppdatert:

Streng updatePositionSql = "OPPDATER ansatte SETT stilling =? HVOR emp_id =?"; PreparedStatement pstmt = con.prepareStatement (updatePositionSql); pstmt.setString (1, "lead developer"); pstmt.setInt (2, 1); String updateSalarySql = "OPPDATER ansatte SETT lønn =? HVOR emp_id =?"; PreparedStatement pstmt2 = con.prepareStatement (updateSalarySql); pstmt.setDouble (1, 3000); pstmt.setInt (2, 1); boolsk autoCommit = con.getAutoCommit (); prøv {con.setAutoCommit (false); pstmt.executeUpdate (); pstmt2.executeUpdate (); con.commit (); } fangst (SQLException exc) {con.rollback (); } til slutt {con.setAutoCommit (autoCommit); }

For korthets skyld, utelater vi prøv-med-ressurser blokker her.

8. Avslutte ressursene

Når vi ikke lenger bruker det, vi må lukke forbindelsen for å frigjøre databaseressurser.

Vi kan gjøre dette ved hjelp av Lukk() API:

lukk ();

Imidlertid, hvis vi bruker ressursen i en prøv-med-ressurser blokkere, vi trenger ikke å ringe Lukk() metoden eksplisitt, som prøv-med-ressurser block gjør det for oss automatisk.

Det samme gjelder for Uttalelses, PreparedStatements, CallableStatements, og ResultatSetts.

9. Konklusjon

I denne veiledningen så vi på det grunnleggende om å jobbe med JDBC API.

Som alltid kan hele kildekoden til eksemplene finnes på GitHub.

Java bunn

Jeg kunngjorde nettopp det nye Lær våren kurs, med fokus på det grunnleggende i vår 5 og vårstøvel 2:

>> KONTROLLER KURSET

$config[zx-auto] not found$config[zx-overlay] not found