# Guía 04 - Conectividad con Bases de Datos **Propósito:** Comprender e implementar la conexión entre aplicaciones de software y sistemas gestores de bases de datos (SGBD) utilizando diversos lenguajes de programación. ## Parte A: Fundamentos Teóricos ### 1. ¿Qué es la Conectividad de Bases de Datos? La conectividad a bases de datos es el mecanismo mediante el cual una aplicación cliente (ya sea web, de escritorio o móvil) se comunica con un servidor de bases de datos. Este proceso permite enviar instrucciones SQL, recuperar conjuntos de resultados y gestionar transacciones de forma programática. ### 2. Controladores (Drivers) y APIs Para que un lenguaje de programación se comunique con un SGBD específico, requiere un "puente" o traductor. * **Controladores Nativos:** Específicos para un SGBD (ej. `mysql-connector` para MySQL, `psycopg2` para PostgreSQL). * **Estándares de Conectividad:** * **JDBC (Java Database Connectivity):** Estándar de Java para interactuar con bases de datos relacionales. * **ODBC (Open Database Connectivity):** Estándar de C/C++ adoptado por múltiples ecosistemas. ### 3. La Cadena de Conexión (Connection String) Es una cadena de texto que contiene los parámetros necesarios para establecer la conexión. Comúnmente incluye: * **Host/Servidor:** Dirección IP o dominio (ej. `localhost` o `127.0.0.1`). * **Puerto:** Puerto de escucha del SGBD (ej. `3306` para MySQL, `5432` para PostgreSQL). * **Base de Datos:** Nombre de la base de datos objetivo. * **Usuario y Contraseña:** Credenciales de acceso. *Importante:* Por seguridad, las credenciales nunca deben estar "quemadas" (hardcoded) en el código fuente. Se deben utilizar variables de entorno (`.env`). ## Parte B: Conectividad en Múltiples Lenguajes A continuación, se presentan ejemplos de cómo establecer una conexión en varios de los lenguajes más utilizados en la industria, comenzando por PHP y MySQL, que es el entorno LAMP central de este curso. ### 1. PHP (usando PDO para MySQL - Entorno LAMP) ```php <?php $host = 'localhost'; $db = 'universidad'; $user = 'root'; $pass = 'admin123'; $charset = 'utf8mb4'; $dsn = "mysql:host=$host;dbname=$db;charset=$charset"; $options = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ]; try { $pdo = new PDO($dsn, $user, $pass, $options); echo "Conexión exitosa a la base de datos MySQL (LAMP)"; } catch (\PDOException $e) { throw new \PDOException($e->getMessage(), (int)$e->getCode()); } ?> ``` ### 2. Python (usando PostgreSQL y `psycopg2`) ```python import psycopg2 import os try: conexion = psycopg2.connect( host=os.getenv("DB_HOST", "localhost"), database=os.getenv("DB_NAME", "universidad"), user=os.getenv("DB_USER", "postgres"), password=os.getenv("DB_PASS", "admin123") ) print("Conexión exitosa a la base de datos") except Exception as e: print(f"Error al conectar: {e}") ``` ### 2. Node.js (usando MySQL y `mysql2`) ```javascript const mysql = require('mysql2'); require('dotenv').config(); const conexion = mysql.createConnection({ host: process.env.DB_HOST || 'localhost', user: process.env.DB_USER || 'root', password: process.env.DB_PASSWORD || 'admin123', database: process.env.DB_NAME || 'universidad' }); conexion.connect((err) => { if (err) { console.error('Error conectando a la base de datos:', err.stack); return; } console.log('Conexión exitosa con el ID ' + conexion.threadId); }); ``` ### 3. Java (usando JDBC) ```java import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class ConexionBD { public static void main(String[] args) { String url = "jdbc:postgresql://localhost:5432/universidad"; String user = System.getenv("DB_USER"); String password = System.getenv("DB_PASS"); try (Connection conn = DriverManager.getConnection(url, user, password)) { System.out.println("Conexión exitosa a PostgreSQL!"); } catch (SQLException e) { System.out.println("Error de conexión: " + e.getMessage()); } } } ``` ## Parte C: Sentencias Principales a través de Conexiones Una vez establecida la conexión, utilizamos objetos como **cursores** (en Python) o **Statements / PDOStatements** (en PHP/Java) para ejecutar comandos CRUD (Create, Read, Update, Delete). ### 1. Consultas (SELECT) Para recuperar datos, se ejecuta la consulta y se itera sobre los resultados. *Ejemplo en PHP (PDO - LAMP):* ```php <?php $sql = "SELECT id, nombre, apellido FROM estudiantes"; $stmt = $pdo->query($sql); $estudiantes = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($estudiantes as $estudiante) { echo "ID: " . $estudiante['id'] . " - Nombre: " . $estudiante['nombre'] . " " . $estudiante['apellido'] . "<br>"; } ?> ``` ### 2. Modificaciones (INSERT, UPDATE, DELETE) y Consultas Parametrizadas **Crucial:** Para evitar la Inyección SQL, NUNCA se deben concatenar variables directamente en la cadena SQL. Siempre se deben usar sentencias preparadas o parametrizadas. *Ejemplo de INSERT seguro en PHP (PDO - LAMP):* ```php <?php $sql = "INSERT INTO estudiantes (nombre, apellido, correo) VALUES (:nombre, :apellido, :correo)"; $stmt = $pdo->prepare($sql); // Bind de parámetros $stmt->bindParam(':nombre', $nombre); $stmt->bindParam(':apellido', $apellido); $stmt->bindParam(':correo', $correo); $nombre = "Ana"; $apellido = "García"; $correo = "[email protected]"; $stmt->execute(); echo "Filas afectadas: " . $stmt->rowCount(); ?> ``` ### 3. Manejo de Transacciones Las operaciones críticas que modifican datos en secuencia deben confirmarse con `commit()`. Si ocurre un error, se debe revertir con `rollback()` para mantener la integridad de la base de datos. ## Parte D: Ejercicio Práctico - Contexto "Universidad" (Entorno LAMP) ### Escenario Usted es el desarrollador backend del sistema de registro de la "Universidad Nacional". Se requiere crear pequeños scripts en PHP que interactúen con la base de datos MySQL/MariaDB 10 para gestionar la matrícula de estudiantes. ### Esquema de Base de Datos Base (MySQL / MariaDB 10) Asegúrese de ejecutar el siguiente DDL en su motor de base de datos antes de iniciar: ```sql CREATE TABLE estudiantes ( id INT AUTO_INCREMENT PRIMARY KEY, nombre VARCHAR(50) NOT NULL, apellido VARCHAR(50) NOT NULL, correo VARCHAR(100) UNIQUE NOT NULL, fecha_registro DATE DEFAULT CURRENT_DATE ); CREATE TABLE cursos ( codigo VARCHAR(10) PRIMARY KEY, nombre VARCHAR(100) NOT NULL, creditos INT NOT NULL ); ``` ### Desarrollo de Actividades (Resolución en PHP / PDO) **1. Configuración del Entorno y Módulo de Conexión:** *Cree un archivo `conexion.php`:* ```php <?php $host = 'localhost'; $db = 'universidad'; $user = 'root'; $pass = 'admin123'; // Cambiar según su configuración $charset = 'utf8mb4'; $dsn = "mysql:host=$host;dbname=$db;charset=$charset"; $options = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ]; try { $pdo = new PDO($dsn, $user, $pass, $options); } catch (\PDOException $e) { die("Error de conexión: " . $e->getMessage()); } ?> ``` **2. Operación de Inserción (Create) y Manejo de Errores (Integridad):** *Cree un archivo `insertar_estudiante.php` que maneje correos duplicados:* ```php <?php require 'conexion.php'; $nombre = "Carlos"; $apellido = "Mendoza"; $correo = "[email protected]"; try { $sql = "INSERT INTO estudiantes (nombre, apellido, correo) VALUES (:nombre, :apellido, :correo)"; $stmt = $pdo->prepare($sql); $stmt->execute(['nombre' => $nombre, 'apellido' => $apellido, 'correo' => $correo]); echo "Estudiante registrado correctamente."; } catch (\PDOException $e) { if ($e->getCode() == 23000) { // Código SQLSTATE para violación de integridad (ej. UNIQUE) echo "Error: El correo electrónico ya se encuentra registrado."; } else { echo "Error en la base de datos: " . $e->getMessage(); } } ?> ``` **3. Operación de Lectura (Read):** *Cree un archivo `listar_estudiantes.php` ordenados alfabéticamente:* ```php <?php require 'conexion.php'; $sql = "SELECT nombre, apellido, correo FROM estudiantes ORDER BY apellido ASC"; $stmt = $pdo->query($sql); $estudiantes = $stmt->fetchAll(); echo "<h2>Lista de Estudiantes</h2><ul>"; foreach ($estudiantes as $est) { echo "<li>{$est['apellido']}, {$est['nombre']} - {$est['correo']}</li>"; } echo "</ul>"; ?> ``` ### Nuevas Actividades para el Estudiante **4. Actualización de Datos (Update):** Desarrolle un script en PHP (`actualizar_correo.php`) que reciba el ID de un estudiante y un nuevo correo electrónico, y actualice el registro correspondiente en la tabla `estudiantes` utilizando sentencias preparadas. Muestre un mensaje indicando si la actualización fue exitosa o si el ID no existe. **5. Eliminación Segura (Delete):** Cree un script (`eliminar_estudiante.php`) que permita eliminar a un estudiante dado su ID. Antes de ejecutar el `DELETE`, el script debe realizar un `SELECT` para verificar que el estudiante exista y mostrar un mensaje de confirmación con el nombre del estudiante eliminado. **6. Transacciones (Inscripción en Lote):** Desarrolle un script (`registro_masivo.php`) que intente insertar tres estudiantes nuevos al mismo tiempo dentro de un bloque de transacción (`$pdo->beginTransaction()`). Provoque intencionalmente un error en el tercer insert (por ejemplo, duplicando un correo) y asegúrese de que el bloque `catch` ejecute un `$pdo->rollBack()`, verificando luego en la base de datos que ninguno de los estudiantes del lote fue insertado.