Ejemplo: DAO Manager

Última parte sobre DAOs en la que creo un manager para poder recuperar todos los DAO mediante el uso del patrón Singleton, de modo que no tengamos más de un DAO suelto por el sistema.

Hay una versión nueva de este curso. Haz clic aquí para revisar JDBC, la versión actualizada de este curso.

En el desarrollo con bases de datos, uno de los aspectos que a menudo genera confusión es cómo manejar los IDs que se generan automáticamente al insertar registros. En nuestra experiencia, es un error común tener que proporcionar manualmente el ID al insertar un nuevo registro, cuando en realidad la base de datos puede encargarse de asignarlo automáticamente gracias a campos con autoincremento. Por ejemplo, en MySQL, si definimos un campo ID con esta característica, al insertar un nuevo alumno solo necesitamos enviar su nombre, apellidos y fecha de nacimiento, y el sistema asignará el siguiente número disponible sin intervención nuestra.

Para aprovechar esta funcionalidad, es importante ajustar el código que realiza la inserción. En lugar de incluir el ID en la sentencia SQL, debemos omitirlo y solo establecer los valores de los demás campos. Luego, utilizando el método getGeneratedKeys() del objeto PreparedStatement, podemos recuperar el ID que la base de datos ha generado para ese nuevo registro. Este método devuelve un ResultSet con las claves generadas, y normalmente el ID estará en la primera columna. Así, podemos extraer ese valor y asignarlo al objeto correspondiente en nuestro código, manteniendo la coherencia entre la base de datos y nuestra aplicación.

Por ejemplo, el código para insertar un alumno y obtener su ID generado podría ser algo así:

PreparedStatement stat = connection.prepareStatement(
    "INSERT INTO alumno (nombre, apellidos, fechaNacimiento) VALUES (?, ?, ?)",
    Statement.RETURN_GENERATED_KEYS
);
stat.setString(1, alumno.getNombre());
stat.setString(2, alumno.getApellidos());
stat.setDate(3, new java.sql.Date(alumno.getFechaNacimiento().getTime()));
stat.executeUpdate();

ResultSet generatedKeys = stat.getGeneratedKeys();
if (generatedKeys.next()) {
    long id = generatedKeys.getLong(1);
    alumno.setId(id);
}
generatedKeys.close();
stat.close();

Con esta mejora, evitamos bugs relacionados con la gestión manual de IDs y hacemos que la inserción sea más limpia y segura.

Una vez resuelto este punto, podemos centrarnos en organizar mejor el acceso a los distintos DAOs (Data Access Objects) que manejan las diferentes entidades de nuestra aplicación, como alumnos, asignaturas, matrículas o profesores. Para ello, es muy útil crear un DAO manager, una clase que actúe como punto centralizado para obtener cada DAO específico. Esto nos permite tener un acceso ordenado y controlado a los distintos DAOs sin tener que instanciarlos dispersamente por todo el código.

Para implementar este DAO manager, lo ideal es definir primero una interfaz que declare los métodos para obtener cada DAO, por ejemplo, getAlumnoDAO(), getAsignaturaDAO(), getMatriculaDAO() y getProfesorDAO(). Luego, creamos una implementación concreta, por ejemplo, MySQLDAOManager, que se encargue de crear y devolver las instancias correspondientes.

Un aspecto clave es aplicar el patrón Singleton para cada DAO dentro del manager. Esto significa que cada DAO se crea una única vez y luego se reutiliza, evitando tener múltiples instancias innecesarias que podrían complicar la gestión de conexiones o el estado interno. En la implementación, esto se traduce en comprobar si la instancia del DAO es null y, en ese caso, crearla; si no, simplemente devolver la instancia existente.

Además, el DAO manager puede encargarse de manejar la conexión a la base de datos. Podemos diseñarlo para que reciba los parámetros necesarios para establecer la conexión, como el host, usuario, contraseña y nombre de la base de datos, y que internamente cree la conexión usando DriverManager.getConnection(). Esto centraliza la gestión de la conexión y facilita su reutilización por los distintos DAOs.

Un ejemplo simplificado de la implementación del DAO manager con patrón Singleton para los DAOs podría ser:

public class MySQLDAOManager implements DAOManager {
    private Connection connection;
    private AlumnoDAO alumnoDAO = null;
    private AsignaturaDAO asignaturaDAO = null;
    private MatriculaDAO matriculaDAO = null;
    private ProfesorDAO profesorDAO = null;

    public MySQLDAOManager(String host, String database, String user, String password) throws SQLException {
        String url = "jdbc:mysql://" + host + "/" + database;
        this.connection = DriverManager.getConnection(url, user, password);
    }

    @Override
    public AlumnoDAO getAlumnoDAO() {
        if (alumnoDAO == null) {
            alumnoDAO = new MySQLAlumnoDAO(connection);
        }
        return alumnoDAO;
    }

    @Override
    public AsignaturaDAO getAsignaturaDAO() {
        if (asignaturaDAO == null) {
            asignaturaDAO = new MySQLAsignaturaDAO(connection);
        }
        return asignaturaDAO;
    }

    @Override
    public MatriculaDAO getMatriculaDAO() {
        if (matriculaDAO == null) {
            matriculaDAO = new MySQLMatriculaDAO(connection);
        }
        return matriculaDAO;
    }

    @Override
    public ProfesorDAO getProfesorDAO() {
        if (profesorDAO == null) {
            profesorDAO = new MySQLProfesorDAO(connection);
        }
        return profesorDAO;
    }

    public void close() throws SQLException {
        if (connection != null) {
            connection.close();
        }
    }
}

Con esta estructura, podemos obtener fácilmente cualquier DAO desde una única instancia del manager, y además, si en el futuro queremos cambiar la base de datos o la implementación de los DAOs, solo tendremos que modificar esta capa sin afectar al resto de la aplicación. Por ejemplo, podríamos crear un PostgreSQLDAOManager o incluso una versión que use colecciones en memoria para pruebas.

Para probar que todo funciona correctamente, podemos crear un método main donde instanciemos el DAO manager, obtengamos el DAO de alumnos y recuperemos la lista completa de alumnos para imprimirla. Esto nos permite verificar que la conexión, la creación de DAOs y las consultas están funcionando como esperamos.

Este enfoque modular y centralizado facilita el mantenimiento y la escalabilidad de la aplicación, y nos prepara para integrar en el futuro una interfaz gráfica o nuevas funcionalidades sin complicaciones en la gestión de acceso a datos.

Lista de reproducción
  1. 1
    Presentación de JDBC
    4 minutos
  2. 2
    Instalando MySQL
    7 minutos
  3. 3
    Creando tablas
    9 minutos
  4. 4
    Agregando el driver JAR
    7 minutos
  5. 5
    Estableciendo la conexión
    8 minutos
  6. 6
    Statement y ResultSet
    9 minutos
  7. 7
    Bobby Tables y PreparedStatement
    8 minutos
  8. 8
    Transacciones y MySQL
    6 minutos
  9. 9
    Transacciones, commits y rollbacks (parte 1)
    7 minutos
  10. 10
    Transacciones, commits y rollbacks (parte 2)
    6 minutos
  11. 11
    Ejemplo: Crear modelos
    9 minutos
  12. 12
    Ejemplo: Crear los DAO (parte 1)
    10 minutos
  13. 13
    Ejemplo: Crear los DAO (parte 2)
    12 minutos
  14. 14
    Ejemplo: Crear los DAO (parte 3)
    13 minutos
  15. 15
    Ejemplo: DAO Manager
    12 minutos
  16. 16
    Ejemplo: CRUD Alumnos (parte 1)
    15 minutos
  17. 17
    Ejemplo: CRUD Alumnos (parte 2)
    14 minutos
  18. 18
    Ejemplo: CRUD Alumnos (parte 3)
    13 minutos
  19. 19
    Ejemplo: CRUD Profesores
    25 minutos
  20. 20
    Ejemplo: CRUD Asignaturas
    alrededor de 1 hora
  21. 21
    Ejemplo: Login (final adelantado)
    17 minutos
  22. 22
    Conectar a PostgreSQL en Java con JDBC
    8 minutos
  23. 23
    Conectar a PostgreSQL en Kotlin con JDBC
    7 minutos
  24. 24
    Conectar a PostgreSQL en Java con JDBC (con NetBeans)
    10 minutos