09.Bases de Datos

Bases de Datos

Introducción a JDBC

Hoy en día, la mayoría de aplicaciones informáticas necesitan almacenar y gestionar gran cantidad de datos.

Esos datos, se suelen guardar en bases de datos relacionales, ya que éstas son las más extendidas actualmente.

Las bases de datos relacionales permiten organizar los datos en tablas y esas tablas y datos se relacionan mediante campos clave. Además se trabaja con el lenguaje estándar conocido como SQL, para poder realizar las consultas que deseemos a la base de datos. Una base de datos relacional se puede definir de una manera simple como aquella que presenta la información en tablas con filas y columnas.

Una tabla es una serie de filas y columnas, en la que cada fila es un registro y cada columna es un campo. Un campo representa un dato de los elementos almacenados en la tabla (NSS, nombre, etc.) Cada registro representa un elemento de la tabla (el equipo Real Madrid, el equipo Real Murcia, etc.) No se permite que pueda aparecer dos o más veces el mismo registro, por lo que uno o más campos de la tabla forman lo que se conoce como clave primaria.

El sistema gestor de bases de datos, en inglés conocido como: Database Management System (DBMS), gestiona el modo en que los datos se almacenan, mantienen y recuperan. En el caso de una base de datos relacional, el sistema gestor de base de datos se denomina: Relational Database Management System (RDBMS). Tradicionalmente, la programación de bases de datos ha sido como una Torre de Babel: gran cantidad de productos de bases de datos en el mercado, y cada uno “hablando” en su lenguaje privado con las aplicaciones.

Java, mediante JDBC (Java Database Connectivity), permite simplificar el acceso a base de datos, proporcionando un lenguaje mediante el cual las aplicaciones pueden comunicarse con motores de bases de datos. Sun desarrolló este API para el acceso a bases de datos, con tres objetivos principales en mente:

  • Ser un API con soporte de SQL: poder construir sentencias SQL e insertarlas dentro de llamadas al API de Java.
  • Aprovechar la experiencia de los APIs de bases de datos existentes.
  • Ser sencillo.

JDBC

De JDBC podemos decir que:

  • Consta de un conjunto de clases e interfaces escritas en Java.
  • Proporciona un API estándar para desarrollar aplicaciones de bases de datos con un API Java pura.

Con JDBC, no hay que escribir un programa para acceder a una base de datos Access, otro programa distinto para acceder a una base de datos Oracle, etc., sino que podemos escribir un único programa con el API JDBC y el programa se encargará de enviar las sentencias SQL a la base de datos apropiada. Además, y como ya sabemos, una aplicación en Java puede ejecutarse en plataformas distintas.

En el desarrollo de JDBC, y debido a la confusión que hubo por la proliferación de API’s propietarios de acceso a datos, Sun buscó los aspectos de éxito de un API de este tipo, ODBC (Open Database Connectivity). ODBC se desarrolló con la idea de tener un estándar para el acceso a bases de datos en entorno Windows. Aunque la industria ha aceptado ODBC como medio principal para acceso a bases de datos en Windows, ODBC no se introduce bien en el mundo Java, debido a la complejidad que presenta ODBC, y que entre otras cosas ha impedido su transición fuera del entorno Windows. El nivel de abstracción al que trabaja JDBC es alto en comparación con ODBC, la intención de Sun fue que supusiera la base de partida para crear librerías de más alto nivel. JDBC intenta ser tan simple como sea posible, pero proporcionando a los desarrolladores la máxima flexibilidad.

jdbc

Establecimiento de conexiones

Conectores o Drivers

El API JDBC viene distribuido en dos paquetes:

  • java.sql, dentro de J2SE
  • javax.sql, extensión dentro de J2EE

Un conector o driver es un conjunto de clases encargadas de implementar las interfaces del API y acceder a la base de datos. Para poder conectarse a una base de datos y lanzar consultas, una aplicación necesita tener un conector adecuado. Un conector suele ser un fichero .jar que contiene una implementación de todas las interfaces del API JDBC. Cuando se construye una aplicación de base de datos, JDBC oculta los detalles específicos de cada base de datos, de modo que le programador se ocupe sólo de su aplicación. El conector lo proporciona el fabricante de la base de datos o bien un tercero. El código de nuestra aplicación no depende del driver, puesto que trabajamos contra los paquetesjava.sql y javax.sql. JDBC ofrece las clases e interfaces para:

  • Establecer una conexión a una base de datos.
  • Ejecutar una consulta.
  • Procesar los resultados.

Los cuatro pasos a seguir

  • Importar el paquete jar de JDBC que se necesite, en el caso de MariaDB se descarga aquí: website

    Este es el que he probado

  • Agregarlo a tu proyecto de Eclipse: enlace

  • Importar lo necesario en código fuente o registrar el driver de conexión con .forname():

try {
   Class.forName("oracle.jdbc.driver.OracleDriver");
}
catch(ClassNotFoundException ex) {
   System.out.println("Error: unable to load driver class!");
   System.exit(1);
}
  • Crear el objeto de conexión –> se va más adelante.

Componer la URL de conexión

Antes de obtener el objeto de conexión necesitamos la información necesaria para la conexión. Para la url de la conexión a la base de datos necesitamos crear un String con las siguientes partes:

  • Protocolo: jdbc
  • Tipo de sistema gestor de base de datos: oracle, mysql, mariadb, db2 etc…
  • Servidor o hostname: localhost si estáis en local.
  • Puerto.
  • Base de datos a conectarse.

Ejemplo:

  // url para un sgbd de mysql en local utilizando el puerto 3306 y conectandose a la bd hola
  String sURL = "jdbc:mysql://localhost:3306/hola";

Hay más opciones podéis verlas en este ejemplo

Obtener objeto de conexión -> conectarse

Una vez que tenemos lista la URL a partir del método estático getConnection() de la clase DriveManager podemos obtener el objeto que utilizaremos para conectarnos a la base de datos. Tiene tres firmas el método:

  • getConnection(String url): dentro de la url viene incluido el usuario y contraseña.
  • getConnection(String url, Properties prop): a partir de un objeto Properties podemos agregar los datos necesarios.
  • getConnection(String url, String user, String password): la versión que vamos a utilizar.

Datos para usar el servidor del cesacle

  • URL: jdbc:mariadb://dbalumnos.sanclemente.local:3319
  • User: alumno
  • Password: abc123..

Indicaciones para crear tu base de datos: nombre_usuario_nombre_base_de_datos

Cerrar conexiones

Si no usamos try-with-resources la clase Connection tiene un método para cerrar close()

Comprobación

A veces es necesario comprobar si el sistema gestor de base datos está online y si tenemos conexión:

if (connection != null && !connection.isClosed()) {
        // run sql statements
    } else {
        // handle closed connection path
  }

Otra forma es validar la conexión:

if (connection.isValid(5)) {
        // run sql statements
    }
    else {
        // handle invalid connection
    }

En el anterior ejemplo espera 5 segundos de respuesta.

O realizar una consulta de prueba:

public static boolean isConnectionValid(Connection connection)
{
    try {
        if (connection != null && !connection.isClosed()) {
            // Running a simple validation query
            connection.prepareStatement("SELECT 1");
            return true;
        }
    }
    catch (SQLException e) {
        // log some useful data here
    }
    return false;
}

Ejemplo clásico

Connection con = null;
  String sURL = "jdbc:mariadb://dbalumnos.sanclemente.local:3319/test";
  try {

      con = (Connection) DriverManager.getConnection(sURL,"xxxx","xxxx");
      System.out.println ("¡Conexión exitosa!");

        } catch (Exception e) { 
     System.out.println("Error en la conexión:" + e.toString() );
  } finally {
  try {
      // Cerramos posibles conexiones abiertas
      if (con!=null) con.close();    
  } catch (Exception e) {
      System.out.println("Error cerrando conexiones: "
        + e.toString());
  } 
}

Ejemplo con try-with-resources y url con múltiples opciones

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class SQLDatabaseConnection {
    // Connect to your database.
    // Replace server name, username, and password with your credentials
    public static void main(String[] args) {
        String connectionUrl =
                "jdbc:sqlserver://yourserver.database.windows.net:1433;"
                        + "database=AdventureWorks;"
                        + "user=yourusername@yourserver;"
                        + "password=yourpassword;"
                        + "encrypt=true;"
                        + "trustServerCertificate=false;"
                        + "loginTimeout=30;";

        try (Connection connection = DriverManager.getConnection(connectionUrl);) {
            // Code here.
        }
        // Handle any errors that may have occurred.
        catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Conexión a base de datos embebidas (SQLite, H2 etc…)

Introducción

Las bases de datos embebidas, también conocidas como bases de datos integradas o bases de datos en memoria, son sistemas de gestión de datos diseñados para funcionar directamente dentro de una aplicación, sin necesidad de una instalación o configuración separada. Estas bases de datos se integran directamente en la aplicación que las utiliza y son especialmente útiles en situaciones donde se necesita un almacenamiento ligero, eficiente y de fácil acceso. Aquí hay algunas características y conceptos básicos de las bases de datos embebidas:

  • Sin servidor independiente: A diferencia de las bases de datos tradicionales que se ejecutan como servicios independientes y requieren instalación y configuración por separado, las bases de datos embebidas se incorporan directamente en la aplicación y se ejecutan dentro del mismo proceso.

  • Ligereza: Estas bases de datos están diseñadas para ser ligeras y tener una huella de memoria mínima. Esto las hace ideales para aplicaciones donde los recursos son limitados, como aplicaciones móviles o sistemas embebidos.

  • Acceso rápido: Al integrarse directamente en la aplicación, las bases de datos embebidas ofrecen un acceso muy rápido a los datos, ya que no hay necesidad de realizar operaciones de red para interactuar con un servidor externo.

  • Uso de memoria compartida: En muchos casos, las bases de datos embebidas utilizan la misma memoria que la aplicación que las utiliza, lo que puede aumentar la eficiencia y el rendimiento al eliminar la necesidad de comunicación entre procesos.

  • Persistencia de datos: A pesar de estar integradas en la aplicación, las bases de datos embebidas suelen ofrecer mecanismos para persistir los datos en el disco, lo que significa que los datos pueden sobrevivir a los reinicios de la aplicación o del sistema.

  • Soporte para múltiples modelos de datos: Aunque algunas bases de datos embebidas están diseñadas para un modelo de datos específico, como clave-valor o documentos, otras ofrecen soporte para modelos más complejos, como relaciones entre tablas.

Ejemplos comunes: Algunos ejemplos de bases de datos embebidas populares incluyen SQLite, Berkeley DB y Realm.

Conexión con SQLite

Cómo con las base de datos más clásicas necesitamos un objeto Connection con JDBC. La URL es similar, ejemplo:

Connection conex = DriverManager.getConnection("jdbc:sqlite:/Users/juan/Documents/SQLite");

Para usar en tu proyecto Maven estas dependencias en el pom.xml:

<dependency>
    <groupId>org.xerial</groupId>
    <artifactId>sqlite-jdbc</artifactId>
    <version>3.45.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.12</version>
</dependency>
</dependencies>

Para crear una base de dato para SQLite puedes usar el DBeaver, es muy sencillo.

Conexión con H2

La URL necesaria con esta base de datos es:

Connection conex = DriverManager.getConnection("jdbc:h2:/Users/juan/Documents/h2/Ult");

Dependencia con Maven:

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>2.2.224</version>
</dependency>

Al crearla con DBeaver debes tener cuidado con la versión de los drivers que usa el gestor. Usando los mismos en tu proyecto Maven.

Extra: comparativa de tres sistemas de base de datos embebidas: enlace

Recuperación de información (Consultas)

Partiendo de una conexión con la base de datos (Clase Connection), ya podemos realizar consultas contra ella

Antes de nada importa esta pequeña base de datos para que practiquéis: bd.sql bd_paraDbeaver.sql

Statement (Consultas sin parámetros)

La clase Statement se obtiene del método createStament() de Connection:

try (Statement stmt = con.createStatement()) {
    // use stmt here
}

A partir de Statement tenemos la posibilidad de ejecutar consultas simples con el método executeQuery(), de esta forma:

String selectSql = "SELECT * FROM employees"; 
try (ResultSet resultSet = stmt.executeQuery(selectSql)) {
    // use resultSet here
}

Como observáis se obtiene un objeto Resulset que veremos más adelante.

PreparedStament (Consultas parametrizadas)

Esta clase nos sirve para realizar consultas más complejas. Que reciban datos o parámetros desde “fuera”, es decir desde nuestro programa. Para obtener dicho objecto se necesita el método prepareStament() de Connection, y se pasa la consulta sql parametrizada:

String consulta = "select * from Persona where nombre = ? ";
PreparedStatement sentencia= conexion.prepareStatement(consulta); 

La consulta sql parametrizada tiene “?” (1 o más), que representa los parámetros de entrada desde nuestro programa a la consulta. Teniendo listo el PreparedStament necesitamos asignar el o los parámetros antes de la ejecución, este objeto tiene los sets necesarios para asignar:

sentencia.setString(1,"Juan");
sentencia.executeQuery(); //Nos devolverá un Resultset

Como puedes advinina el primer parámetro debe ser un número que indica a que “?” (o parámetro) de la sentencia sql quiere asignar. Ojo –> siempre empieza en 1 El segundo parámetro de los sets es el propio valor o variable que quieres pasar a la consulta. Debes consultar todos los set que te ofrece el jdbc en correlación a los tipos de datos del sistema gestor de base de datos.

Extra: hay otra forma para poder agregar parámetros a tu consulta SQL:

Statement sentencia = conexion.createStatement(); 
String nombre="pepe"; 
String consulta = "select * from Persona where nombre='"+nombre+"'";
ResultSet rs=sentencia.executeQuery(consulta);

Pero resulta muy peligrosa por el problema de seguridad SQL Injection.

ResultSet

Esta interfaz nos permitirá obtener toda la información que devuelve nuestras instrucciones sql. Como si fuera un cursor o iterable. El método más importante es el next(), que recorre el resultado de la consulta linea a linea hasta que devuelve false: Base de datos a importar

// Pojo de empleado
public class Employee {
    private int id;
    private String name;
    private String position;
    private double salary;
 
    // standard constructor, getters, setters
}

String selectSql = "SELECT emp_id,name, posicion, salary  FROM employees"; 
try (ResultSet resultSet = stmt.executeQuery(selectSql)) {
    List<Employee> employees = new ArrayList<>(); 
    while (resultSet.next()) { 
        Employee emp = new Employee(); 
        emp.setId(resultSet.getInt("emp_id")); 
        emp.setName(resultSet.getString("name")); 
        emp.setPosition(resultSet.getString("posicion")); 
        emp.setSalary(resultSet.getDouble(4)); 
        employees.add(emp); 
    }
}

Y tiene los correspondientes gets para ir obteniendo campo a campo la información, siempre en consonacia con los parámetros del “Select” de la consulta slq a ejecutar. Los gets tienen dos formas:

  • resultSet.getInt(int indice_Columna): recibe un número que corresponde al orden en la clausula Select.
  • resultSet.getInt(String etiqueta_Columna): recibe el nombre de la columna en la clausula Select.

Base de datos de prueba: empresa.sql

Conversión JDBC a Java

imagen

Ejercicio 1

Partiendo del ejemplo anterior, nos piden una pequeña aplicación que nos permita conocer el salario y su posición a partir del id del empleado (se recoge por teclado). Resultado por pantalla.

Posteriormente el total del gasto en salario de la empresa.

Y por último cuantos empleados tienen más que un salario (se pide por teclado) y una posición (también se pide por teclado).

Altas, bajas y modificaciones de información

Lo primero obtenemos un objeto Statement a partir de Connection:

try (Statement stmt = con.createStatement()) {
    // use stmt here
}

El Statement tiene tres métodos para ejecutar:

  • executeQuery(): para SELECT instrucciones. Visto en el anterior ejercicio.
  • executeUpdate(): para actualizar información o estructura de base de datos.
  • execute(): se puede usar para ambos casos. Más utilizada para crear o eliminar estructura de base de datos

Actualización de información

Respecto a las consultas de actualización, con un objeto de Connection tenemos el método executeUpdate(), retornan el número de registros insertados, registros actualizados o eliminados, dependiendo del tipo de consulta que se trate. Supongamos que tenemos varios registros en la tabla Cliente, de una base de datos. Si quisiéramos actualizar el teléfono del tercer registro, que tiene idCLIENTE=3 y ponerle como nuevo teléfono el 968610009 tendríamos que hacer:

String connectionUrl = "jdbc:mysql://localhost/notarbd?" + "user=root&password=admin";
// Obtener la conexión
Connection con = DriverManager.getConnection(connectionUrl);
// Preparamos la consulta y la ejecutamos
Statement s = con.createStatement();
int registros_afectados = s.executeUpdate("UPDATE CLIENTE SET teléfono='968610009' WHERE idCLIENTE=3");
// Cerramos la conexión a la base de datos.
con.close();

Creación


String tableSql = "CREATE TABLE IF NOT EXISTS employees" 
  + "(emp_id int PRIMARY KEY AUTO_INCREMENT, name varchar(30),"
  + "position varchar(30), salary double)";
stmt.execute(tableSql);

Inserciones de información

Usamos excuteUpdate():

Statement s = con.createStatement();
s.executeUpdate( "INSERT INTO CLIENTE" +
" (idCLIENTE, NIF, NOMBRE, APELLIDOS, DIRECCIÓN, CPOSTAL, TELÉFONO, CORREOELEC)" + " VALUES (4, '66778998T', 'Alfredo', 'Gates Gates', 'C/
Pirata 23','20400', '891222112', 'prueba@eresmas.es' )") ;

Borrado de información

Lo mismo:

Statement s = con.createStatement();
numReg = s.executeUpdate( "DELETE FROM CLIENTE WHERE NIF= '66778998T' " );
// Informamos del número de registros borrados 
System.out.println ("\nSe borró " + numReg + " registro\n") ;

Nota: Si usamos execute en vez de executeUpdate, tenemos un método que nos devuelve el número de filas afectadas tras la ejecución de la sentencia sql: stmt.getUpdateCount()

PreparedStatement

En este tipo de sentencias sql también necesitamos parámetros de entrada y se trabaja de la misma forma que lo explicado en el anterior recurso

Autoguardado

Por defecto en MariaDB cualquier operación que implica alta, baja o modificación de datos tiene el autoguardado activo (autocommit true). Es decir se ejecuta la sentencia SQL y se guardan los cambios. Pero a veces nos interesa que no se así. Por ejemplo operaciones sobre la base de datos de todas o ninguna, se llaman transaciones. Si tenemos dudas de que valor está la variable autocommit podemos asignarla a verdadero:

connection.setAutoCommit(true);

Si desabilitamos el autoguardado luego podemos ejecutar el método commit para forzar el guardado de operaciones:

connection.setAutoCommit(false);
// ejecutamos las sentencias importantes ....
connection.commit();

Ejercicio 1

Partiendo del Pojo:

public class Person {

    private Integer id;
    private String name;
    private String lastName;
    private Integer age;

    // standard constructor, getters, and setters
}

Desarrolla estas funcionalidades a partir de la interface:

public interface IPerson {
    public  Connection openConnection() throws SQLException;
    public  int insertPerson(Connection connection, Person person) throws SQLException;
    public  void updatePersonAgeById(Connection connection, int id, int newAge) throws SQLException;
    public  List selectAllPeople(Connection connection) throws SQLException;
    public int deletePerson(Connection connection, int id) throws SQLException;
    public Person maxAgePerson(Connection connection) throws SQLException;
}

Pool de Conexiones

Como ya sabéis Java Database Connectivity (JDBC) es una API que proporciona una interfaz común para acceder a bases de datos relacionales desde Java. JDBC utiliza un conjunto de clases e interfaces para conectarse a una base de datos, enviar consultas y recuperar resultados. Pero hasta ahora usabamos/ocupabámos una conexión del SGBD. Las conexiones de un SGBD son limitadas, por tanto hay formas de conectarse a la base de datos de manera más eficiente.

Pool de Conexiones

El uso de un pool de conexiones JDBC puede mejorar el rendimiento de una aplicación Java que requiere conectarse a una base de datos. En lugar de abrir y cerrar una conexión cada vez que se necesita acceder a la base de datos, el pool de conexiones permite reutilizar las conexiones existentes.

Lo primero que es necesario es agregar tres jars de Apache a tu proyecto (de la misma manera que el jar de MariaDB) para poder usar esta forma nueva de conectarse:

Para crear un pool básico de conexiones se crea un objeto de la clase BasicDataSource, a partir de ese objeto se obteniene las conexiones necesitadas. Y se le asigna todos los parámetros necesarios:

public class EjemploPoolConexionJDBC {

    public static void main(String[] args) throws SQLException {
        // Configurar el pool de conexiones
        BasicDataSource dataSource = new BasicDataSource();
        //dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/hola");
        dataSource.setUsername("xxx");
        dataSource.setPassword("xxxx");
        dataSource.setInitialSize(5);
        dataSource.setMaxTotal(10);
        
        // Obtener una conexión del pool
        Connection conn = dataSource.getConnection();
        
        // Usar la conexión para enviar consultas
        // ...
        
        // Devolver la conexión al pool
        conn.close();
    }
}

Hay más opciones de pool con diferentes clases DataSource, y también podemos configurarlo con determinados sets del DataSource tal como véis en el ejemplo. Extra:

  • setMaxActive() : Número máximo de conexiones que se pueden abrir simultáneamente.
  • setMinIdle() : Número mínimo de conexiones inactivas que queremos que haya. Si el número de conexiones baja de este número, se abriran más.
  • setMaxIdle() : Número máximo de conexiones inactivas que queremos que haya. Si hay más, se irán cerrando.
  • setInitialSize() : Número de conexiones que se quiere que se abran en cuanto el pool comienza a trabajar (se llama por primera vez a getConnection(), setLogwriter(), setLoginTimeout(), getLoginTimeout() o getLogWriter(). Debe llamarse a setInitialSize() antes de llamar a cualquiera de estos métodos y después no puede cambiarse el valor.

Rowset

Vamos a ver una interfaz de manipulación de datos que nos ofrece más posibilidades que lo hemos estado usando hasta ahora -> Resulset Dicha interfaz es RowSet, que se encuentra en el paquete javax.sql Descargar. Esta interfaz tiene muchos tipos: JdbcRowSet, CachedRowSet, WebRowSet, FilteredRowSet o JoinRowSet. Las que vamos a usar es JdbcRowSet.

Imagen Resulset

Si os fijáis RowSet hereda toda la funcionalidades de ResultSet, lo que nos aporta RowSet es mayor flexibilidad y posibilidades.

Ventajas y diferencias entres RowSet y ResultSet

RowSet ResultSet
paquete javax.sql paquete java.sql
puede estas desconectado de la BD siempre debe mantener la conexión con la BD
flexibilidad en la navegación siempre hacia delante (next())
puede ser serializable y transmitirlo no puede ser serializable
es un Java.Bean no es un Java.Bean se obtiene del método executeQuey()
se puede utilizar para actualizaciones sólo se utiliza de consulta

Creación y consulta

Una vez importado el jar de javax.sql podemos crear un objeto tipo JdbcRowSet, con un factory y asignarle lo necesario para conectarse a la base de datos:

JdbcRowSet rowSet;
try {
    //obtenemos el rowset de forma estatica
    rowSet = RowSetProvider.newFactory().createJdbcRowSet();
    rowSet.setUrl("jdbc:mysql://localhost:3306/Empresa");

    rowSet.setUsername("root");
    rowSet.setPassword("xxxxx");

    // Asignamos consulta y ejecutamos 
    rowSet.setCommand("select * from employees");
    rowSet.execute();

Los métodos setCommand() y execute() asigna consulta y la ejecuta.

Recorrer resultado y navegación

Como ya sabéis podemos usar el método next() para ir recorriendo fila a fila:

while (rowSet.next()) {
                System.out.println("Id: "  + rowSet.getInt(1));
                System.out.println("Name: " + rowSet.getString(2));
                System.out.println("Posicion: " + rowSet.getString(3));
            }

Método más interesantes de navegación:

  • previous(): hacia atrás.
  • absolute(int i): colocarse en una una posición concreta (empieza en 1).
  • first(): primer registro.
  • last(): último registro.

Actualizar registros

No sólo nos sirve para recorrer y navegar por los registros del resultado de una consulta, también podemos usarlo para actualizar información:

Donde se encuentra el cursor podemos actualizar un campo:

rset.updateString("PRODUCT", "HDMI CABLE");
rowSet.updateRow();

Con este método updateXXX() debes elegir el tipo de dato a actualizar. El primer argumento nombre de columna de la consulta o número (posición). Y ejecutar updateRow().

Realizar inserciones

RowSet también nos permite realizar inserciones usando el método moveToInsertRow() para colocarse en posición de inserción y usando los updates correspondientes. Es necesario ejecutar insertRow() para confirmar el nuevo registro:

rowSet.moveToInsertRow();
rowSet.updateInt("emp_id", 500);
rowSet.updateString("name", "Paco");
rowSet.updateString("posicion", "secretario");
rowSet.updateFloat("salary",10000);
rowSet.insertRow();

Borrado de filas

Usamos el método deleteRow() una vez te coloques en el registro que quieres eliminar.

Consultas parametrizadas

Exactamente igual que con ResultSet, necesitamos las “?” para indicar el parámetro de entrada, y existen los “set`s” correspondientes para asignar el valor al parámetro.

Ejercicio 1

Partiendo de la base de datos Empresa y la tabla Person realiza un programa que nos permita estas operaciones (usar RowSet en la mayoría de casos):

  • Mostrar todos los registros.
  • Mostrar el número de personas de la empresa.
  • Mostrar un registro por la posición. Una vez colocado que nos permita mostrar el anterior registro o el siguiente.
  • Actualizar una edad de una persona, indicando la posición.
  • Eliminar último registro.
  • Insertar una nueva persona en la empresa.

Extra: Por si queréis más

  • Típicos patrones de diseño con datos: DAO y Repository.
  • Frameworks de persistencia: Hibernate y JPA.
  • Uso de NoSQL con Java.
  • Transacciones y uso de objetos grandes (imágenes, ficheros etc…) Blob/Clob.

Dotenv

Una vez que vamos avanzando y creamos proyectos más complejos, surge la necesidad de gestionar variables de entorno de nuestro programa. Estas variables de entorno pueden contener desde parámetros de configuración, datos de conexión (server, base de datos, ficheros, repositorios) o de usuario, información del entorno de ejecución, parámetros etc…

Para gestionar y tener bien centralizado todos esos parámetros surge la librería dotenv.

Introdución a DotEnv

Dotenv es una herramienta que permite cargar variables de entorno desde un archivo de configuración en proyectos de desarrollo de software. Es comúnmente utilizado en entornos de desarrollo de aplicaciones web para cargar configuraciones sensibles, como credenciales de bases de datos o claves de API, sin necesidad de exponerlas directamente en el código fuente.

Dotenv se encarga de cargar estas variables de entorno cuando la aplicación se inicia en un entorno de desarrollo, haciendo que la configuración sea fácilmente ajustable y portátil entre diferentes sistemas y configuraciones. Esto promueve una práctica de desarrollo más segura y escalable al separar la configuración del código fuente y permitir una gestión más centralizada de las variables de entorno.

Instalación de DotEnv

A partir de una dependencia Maven en lo más cómodo:

<dependency>
    <groupId>io.github.cdimascio</groupId>
    <artifactId>dotenv-java</artifactId>
    <version>3.0.0</version>
</dependency>

Aquí os dejo también el jar que podéis agregar a un proyecto: jar

Uso de DotEnv

Lo primero es crear un fichero dentro de tu proyecto, con el nombre y extensión: “.env”

Este fichero contendrá por cada línea la clave de la variable y su valor, separado por un “=”. Ejemplo:

MY_ENV=MY_VALUE
...

Cuando se necesite usar dichas variables se debe cargar el .env desde el código fuente. La función load() de la clase Dotenv se encargará de ello, y la función get(String) para obtener el valor a partir de la clave. Ejemplo:

Dotenv dotenv = Dotenv.load();
System.out.println(dotenv.get("Mysql"));

También podemos obtener variables de sistema, a partir de su nombre. Y tienen preferencia con respecto a las del fichero .env.

Uso más avanzado

Ejemplo más avanzado, donde hay directrices de donde buscar el fichero, o si no está que no de error etc…

Dotenv dotenv = Dotenv.configure()
        .directory("./some/path")
        .ignoreIfMalformed()
        .ignoreIfMissing()
        .load();

Enlace con las opciones: enlace Portal de Github: enlace

Ejercicio

Cambia algun proyecto de base de datos, usando dotenv para todos los parámetros de conexión utilizados.

Conclusión

La idea es tener a mano todos esos parámetro que hay que cambiar cuando se pasa de un proyecto de desarrollo a producción, y tener mucho control sobre ese fichero. Son datos que no deberían estar esparcidos por el código. Una recomendación es incluir en el fichero .gitignore el fichero .env, para que no suba a ningún repositorio publico.