JDBC (Java Database Connectivity) és l'API estàndard de Java per connectar-se a bases de dades relacionals. Forma part del JDK i defineix un conjunt d'interfícies que tots els drivers de BD relacionals implementen.
Gràcies a JDBC, el codi que escriviu per a MySQL és molt similar al que usaríeu per a PostgreSQL o Oracle: canviant el driver i la URL de connexió, la resta del codi gairebé no canvia.
Les classes principals de JDBC estan al paquet java.sql:
-- Taula principal: publicacions del fòrumpublicacio(id,nick_que_publica,text,data_publicacio,paraules_clau)-- Taula relacionada: comentaris a cada publicaciócomentari(id,nick_comenta,text,valoracio,data_comentari,publicacio_id)
La relació és 1:M (una publicació pot tenir molts comentaris).
Per crear i poblar la base de dades, executeu a MySQL:
L'opció serverTimezone=UTC evita errors de zona horària que apareixen amb versions recents del connector MySQL.
4. READ: Consultar Dades amb SELECT
4.1. El ResultSet
Quan executem un SELECT, JDBC ens retorna un ResultSet. Penseu-lo com un cursor que apunta inicialment abans de la primera fila. Hem d'anar avançant fila a fila amb next().
rs.getString("nick_que_publica")// per nom de columnars.getString(2)// per posició (la 2a columna)rs.getInt("valoracio")rs.getTimestamp("data_publicacio")
importjava.sql.*;publicclassLlistarPublicacions{publicstaticvoidmain(String[]args){// Dades de connexióStringurl="jdbc:mysql://localhost:3306/forum?useSSL=false&serverTimezone=UTC";Stringuser="root";Stringpass="1234";// SQL que volem executarStringsql="SELECT id, nick_que_publica, text, data_publicacio FROM publicacio ORDER BY data_publicacio";// try-with-resources: tanca Connection i Statement automàticamenttry(Connectionconn=DriverManager.getConnection(url,user,pass);Statementstmt=conn.createStatement();ResultSetrs=stmt.executeQuery(sql)){// Iterem fila a fila pel ResultSetwhile(rs.next()){intid=rs.getInt("id");Stringnick=rs.getString("nick_que_publica");Stringtext=rs.getString("text");Stringdata=rs.getString("data_publicacio");System.out.printf("[%d] %s (%s)%n",id,nick,data);System.out.println(" "+text);System.out.println();}}catch(SQLExceptione){// Mostrem el codi d'error SQL i el missatgeSystem.err.println("Error SQL "+e.getErrorCode()+": "+e.getMessage());}}}
5. PreparedStatement: Consultes amb Paràmetres
Quan la consulta depèn de valors que provenen de l'usuari (o de variables), mai hem de concatenar strings directament. Això podria causar un SQL Injection (atac de seguretat).
// MAL: vulnerable a SQL Injection!Stringsql="SELECT * FROM publicacio WHERE nick_que_publica = '"+nick+"'";// BÉ: usar PreparedStatement amb paràmetres (?)Stringsql="SELECT * FROM publicacio WHERE nick_que_publica = ?";
El PreparedStatement usa ? com a marcadors de posició i després omplim els valors amb els mètodes set...():
Stringsql="SELECT * FROM publicacio WHERE nick_que_publica = ?";try(Connectionconn=DriverManager.getConnection(url,user,pass);PreparedStatementpstmt=conn.prepareStatement(sql)){// Posem el valor al primer '?' (posició 1)pstmt.setString(1,"maria_dev");// Executem i processem el ResultSettry(ResultSetrs=pstmt.executeQuery()){while(rs.next()){System.out.println(rs.getString("text"));}}}catch(SQLExceptione){System.err.println("Error: "+e.getMessage());}
5.1. Exemple: Buscar publicacions per paraula clau
importjava.sql.*;publicclassBuscarPerParaulaClau{publicstaticvoidmain(String[]args){Stringurl="jdbc:mysql://localhost:3306/forum?useSSL=false&serverTimezone=UTC";Stringuser="root";Stringpass="1234";// Busquem publicacions que continguin "spring" a les paraules clauStringparaula="spring";// LIKE amb % per buscar que contingui la paraulaStringsql="SELECT id, nick_que_publica, text FROM publicacio WHERE paraules_clau LIKE ?";try(Connectionconn=DriverManager.getConnection(url,user,pass);PreparedStatementpstmt=conn.prepareStatement(sql)){// Establim el paràmetre: %spring% (qualsevol cosa, spring, qualsevol cosa)pstmt.setString(1,"%"+paraula+"%");try(ResultSetrs=pstmt.executeQuery()){System.out.println("Publicacions amb paraula clau '"+paraula+"':");System.out.println("=".repeat(50));while(rs.next()){System.out.printf(" [%d] %s%n",rs.getInt("id"),rs.getString("nick_que_publica"));System.out.println(" "+rs.getString("text"));}}}catch(SQLExceptione){System.err.println("Error: "+e.getMessage());}}}
6. CREATE: Inserir Dades amb INSERT
Per inserir dades usem executeUpdate() en lloc de executeQuery(). Aquest mètode retorna el nombre de files afectades.
importjava.sql.*;publicclassAfegirPublicacio{publicstaticvoidmain(String[]args){Stringurl="jdbc:mysql://localhost:3306/forum?useSSL=false&serverTimezone=UTC";Stringuser="root";Stringpass="1234";// INSERT amb paràmetres per a cada valor que volem inserirStringsql="INSERT INTO publicacio (nick_que_publica, text, data_publicacio, paraules_clau) "+"VALUES (?, ?, NOW(), ?)";// Statement.RETURN_GENERATED_KEYS indica que volem obtenir l'id generattry(Connectionconn=DriverManager.getConnection(url,user,pass);PreparedStatementpstmt=conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS)){// Omplim els paràmetres en ordre (posicions 1, 2, 3)pstmt.setString(1,"nou_usuari");pstmt.setString(2,"Primera publicació al fòrum! Hola a tothom.");pstmt.setString(3,"presentacio,java");// executeUpdate() retorna el nombre de files inseridesintfilesAfectades=pstmt.executeUpdate();System.out.println("Files inserides: "+filesAfectades);// Recuperem l'ID generat automàticament per MySQL (AUTO_INCREMENT)try(ResultSetclaus=pstmt.getGeneratedKeys()){if(claus.next()){intidNou=claus.getInt(1);System.out.println("Nova publicació creada amb id: "+idNou);}}}catch(SQLExceptione){System.err.println("Error en inserir: "+e.getMessage());}}}
importjava.sql.*;publicclassActualitzarParaulesClau{publicstaticvoidmain(String[]args){Stringurl="jdbc:mysql://localhost:3306/forum?useSSL=false&serverTimezone=UTC";Stringuser="root";Stringpass="1234";// Actualitzem les paraules clau d'una publicació concretaStringsql="UPDATE publicacio SET paraules_clau = ? WHERE id = ?";try(Connectionconn=DriverManager.getConnection(url,user,pass);PreparedStatementpstmt=conn.prepareStatement(sql)){// Primer paràmetre: el nou valorpstmt.setString(1,"spring,java,backend,tutorial");// Segon paràmetre: l'id de la publicació a modificarpstmt.setInt(2,1);intfilesAfectades=pstmt.executeUpdate();if(filesAfectades>0){System.out.println("Publicació actualitzada correctament.");}else{System.out.println("No s'ha trobat cap publicació amb aquest id.");}}catch(SQLExceptione){System.err.println("Error en actualitzar: "+e.getMessage());}}}
importjava.sql.*;publicclassEliminarComentari{publicstaticvoidmain(String[]args){Stringurl="jdbc:mysql://localhost:3306/forum?useSSL=false&serverTimezone=UTC";Stringuser="root";Stringpass="1234";intidComentariAEliminar=3;Stringsql="DELETE FROM comentari WHERE id = ?";try(Connectionconn=DriverManager.getConnection(url,user,pass);PreparedStatementpstmt=conn.prepareStatement(sql)){pstmt.setInt(1,idComentariAEliminar);intfilesAfectades=pstmt.executeUpdate();if(filesAfectades>0){System.out.println("Comentari "+idComentariAEliminar+" eliminat.");}else{System.out.println("No s'ha trobat el comentari "+idComentariAEliminar);}}catch(SQLExceptione){System.err.println("Error en eliminar: "+e.getMessage());}}}
9. Consulta amb JOIN: Publicació i els seus Comentaris
Un dels punts forts de les bases de dades relacionals és poder unir taules. Veiem com obtenir una publicació amb tots els seus comentaris:
importjava.sql.*;publicclassPublicacioAmbComentaris{publicstaticvoidmain(String[]args){Stringurl="jdbc:mysql://localhost:3306/forum?useSSL=false&serverTimezone=UTC";Stringuser="root";Stringpass="1234";intidPublicacio=1;// Primer obtenim la publicacióStringsqlPub="SELECT * FROM publicacio WHERE id = ?";// Després obtenim tots els comentaris d'aquesta publicació, ordenats per dataStringsqlCom="SELECT * FROM comentari WHERE publicacio_id = ? ORDER BY data_comentari";try(Connectionconn=DriverManager.getConnection(url,user,pass)){// --- Llegim la publicació ---try(PreparedStatementpstmt=conn.prepareStatement(sqlPub)){pstmt.setInt(1,idPublicacio);try(ResultSetrs=pstmt.executeQuery()){if(rs.next()){System.out.println("=== PUBLICACIÓ ===");System.out.println("Nick: "+rs.getString("nick_que_publica"));System.out.println("Data: "+rs.getString("data_publicacio"));System.out.println("Text: "+rs.getString("text"));System.out.println("Tags: "+rs.getString("paraules_clau"));}}}System.out.println();System.out.println("--- COMENTARIS ---");// --- Llegim els comentaris ---try(PreparedStatementpstmt=conn.prepareStatement(sqlCom)){pstmt.setInt(1,idPublicacio);try(ResultSetrs=pstmt.executeQuery()){while(rs.next()){Stringnick=rs.getString("nick_comenta");Stringtext=rs.getString("text");intvaloracio=rs.getInt("valoracio");Stringdata=rs.getString("data_comentari");// Representem la valoració amb estrellesStringestrelles="★".repeat(valoracio)+"☆".repeat(5-valoracio);System.out.printf("%s [%s] (%s)%n",estrelles,nick,data);System.out.println(" "+text);}}}}catch(SQLExceptione){System.err.println("Error: "+e.getMessage());}}}
10. Resum dels Mètodes Principals
10.1. Connection
Mètode
Descripció
createStatement()
Crea un Statement per a SQL sense paràmetres
prepareStatement(sql)
Crea un PreparedStatement per a SQL amb ?
close()
Tanca la connexió (automàtic amb try-with-resources)