1.1 Idea general
Una vez que ya tenemos la conexión con la base de datos, podemos ejecutar sentencias SQL (si la base de datos es relacional) para gestionar la información. La base de datos será quien garantice la persistencia de los datos cuando nuestro programa deje de ejecutarse.
RECUERDA: Los datos que maneja un programa se almacenan en la memoria principal del ordenador. Como esta memoria es volátil, los datos se borran al terminar la ejecución o al apagar el ordenador. La persistencia nos garantiza que podemos salvar y recuperar los datos entre ejecuciones.
1.2 Ejemplo
Insertaremos datos aprovechando la conexión que creamos con SQLite:
Copia el Proyecto
Empieza copiando el proyecto del ejercicio anterior. Nos servirá de base para este ejemplo.
Escribe el código
Programamos una prueba de concepto para realizar la inserción.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class InsertarAutor {
public static void main(String[] args) {
//1. Establecemos la URL de conexión con la base de datos
String url = "jdbc:sqlite:autores.sqlite";
//2. Definimos las sentencia SQL que queremos ejecutar.
// Los parámetros se identifican con ?
String sqlInsertar = """
INSERT INTO Autor (nombre, nacionalidad)
VALUES (?, ?);
""";
//3. Intentamos realizar la conexión en un "try con recursos"
// En el mismo try, creamos la sentencia.
// Si el try falla, la conexión y la sentencia se cerrarán correctamente
try (Connection conexion = DriverManager.getConnection(url)) {
//4. Establecemos el valor de los datos a insertar
String nombre = "Miguel de Cervantes";
String nacionalidad = "Española";
//5. Intentamos preparar la sentencia.
try (PreparedStatement sentencia = conexion.prepareStatement(
sqlInsertar,
Statement.RETURN_GENERATED_KEYS
)) {
//6. Asociamos los datos a los parámetros de la sentencia preparada
sentencia.setString(1, nombre);
sentencia.setString(2, nacionalidad);
//7. Ejecutamos la sentencia preparada
Integer filasInsertadas = sentencia.executeUpdate();
//8. Si se ha insertado, leemos el id que ha asignado la base de datos.
// y mostramos los resultados
if (filasInsertadas > 0) {
try (ResultSet clavesGeneradas = sentencia.getGeneratedKeys()) {
if (clavesGeneradas.next()) {
Integer idGenerado = clavesGeneradas.getInt(1);
System.out.println("Autor insertado correctamente.");
System.out.println("ID generado: " + idGenerado);
}
}
}
}
} catch (SQLException e) {
System.err.println("Error al insertar el autor:");
System.err.println(e.getMessage());
}
}
}
Compila y Ejecuta
Compila el programa con:
javac InsertarAutor.java
Ejecuta enlazando la librería:
(en linux) java -cp sqlite-jdbc-3.51.3.0.jar:. InsertarAutor
(en windows) java -cp sqlite-jdbc-3.51.3.0.jar;. InsertarAutor
Debes ver en la consola el mensaje "Autor insertado correctamente." y el número de id asignado.
Compruébalo
Abre la aplicación DB SQLite Browser y abre con ella nuestra base de datos. Comprueba que se han insertado los datos en la tabla.
1.3 La importancia de las sentencias preparadas
Las sentencias preparadas son fundamentales en las aplicaciones que trabajan con bases de datos. Una razón es que permiten separar claramente dos cosas:
- La estructura de la sentencia SQL.
- Los datos que se van a insertar, actualizar o consultar.
Otra es que evitan los ataques por inyección de SQL.
La inyección SQL es uno de los problemas de seguridad más graves en aplicaciones que usan bases de datos. Supongamos que construimos la consulta concatenando texto:
String sql = "INSERT INTO Autor (nombre, nacionalidad) VALUES ('"
+ nombre + "', '"
+ nacionalidad + "')";
Esto es peligroso, porque el usuario podría introducir texto malicioso. Por ejemplo, en un formulario podría escribir algo que alterase la sentencia SQL original (por ejemplo, asignar a nombre el valor ";DROP DATABASE"). En cambio, con una sentencia preparada, los valores no se interpretan como parte del SQL, sino como datos. Es decir, aunque el usuario introduzca caracteres especiales, comillas o palabras reservadas de SQL, la base de datos los tratará como texto normal. Por eso esta forma es mucho más segura.
Además, con las sentencias preparadas también evitamos problemas con los valores que tienen comillas dentro (como el apellido O'Reilly), gestionamos mejor los tipos de datos de las sentencias y podemos mejorar el rendimiento (pues la base de datos no tiene que repetir la preparación del plan de ejecución cada vez que se ejecuta la sentencia).
Por todo esto, utiliza SIEMPRE sentencias preparadas en tus programas.