CRUD completo con JDBC

Patrón Repository para acceso a datos

La clase repositorio

El patrón Repository encapsula toda la lógica de acceso a datos de una entidad en una clase dedicada. El resto de la aplicación llama a métodos del repositorio sin conocer los detalles de JDBC — del mismo modo que funcionan Spring Data JPA o Hibernate, pero sin el framework.

import java.sql.*;
import java.util.*;

// Patrón Repository: encapsula todo el acceso a datos de una entidad
public class ClienteRepositorio {
    private final DataSource dataSource;

    public ClienteRepositorio(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    // CREATE
    public long insertar(Cliente c) throws SQLException {
        String sql = "INSERT INTO clientes (nombre, email, activo) VALUES (?, ?, ?)";
        try (Connection con = dataSource.getConnection();
             PreparedStatement ps = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
            ps.setString(1, c.getNombre());
            ps.setString(2, c.getEmail());
            ps.setBoolean(3, c.isActivo());
            ps.executeUpdate();
            try (ResultSet keys = ps.getGeneratedKeys()) {
                keys.next();
                return keys.getLong(1);
            }
        }
    }
    // READ por ID
    public Optional<Cliente> buscarPorId(long id) throws SQLException {
        String sql = "SELECT id, nombre, email, activo FROM clientes WHERE id = ?";
        try (Connection con = dataSource.getConnection();
             PreparedStatement ps = con.prepareStatement(sql)) {
            ps.setLong(1, id);
            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) return Optional.of(mapear(rs));
                return Optional.empty();
            }
        }
    }

    // READ lista
    public List<Cliente> buscarTodos() throws SQLException {
        String sql = "SELECT id, nombre, email, activo FROM clientes ORDER BY nombre";
        List<Cliente> lista = new ArrayList<>();
        try (Connection con = dataSource.getConnection();
             PreparedStatement ps = con.prepareStatement(sql);
             ResultSet rs = ps.executeQuery()) {
            while (rs.next()) lista.add(mapear(rs));
        }
        return lista;
    }

    // UPDATE
    public int actualizar(Cliente c) throws SQLException {
        String sql = "UPDATE clientes SET nombre = ?, email = ?, activo = ? WHERE id = ?";
        try (Connection con = dataSource.getConnection();
             PreparedStatement ps = con.prepareStatement(sql)) {
            ps.setString(1, c.getNombre());
            ps.setString(2, c.getEmail());
            ps.setBoolean(3, c.isActivo());
            ps.setLong(4, c.getId());
            return ps.executeUpdate(); // filas afectadas
        }
    }

    // DELETE
    public int eliminar(long id) throws SQLException {
        String sql = "DELETE FROM clientes WHERE id = ?";
        try (Connection con = dataSource.getConnection();
             PreparedStatement ps = con.prepareStatement(sql)) {
            ps.setLong(1, id);
            return ps.executeUpdate();
        }
    }

    private Cliente mapear(ResultSet rs) throws SQLException {
        Cliente c = new Cliente();
        c.setId(rs.getLong("id"));
        c.setNombre(rs.getString("nombre"));
        c.setEmail(rs.getString("email"));
        c.setActivo(rs.getBoolean("activo"));
        return c;
    }
}

Uso del repositorio

// Uso del repositorio
DataSource ds = crearDataSource(); // HikariCP, DriverManager, etc.
ClienteRepositorio repo = new ClienteRepositorio(ds);

// Crear
Cliente nuevo = new Cliente();
nuevo.setNombre("Ana García");
nuevo.setEmail("ana@ejemplo.com");
nuevo.setActivo(true);
long id = repo.insertar(nuevo);
System.out.println("Creado con id: " + id);

// Leer
Optional<Cliente> encontrado = repo.buscarPorId(id);
encontrado.ifPresent(c -> System.out.println("Encontrado: " + c.getNombre()));

// Actualizar
encontrado.ifPresent(c -> {
    c.setEmail("nuevo@ejemplo.com");
    try { repo.actualizar(c); } catch (SQLException e) { throw new RuntimeException(e); }
});

// Eliminar
int filas = repo.eliminar(id);
System.out.println("Eliminados: " + filas + " registros");
En proyectos reales: este patrón es exactamente lo que Spring JDBC Template y Spring Data JDBC automatizan. Conocer la implementación manual ayuda a entender qué hace el framework por debajo, depurar problemas de rendimiento y trabajar con código legacy que no usa frameworks.

Siguiente apartado → Transacciones: commit, rollback y niveles de aislamiento

Índice de la sección

Índice del curso