Acceso a bases de datos con JDBC: tutorial completo Java 21

El patrón DAO separa la lógica de acceso a datos de la lógica de negocio. Define una interfaz para las operaciones CRUD y proporciona una implementación para cada base de datos. Es la base de Spring Data y JPA.

Estructura del patrón

src/main/java/com/ejemplo/
├── model/
│   └── Producto.java         # Record Java 21
├── dao/
│   ├── ProductoDao.java      # Interfaz
│   └── ProductoDaoJdbc.java  # Implementación JDBC
└── service/
    └── ProductoService.java  # Lógica de negocio (usa ProductoDao)
// model/Producto.java
record Producto(Integer id, String nombre, double precio, boolean activo) {
    // Constructor para inserciones (sin id)
    Producto(String nombre, double precio) { this(null, nombre, precio, true); }
}

// dao/ProductoDao.java
public interface ProductoDao {
    List<Producto> findAll();
    Optional<Producto> findById(int id);
    List<Producto> findByNombre(String patron);
    int save(Producto p);         // devuelve el id generado
    boolean update(Producto p);
    boolean delete(int id);
}
// dao/ProductoDaoJdbc.java
public class ProductoDaoJdbc implements ProductoDao {

    private final DataSource ds;

    public ProductoDaoJdbc(DataSource ds) { this.ds = ds; }

    @Override
    public List<Producto> findAll() {
        List<Producto> lista = new ArrayList<>();
        try (Connection c = ds.getConnection();
             Statement st = c.createStatement();
             ResultSet rs = st.executeQuery("SELECT * FROM productos WHERE activo=1 ORDER BY nombre")) {
            while (rs.next()) lista.add(mapRow(rs));
        } catch (SQLException e) { throw new RuntimeException("findAll", e); }
        return lista;
    }

    @Override
    public Optional<Producto> findById(int id) {
        try (Connection c = ds.getConnection();
             PreparedStatement ps = c.prepareStatement("SELECT * FROM productos WHERE id=?")) {
            ps.setInt(1, id);
            try (ResultSet rs = ps.executeQuery()) {
                return rs.next() ? Optional.of(mapRow(rs)) : Optional.empty();
            }
        } catch (SQLException e) { throw new RuntimeException("findById", e); }
    }

    @Override
    public int save(Producto p) {
        String sql = "INSERT INTO productos (nombre, precio, activo) VALUES (?, ?, ?)";
        try (Connection c = ds.getConnection();
             PreparedStatement ps = c.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
            ps.setString(1, p.nombre());
            ps.setDouble(2, p.precio());
            ps.setBoolean(3, p.activo());
            ps.executeUpdate();
            try (ResultSet keys = ps.getGeneratedKeys()) {
                return keys.next() ? keys.getInt(1) : -1;
            }
        } catch (SQLException e) { throw new RuntimeException("save", e); }
    }

    @Override
    public boolean update(Producto p) {
        String sql = "UPDATE productos SET nombre=?, precio=?, activo=? WHERE id=?";
        try (Connection c = ds.getConnection();
             PreparedStatement ps = c.prepareStatement(sql)) {
            ps.setString(1, p.nombre()); ps.setDouble(2, p.precio());
            ps.setBoolean(3, p.activo()); ps.setInt(4, p.id());
            return ps.executeUpdate() > 0;
        } catch (SQLException e) { throw new RuntimeException("update", e); }
    }

    @Override
    public boolean delete(int id) {
        try (Connection c = ds.getConnection();
             PreparedStatement ps = c.prepareStatement("UPDATE productos SET activo=0 WHERE id=?")) {
            ps.setInt(1, id);
            return ps.executeUpdate() > 0;
        } catch (SQLException e) { throw new RuntimeException("delete", e); }
    }

    private Producto mapRow(ResultSet rs) throws SQLException {
        return new Producto(rs.getInt("id"), rs.getString("nombre"),
                            rs.getDouble("precio"), rs.getBoolean("activo"));
    }
}

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR