Clase AlumnoVO y AlumnoDAO - eliizquierdo/material_programacion_avanzada2026 GitHub Wiki

📦 Proyecto: mini-crud-alumno-sql (Tema 4 — Herencia + MySQL) — ⬇️ Descargar ZIP · ☁️ Abrir en Codespace

Índice

-La clase AlumnoVO

-La clase AlumnoDAO

  1. Operaciones de Lectura: listarAlumnos() y obtenerAlumnoPorCodigo()
  2. Operaciones de inserción: agregarAlumno()
  3. Actualizar Alumno
  4. Eliminar Alumno

La clase AlumnoVO

La clase AlumnoVO extiende de PersonaVO y solo necesita los atributos específicos de un alumno,
en este caso, teniendo en cuenta la BD, el teléfono.

package modelo.vo;

public class AlumnoVO extends PersonaVO {
    private String telefono;

    // Constructor vacío
    public AlumnoVO() {
        super();
    }

    // Constructor completo (código, nombre y teléfono)
    public AlumnoVO(int codigo, String nombre, String telefono) {
        super(codigo, nombre);
        this.telefono = telefono;
    }

    // Getters y Setters
    public String getTelefono() {
        return telefono;
    }

    public void setTelefono(String telefono) {
        this.telefono = telefono;
    }

    // ... toString()
}

La clase AlumnoDAO


1.Operaciones de Lectura: listarAlumnos() y obtenerAlumnoPorCodigo() 🔍

Concepto Clave: JOIN en la Sentencia SQL

Para obtener un AlumnoVO completo (con su nombre heredado de personas y su telefono de alumnos), se utiliza un INNER JOIN entre ambas tablas usando el campo codigo como vínculo.

FASE: listarAlumnos()

  • FASE 1: SQL - Se define un INNER JOIN para obtener codigo, nombre (de personas) y telefono (de alumnos).
  • FASE 5: Leer - Se recorre el ResultSet y se usa el Constructor Completo de AlumnoVO para crear los objetos.

Listar Alumnos

package modelo.dao;

import modelo.conexion.Conexion;
import modelo.vo.AlumnoVO;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class AlumnoDAO {

    // ============== LISTAR ALUMNOS ==============
    public List<AlumnoVO> listarAlumnos() {
        List<AlumnoVO> lista = new ArrayList<>();

        // FASE 1: Definir la sentencia SQL
        String sql = "SELECT p.codigo, p.nombre, a.telefono " +
                "FROM personas p " +
                "INNER JOIN alumnos a ON p.codigo = a.codigo";

        // FASES 2, 3 y 4: conexión, PreparedStatement y ejecución en try-with-resources
        try (Connection con = Conexion.obtener();
             PreparedStatement ps = con.prepareStatement(sql);
             ResultSet rs = ps.executeQuery()) {

            // FASE 5: Leer el ResultSet
            while (rs.next()) {
                AlumnoVO alumno = new AlumnoVO(
                        rs.getInt("codigo"),
                        rs.getString("nombre"),
                        rs.getString("telefono"));
                lista.add(alumno);
            }
        } catch (SQLException e) {
            System.err.println("Error al listar alumnos: " + e.getMessage());
            e.printStackTrace();
        }
        return lista;
    }
}

Obtener Alumno por Código

    // ============== OBTENER ALUMNO POR CÓDIGO ==============
    public AlumnoVO obtenerAlumnoPorCodigo(int codigo) {

        // FASE 1: Definir la sentencia SQL
        String sql = "SELECT p.codigo, p.nombre, a.telefono " +
                "FROM personas p " +
                "INNER JOIN alumnos a ON p.codigo = a.codigo " +
                "WHERE p.codigo = ?";
        AlumnoVO alumno = null;

        // FASES 2 y 3: conexión y PreparedStatement en try-with-resources
        try (Connection con = Conexion.obtener();
             PreparedStatement ps = con.prepareStatement(sql)) {

            // FASE 3: Asignar parámetros
            ps.setInt(1, codigo);

            // FASE 4 & 5: Ejecutar y leer resultado
            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    alumno = new AlumnoVO(
                            rs.getInt("codigo"),
                            rs.getString("nombre"),
                            rs.getString("telefono"));
                }
            }
        } catch (SQLException e) {
            System.err.println("Error al obtener alumno: " + e.getMessage());
            e.printStackTrace();
        }
        return alumno;
    }

2.Operaciones de inserción: agregarAlumno() 💾

Esta operación necesita insertar en las tablas personas y alumnos, usando el mismo código para ambas.

El problema: dos inserciones, un solo registro

Una primera versión, sin ningún cuidado especial, sería así:

public boolean agregarAlumno(AlumnoVO alumno) {
    boolean exito = false;
    int codigo = alumno.getCodigo();

    // FASE 1: Definir las sentencias SQL
    String sql1 = "INSERT INTO personas(codigo, nombre) VALUES (?, ?)";
    String sql2 = "INSERT INTO alumnos(codigo, telefono) VALUES (?, ?)";

    try (Connection con = Conexion.obtener();
         PreparedStatement ps1 = con.prepareStatement(sql1);
         PreparedStatement ps2 = con.prepareStatement(sql2)) {

        ps1.setInt(1, codigo);
        ps1.setString(2, alumno.getNombre());
        int filasPersona = ps1.executeUpdate();

        ps2.setInt(1, codigo);
        ps2.setString(2, alumno.getTelefono());
        int filasAlumno = ps2.executeUpdate();

        exito = filasPersona > 0 && filasAlumno > 0;
    } catch (SQLException e) {
        System.err.println("Error al agregar alumno: " + e.getMessage());
        e.printStackTrace();
    }
    return exito;
}

¿Cuál es el problema? Si la inserción en personas tiene éxito pero la de alumnos falla (por ejemplo, se corta la conexión justo ahí), quedamos con una fila en personas que nunca llegó a ser un alumno — datos inconsistentes.

La solución: transacción con setAutoCommit

Para evitar ese escenario, agrupamos ambas inserciones en una sola transacción: o se aplican las dos, o no se aplica ninguna.

Componentes clave de una transacción:

Método Propósito
con.setAutoCommit(false) Desactiva el guardado automático. Los cambios quedan pendientes hasta llamar a commit().
con.commit() Confirma todos los cambios pendientes. Los hace permanentes en la base de datos.
con.rollback() Revierte todos los cambios pendientes. Deshace las operaciones como si nunca ocurrieran.

IMPORTANTE: Para profundizar en el concepto de transacciones, revisar el Anexo de Transacciones (incluye, al final, cómo se haría esto mismo con código autoincremental).

Código final: agregarAlumno() con transacción

// ============== AGREGAR ALUMNO (CON TRANSACCIÓN) ==============
/**
 * Agrega un Alumno en dos tablas utilizando una transacción para garantizar
 * que ambas inserciones se completen o ninguna lo haga (Atomicidad).
 */
public boolean agregarAlumno(AlumnoVO alumno) {
    boolean exito = false;
    int codigo = alumno.getCodigo();

    // FASE 1: Definir las sentencias SQL
    String sql1 = "INSERT INTO personas(codigo, nombre) VALUES (?, ?)";
    String sql2 = "INSERT INTO alumnos(codigo, telefono) VALUES (?, ?)";

    // FASES 2 y 3: conexión y PreparedStatement en try-with-resources
    try (Connection con = Conexion.obtener();
         PreparedStatement ps1 = con.prepareStatement(sql1);
         PreparedStatement ps2 = con.prepareStatement(sql2)) {

        // 1. INICIO DE LA TRANSACCIÓN: Desactivar el guardado automático
        con.setAutoCommit(false);

        try {
            // FASE 3: Asignar parámetros  |  FASE 4: Ejecutar
            ps1.setInt(1, codigo);
            ps1.setString(2, alumno.getNombre());
            ps1.executeUpdate();                 // INSERT en personas

            ps2.setInt(1, codigo);
            ps2.setString(2, alumno.getTelefono());
            ps2.executeUpdate();                 // INSERT en alumnos

            // 2. CONFIRMACIÓN: ambas inserciones fueron exitosas
            con.commit();
            exito = true;
            System.out.println("Alumno agregado: " + alumno);
        } catch (SQLException e) {
            // 3. REVERSIÓN: cualquier fallo (por ejemplo, código duplicado) deshace todo
            con.rollback();
            System.out.println("Error: no se pudo agregar el alumno. Transacción revertida.");
            throw e;                             // lo informa el catch de afuera
        }
    } catch (SQLException e) {
        System.err.println("Error al agregar alumno: " + e.getMessage());
        e.printStackTrace();
    }
    return exito;
}

3.Actualizar Alumno 🔄

La actualización también modifica datos en las dos tablas, así que aplicamos la misma solución: transacción.

Explicación: Actualización con transacción

FASE Descripción y Notas Clave
Transacción Se usa con.setAutoCommit(false) para garantizar que ambos UPDATE se ejecuten correctamente o ninguno.
FASE 1: SQL Se definen dos UPDATE: sql1 para actualizar el nombre en personas, y sql2 para actualizar el teléfono en alumnos. Ambos usan el codigo como condición (WHERE).
FASES 2, 3 y 4 Se abre la conexión y se preparan y ejecutan ambos PreparedStatement (ps1 y ps2) dentro de un try interno.
Confirmación Si filasPersona > 0 y filasAlumno > 0 (lo que indica que se encontró el registro), se llama a con.commit().
Error/Reversión Si no se encuentra la persona (0 filas) o si ocurre una SQLException, se llama a con.rollback(). El try interno es necesario porque con solo existe dentro del try-with-resources.
Finalización Los recursos se cierran automáticamente (try-with-resources).

Código: Actualizar Alumno con transacción

// ============== ACTUALIZAR ALUMNO (CON TRANSACCIÓN) ==============
public boolean actualizarAlumno(AlumnoVO alumno) {
    boolean exito = false;

    // FASE 1: Definir las sentencias SQL
    String sql1 = "UPDATE personas SET nombre = ? WHERE codigo = ?";
    String sql2 = "UPDATE alumnos SET telefono = ? WHERE codigo = ?";

    // FASES 2 y 3: conexión y PreparedStatement en try-with-resources
    try (Connection con = Conexion.obtener();
         PreparedStatement ps1 = con.prepareStatement(sql1);
         PreparedStatement ps2 = con.prepareStatement(sql2)) {

        con.setAutoCommit(false);            // INICIO DE LA TRANSACCIÓN

        try {
            // FASE 3: Asignar parámetros
            ps1.setString(1, alumno.getNombre());
            ps1.setInt(2, alumno.getCodigo());

            ps2.setString(1, alumno.getTelefono());
            ps2.setInt(2, alumno.getCodigo());

            // FASE 4: Ejecutar
            int filasPersona = ps1.executeUpdate();
            int filasAlumno = ps2.executeUpdate();

            if (filasPersona > 0 && filasAlumno > 0) {
                con.commit();                // ambos UPDATE afectaron filas → confirmar
                exito = true;
                System.out.println("Alumno actualizado: " + alumno);
            } else {
                con.rollback();              // el código no existe → revertir
                System.out.println("No se encontró alumno con código " + alumno.getCodigo());
            }
        } catch (SQLException e) {
            con.rollback();                  // cualquier error → revertir todo
            throw e;                         // lo informa el catch de afuera
        }
    } catch (SQLException e) {
        System.err.println("Error al actualizar alumno: " + e.getMessage());
        e.printStackTrace();
    }
    return exito;
}

4.Eliminar Alumno 🗑️

Esta operación solo necesita eliminar de la tabla personas.

Concepto Clave: ON DELETE CASCADE

Gracias a la definición de la tabla alumnos en SQL:

FOREIGN KEY (codigo) REFERENCES personas (codigo) ON DELETE CASCADE

Al ejecutar:

DELETE FROM personas WHERE codigo = ?;

El motor de la base de datos elimina automáticamente la fila asociada en la tabla alumnos (hija). Por lo tanto, no es necesario un DELETE explícito ni una transacción adicional.

Código: Eliminar Alumno

// ============== ELIMINAR ALUMNO ==============
public boolean eliminarAlumno(int codigo) {

    boolean exito = false;

    // FASE 1: Solo eliminamos de personas, el CASCADE eliminará de alumnos automáticamente
    String sql = "DELETE FROM personas WHERE codigo = ?";

    // FASES 2 y 3: conexión y PreparedStatement en try-with-resources
    try (Connection con = Conexion.obtener();
         PreparedStatement ps = con.prepareStatement(sql)) {

        // FASE 3: Asignar parámetros
        ps.setInt(1, codigo);

        // FASE 4: Ejecutar la sentencia SQL
        int filasAfectadas = ps.executeUpdate();
        exito = filasAfectadas > 0;

        if (exito) {
            System.out.println("Alumno con código " + codigo + " eliminado exitosamente");
        } else {
            System.out.println("No se encontró alumno con código " + codigo);
        }
    } catch (SQLException e) {
        System.err.println("Error al eliminar alumno: " + e.getMessage());
        e.printStackTrace();
    }
    return exito;
}

📋 Palabras clave

AlumnoVO · AlumnoDAO · extends PersonaVO · herencia Java · doble INSERT · transacción · setAutoCommit · commit · rollback · JOIN · FK · ON DELETE CASCADE · telefono · tabla alumnos


🧠 Autoevaluación — Herencia en Java y BD


← Introducción: Herencia · 🏠 Inicio · AlumnoControladorServlet →

⚠️ **GitHub.com Fallback** ⚠️