GESTIÓN DE CONECTORES (DESARROLLO DE APLICACIONES QUE GESTIONAN INFORMACIÓN EN BASES DE DATOS RELACIONALES) PDF
Document Details
Uploaded by RightLife4593
ILERNA
Tags
Summary
This document is about database management, including relational databases and database concepts. It introduces the basic ideas of databases and describes different types of databases in use in programming.
Full Transcript
2 GESTIÓN DE CONECTORES (DESARROLLO DE APLICACIONES QUE GESTIONAN INFORMACIÓN BASES DE DATOS RELACIONALES) EN Acceso a datos En este tema introduciremos todos los conceptos relacio- nados con bases de datos y aprender...
2 GESTIÓN DE CONECTORES (DESARROLLO DE APLICACIONES QUE GESTIONAN INFORMACIÓN BASES DE DATOS RELACIONALES) EN Acceso a datos En este tema introduciremos todos los conceptos relacio- nados con bases de datos y aprenderemos a realizar todo tipo de conexiones y consultas a bases de datos. CONCEPTO Una base de datos es un conjunto de infor- mación almacenada en un servidor o un disco que permite guardar información de cualquier tipo para poder recuperarla y usarla en una aplicación cuando sea necesario. Cada base de datos se compone por un grupo de tablas. Cada tabla se compone por una o más columnas que defini- rán el nombre de cada elemento que guardar. Cada registro será una fila en esta tabla, que corresponderá a uno de los datos que guardar. En programación, es muy común usar bases de datos rela- cionales. CONCEPTO Una base de datos relacional es un tipo de base de datos en la cual las tablas se relacionan entre sí. En una base de datos relacional, cada fila de una tabla contiene un registro único llamado ID que se definirá como una clave única que ninguna fila más de la tabla puede compartir. Ese identificador es el que nos facilitará la rela- ción entre una tabla y otra. Este tipo de base de datos es especialmente útil para las aplicaciones que tienen que gestionar una gran cantidad de datos que administrar. Además, facilitan la coherencia de datos, ya que permiten varias instancias de base de datos. 73 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) 2.1. Gestores de bases de datos embebidos e independientes En este apartado, profundizaremos en los conceptos bási- cos de la base de datos embebida y explicaremos las BBDD más comunes de este tipo. Cuando desarrollamos una aplicación, en muchos casos, es necesario el uso de bases de datos para almacenar la infor- mación que se gestiona. Normalmente, se usan bases de datos que guardan los registros en el disco (cuando traba- jamos en una aplicación en local) o en un servidor, pero existen otras alternativas. Aquí entran las bases de datos embebidas. CONCEPTO Las bases de datos embebidas son aquellas bases de datos que son incorporadas dentro del software del programa por el desarrollador, de manera que es invisible para el usuario. Este tipo de BBDD no necesita ninguna interfaz externa para mantener la base de datos. La característica principal es que se ejecuta al arrancar la aplicación, es decir, cuando la ejecutamos en el servidor. Se recomienda su uso en apli- caciones que no requieran almacenar datos por parte de los usuarios o para probar aplicaciones en el entorno de desarrollo. Se adapta perfectamente a las aplicaciones que solo requieren un repositorio para mantener su propia transacción sin la intervención del usuario final. 74 Acceso a datos Sin embargo, es necesario recalcar que este tipo de base de datos ofrece una falta de cohesión, y se puede convertir en una opción muy difícil de mantener al no estar registrada en un disco. En las siguientes secciones veremos algunas de las bases de datos en memoria más utilizadas para el entorno Java y la configuración necesaria para cada una de ellas. Podemos encontrar diferentes opciones de BBDD embebi- das. Aquí detallamos las más conocidas: SQLite: es un gestor de BBDD muy usado en lenguajes como Java, PHP, VisualBasic o Perl. Se caracteriza por ser muy ligero, multiplataforma, no requiere instalación y ofrece un buen rendimiento. Como curiosidad principal, la base de datos se guarda como un fichero guardado en la aplicación. H2: es un gestor de código abierto y se caracteriza por soportar SQL para gestionar la base de datos. También se trata de una BBDD multiplataforma, tiene un tamaño re- ducido y es una de las más rápidas. Apache Derby: usa el modelo de base de datos relacio- nal. Se caracteriza por tener un tamaño muy reducido y es una opción muy fácil de instalar, usar e implementar. Firebird: también es un gestor de base de datos de có- digo abierto, multiplataforma y que es soportado por diferentes lenguajes. Oracle Embedded: se caracteriza por su fácil instalación y su configuración automática. Su rendimiento es de los más eficaces, se adapta a las aplicaciones móviles gra- cias a que consume muy pocos recursos en este tipo de dispositivos. HSQLDB: es otra opción disponible que se caracteriza por ser muy rápida en la gestión de consultas, es de códi- go libre y permite gestión de bases de datos relacionales. 2.2. El desfase objeto-relacional Una de las características principales de la programación en el lenguaje Java es que se trata de un lenguaje orienta- do a objetos, lo que será clave para diseñar nuestra futura aplicación. La aplicación que desarrollemos requerirá una conexión a una base de datos para poder persistir los datos que la aplicación gestione. Para ello, necesitaremos diseñar una base de datos relacional, y es donde nos encontraremos los primeros problemas. Podemos entender que: 75 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) CONCEPTO La programación orientada a objetos es un paradigma de programación que permite Serialización desarrollar aplicaciones complejas con una estructura más clara y entendible, en la que youtu.be/-QvgHnudI3o se organiza el código en clases, en las cuales predominan, por su importancia, los objetos. En otras palabras, la programación orientada a objetos se caracteriza por gestionar entidades y realizar asociaciones entre ellos. Por otro lado, las bases de datos utilizan otro tipo de nexo para entrelazarse, que no es otro que identificado- res. Cada tabla se puede relacionar con otra con una columna de un identificador de otra. Es por ese motivo, y por la dife- rencia de relaciones entre un lenguaje orientado a objetos y la base de datos, que se produce este tipo de desfase. CONCEPTO El desfase objeto-relacional es aquel que surge cuando se desarrollan aplicaciones orientadas a objetos usando una base de datos de tipo relacional. Para entender mejor el concepto, lo que ocurre en nuestra aplicación es que tendremos algo más o menos así: public class Estudiante { private int id; private String dni; private String nombre; private String apellido; private int edad; //constructor // getters y setters } Un objeto llamado “Estudiante”, con diferentes atributos. En cambio, en la base de datos, cuando creamos la tabla, normalmente creamos campos que se asemejen a los defi- nidos en el objeto de la clase. Como podemos ver, no están definidos del mismo modo que en el objeto, sino con un equivalente, y realizamos el 76 Acceso a datos mapeo de manera manual. Es decir, cada vez que tenga- mos que añadir o modificar registros, se tendrán que hacer de manera manual a través de consultas de base de datos. Además, los tipos de atributos en un objeto y en una tabla no se definen con el mismo tipo, sino con equivalentes. CREATE TABLE estudiante ( id INT PRIMARY KEY AUTO_INCREMENT; dni VARCHAR(50) NOT NULL, nombre VARCHAR(50) NOT NULL, apellido VARCHAR(100), edad INT DEFAULT 10; ); 2.3. Conexión a bases de datos En este apartado del tema, aprenderemos cómo conectar nuestra aplicación de Java a una base de datos, cómo rea- lizar diferentes consultas y las acciones básicas para tratar Modelos de acceso a los datos. datos youtu.be/ka6KIvrh4QA 2.3.1. Protocolos de acceso a bases de datos. Conectores Cuando tenemos una aplicación creada en Java, en muchos casos es necesario guardar datos en una base de datos. Para ello, es necesario establecer una conexión entre la aplicación y la base de datos. En ese caso, es necesario utilizar una interfaz para acceder a la base de datos desde Java con la API JDBC (Java Database Conectivity). A través de ella, se establece la conexión a la BBDD, que nos permitirá realizar consultas, actualizar los datos y recibir resultados de las consultas. Permite realizar operaciones con lenguaje SQL independientemente de la instancia de la base de datos utilizada. Para usar JDBC, es necesaria la implementación específica de la base de datos del controlador JDBC. Acceso a datos mediante el puente JDBC-ODBC La API de JDBC soporta la comunicación entre la aplicación youtu.be/94SWbLz36lc Java y el driver que realizará la conexión entre la base de datos, es decir, actúa como intermediaria. JDBC es la API común con la que interactúa el código de nuestra aplica- ción. Debajo está el controlador compatible con JDBC para la base de datos que vamos a utilizar. Para realizar la conexión, necesitaremos el driver JDBC, que es el que nos permitirá que nuestra aplicación interactúe con la base de datos. Según qué base de datos queramos utilizar, tendremos que usar el driver correspondiente. 77 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) JDBC API Aplicación Driver JDBC BBDD Ilustración 16. Esquema de conexión de base de datos en una aplicación Java. Como ventajas, podemos destacar que es muy fácil de uti- lizar y permite conectarse a cualquier base de datos. Por otro lado, como desventajas podemos destacar que su rendimiento puede verse degradado y el driver se tiene que instalar en el cliente. Ilustración 17. Indicación para descargar el jar del driver de MySQL. 2.3.2. Establecimiento de conexiones Para entender mejor cómo se realiza la conexión, realizare- mos un ejercicio explicativo. BUSCA EN LA WEB Recordad que todos los ejercicios los tenéis en GitLab, y os permitirá seguir mejor la explicación. Aquí tenéis el enlace: https://gitlab.com/ilerna/common/java 78 Acceso a datos Para este ejercicio, utilizaremos una base de datos MySQL. El proceso de instalación de MySQL puede ser algo compli- cado. A continuación, detallaremos los pasos para tener correctamente instalada la base de datos y crear una nueva base de datos para nuestro proyecto. Tenemos que descar- gar el instalador de aquí: BUSCA EN LA WEB https://dev.mysql.com/downloads/installer/ Abriremos el instalador y se abrirá este pop up y seguire- mos los pasos que vemos detallados en las capturas. Ilustración 18. Paso 1: le damos a Next. 79 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) Ilustración 19. Paso 2: elegimos instalación Full y le damos a Next. Ilustración 20. Paso 3: apretar botón Execute. 80 Acceso a datos Ilustración 21. Paso 4: dar a Instalar si aparece el pop up. Ilustración 22. Paso 5: saldrá este pop up en el cual debemos seleccionar Execute. 81 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) Ilustración 23. Paso 6: al acabar el paso anterior, saldrá este, solo hay que pulsar Next. Ilustración 24. Paso 7: elegimos la selección que vemos por pantalla y damos a Next. 82 Acceso a datos Ilustración 25. Paso 8: dejamos la configuración por defecto y damos a Next. Ilustración 26. Paso 9: si nos sale esta pantalla, elegimos la opción de la pantalla y damos a Next. 83 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) Es importante prestarle atención a esta captura de panta- lla, porque es la que nos permite establecer la contraseña de la base de datos. El usuario por defecto era “root”, pero podemos elegir la contraseña que deseemos. Es importan- te guardar la contraseña porque la utilizaremos en el futuro para establecer la conexión a la base de datos. Ilustración 27. Paso 10: tendremos que definir la contraseña de nuestra base de datos. Ilustración 28. Paso 11: en esta pantalla dejamos valores por defecto o como en la captura y damos a Next. 84 Acceso a datos Ilustración 29. Paso 12: pulsamos el botón Execute. Ilustración 30. Paso 13: en esta pantalla solo tendremos que darle a Next. 85 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) A continuación, nos aparecerá esta pantalla. Servirá para comprobar la conexión. Tenemos que meter el usuario “root” y la contraseña que hayamos elegido unos pasos anteriores. Le damos al botón de Check y tiene que salir el mensaje “Connection succeded” como en la captura. Si no es así, es que no hemos introducido bien el usuario o la contraseña. Una vez que todo esté correcto, pulsamos sobre Next. Ilustración 31. Paso 14: esta pantalla permite revisar la conexión con la BBDD. Ilustración 32. Paso 15: en esta pantalla le debemos pulsar Execute. 86 Acceso a datos Ilustración 33. Paso 16: si todo va bien en el paso anterior, saldrá esta ventana y le damos a Finish. Después de todo esto, saldrá otra ventana en la cual solo debemos darle a Next y ya habremos acabado la instalación. Una vez finalizada, deberemos crear la base de datos que utilizará nuestra aplicación. Cada aplicación debería tener su base de datos por separado para diferenciarla del resto y para mantener un orden. Este paso lo debemos realizar mediante un script. Un script es un conjunto de sentencias SQL que ejecutan ta- reas en la base de datos para realizar ciertas acciones. En este caso, tendremos que crear la base de datos. Para eje- cutar estas sentencias, deberemos abrir MySQL Client, que se ha instalado en nuestro ordenador durante la instala- ción de MySQL. Ilustración 34. Captura de MySql Comand Line Client. 87 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) Pondremos la contraseña que hemos definido durante la instalación y ya podremos realizar la creación de nuestra base de datos. Los comandos que tendremos que ejecutar son: CREATE DATABASE ilerna; CREATE USER ‘alumno’@’localhost’ IDENTIFIED BY ‘password’; GRANT ALL PRIVILEGES ON *.* TO ‘alumno’@’localhost’; La primera línea crea la base de datos para nuestra aplicación. La segunda crea un usuario y le asigna una contraseña. El usuario que hemos creado al principio es el usuario administrativo de nuestra base de datos MySQL. Cuando creamos una base de datos para una aplicación, también debemos crear el usuario que gestionará esa base de datos. Por eso, creamos uno para la aplicación. A con- tinuación, debemos darle permisos para realizar cualquier tipo de acción en la base de datos. Después necesitaremos descargar el driver de MySQL que nos permitirá realizar la conexión con la base de datos y tendremos que añadirlo a nuestro proyecto. Lo descargare- mos de este enlace: BUSCA EN LA WEB https://dev.mysql.com/downloads/connector/j/ Después de añadir el driver a nuestro proyecto, es el mo- mento de realizar la conexión usando JDBC. Podremos diferenciar tres pasos para realizar el proceso: 2.3.2.1. Registrar el driver JDBC Cuando tengamos la clase creada de encargarse de las co- nexiones, tendremos que registrar el driver JDBC. Este proceso no es más que indicarle a la clase a qué driver tiene que apuntar. Al registrar el driver, se carga en memoria para poder ser utilizado por la interfaz JDBC. try { Class.forName("com.mysql.jdbc.Driver"); }catch(ClassNotFoundException ex) { System.out.println("Error al cargar el driver."); } 88 Acceso a datos Como vemos en el ejemplo, para registrar el driver usare- mos Class.forName(). Al llamar al método forName() lo que hacemos es cargar de manera dinámica el driver de la clase en memoria. Necesitamos pasar por parámetro el driver de la base de datos que vamos a usar. Esta llamada solo se va a realizar una vez. Si no usamos una base de datos MySQL, el procedimiento es el mismo, pero le pasaremos el driver de la base de datos correspondiente. 2.3.2.2. Crear una URL de conexión a la base de datos Después de cargar el driver, tendremos que establecer la conexión con la base de datos. Para ello, usaremos Dri- verManager.getConnection(). Al llamar a getConnection() tendremos que pasarle una URL indicando qué base de datos usaremos, el nombre, el usuario y la contraseña para que pueda acceder a la BBDD. Según la BBDD que usemos, el formato de la URL será algo distinto. Aquí tenemos una muestra ejemplo de las más importantes: RDBMS Nombre driver JDBC Formato URL MySQL com.mysql.jdbc.Driver jdbc:mysql://hostname/ databaseName ORACLE oracle.jdbc.driver.OracleDriver jdbc:oracle:thin:@hostname:port Number:databaseName DB2 COM.ibm.db2.jdbc.net.DB2Driver jdbc:db2:hostname:port Number/ databaseName Tabla 11. Tabla esquemática según BBDD para la formación de la URL de conexión. Como podemos ver, la estructura para formar la URL es siempre parecida, pero tiene variaciones según la BBDD, por eso es importante mirar antes cómo formarla para establecer correctamente la conexión y así evitar posibles errores. 2.3.2.3. Crear los objetos de conexión Preparar la conexión a la base de datos es un paso impor- tante que tener en cuenta. Para realizar la conexión, es necesario utilizar la clase Connection. Este será el inter- mediario que conectará nuestra aplicación y la base de datos. Si estamos utilizando otro entorno para realizar la conexión, se necesitará el nombre del host y el puerto. Para construir la URL, seguiremos esta estructura: 89 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) String url = “jdbc:mysql://hostname:port/ilerna”; String url = “jdbc:mysql://localhost/ilerna”; String usuario= “alumno”; String contrasena = “password” Connection conn = DriverManager.getConnection(url, usuario, contrasena); Para realizar la conexión se utiliza DriverManager y se llamará al método getConnection(), que devolverá un objeto Connection con todos los datos de la conexión rea- lizada en BBDD. Ahora que sabemos cómo realizar la conexión, veremos un ejemplo práctico con todo lo que hemos aprendido hasta ahora. Connection connection; String url = "jdbc:mysql:tema2"; String usuario = "usuario"; String contrasena = "pasword"; try { Class.forName("com.mysql.jdbc.Driver"); connection = DriverManager.getConnection(url,usuario, contrasena); // Acceso a datos utilizando el objeto de conexión } catch (SQLException sqle) { // Trataremos el error si no se establece conexión } finally { try { connection.close(); } catch (SQLException e) { // Trataremos el error si no podemos cerrar la conexión } } En la muestra de código anterior, podemos ver una simpli- ficación de lo que sería una conexión sencilla. En primer lugar, se deberá declarar el objeto Connection, sin asignar valor. Este objeto es el encargado de recoger el resultado de la conexión que realiza la clase DriverManager. A continua- ción, se definirá la URL a la BBDD creada (en esta ocasión es “tema2”, pero en vuestro lugar será el nombre de vuestra base de datos), el usuario y la contraseña. Una vez realizada la conexión, si se produce un error se debe recoger en un SQLException, tal y como mostramos en el ejemplo, y recordad de cerrar la conexión: debemos cerrar los recursos de la aplicación, pues dejarla abierta nos dará problemas en futuras consultas. Para poner en práctica los conceptos explicados, vamos a crear nuestra base de datos. 90 Acceso a datos public class ConnectorBBDD { //Declaramos los diferentes objetos que nos permitirán realizar la co- nexion private Connection connect = null; // JDBC driver static final String JDBC_DRIVER = “com.mysql.jdbc.Driver”; static final String DB_URL = “jdbc:mysql://tema2”; static final String USUARIO = “usuario”; static final String CONTA = “password”; public Connection conector() throws Exception { try { // Esto se encarga de cargar el driver de MySQL Class.forName(JDBC_DRIVER); // Establecemos la conexion Connection connect = DriverManager.getConnection(DB_URL + “user=” + USUARIO + “ &password=” + CONTA); } catch (Exception e) { throw e; } finally { connect.close(); } return connect; } } Método Descripción getConnection(String url) Establecerá la conexión con tan solo la URL. La URL deberá contener toda la información necesaria para conectarse a la base de datos. getConnection(String url, Este método se encargará de realizar la conexión a partir Properties info) de la URL y un Properties. Esta clase contendrá toda la información necesaria para conectarse a la base de datos, como el usuario o la contraseña. Debemos tener en cuenta que cabe la posibilidad de que se produzca un error en este proceso, por este motivo hemos englobado todos los pasos dentro de un try-catch que con- trolará el error si se produce. Un posible error podría ser un error de conexión debido a que hemos definido incorrecta- mente la conexión, a que el usuario o la contraseña están mal o a que no hemos realizado el proceso de registrar el driver. En todo caso, si se produce un error, entrará en el bloque catch. Para finalizar, vemos que tenemos un bloque finally para cerrar la conexión del objeto. 91 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) 2.3.3. Definición de objetos destinados al almacenamiento del resultado de operaciones con bases de datos. Eliminación de objetos una vez finalizada su función Ya realizada la conexión, nuestra aplicación está preparada para interactuar con la base de datos. En este apartado os explicaremos qué objetos serán los más adecuados para almacenar la información de nuestras operaciones con la base de datos. Podemos destacar estos tres como los más importantes: Nombre de la interfaz Descripción PreparedStatement Se utiliza para las operaciones SQL con parámetros dinámi- cos. Esta interfaz permite pasar parámetros a sus métodos y ejecutar las operaciones más de una vez. Statement Se utiliza de manera general para cualquier tipo de opera- ción con la base de datos. Es especialmente útil con SQL sin valores dinámicos. ResultSet Esta interfaz se caracteriza por devolver la información en forma de tabla. CallableStatement Se utiliza para trabajar con procedimientos. Tabla 12. Interfaces más importantes para las operaciones con bases de datos. 92 Acceso a datos 2.3.3.1. Interfaz Statement Este tipo de interfaz se utiliza para conexiones de carácter general. Es bastante útil cuando queremos usar consultas estáticas SQL. CONCEPTO Una consulta estática es aquella operación SQL que no varía la consulta en ningún momento, siempre será la misma y los valores de la consulta no cambiarán. Este tipo de clase no acepta parámetros. Las sentencias más utilizadas son todas aquellas que no necesitan datos de los objetos Java, como, por ejemplo, cualquier create, insert, delete, alter o drop. La implementación sería algo más o menos así: Statement stmt = null; private Connection connect = null; // JDBC driver name and database URL private String JDBC_DRIVER = "com.mysql.jdbc.Driver"; private final String DB_URL = "jdbc:mysql://localhost/ilerna"; private String USUARIO = "alumno"; private String CONTA = "password"; try { Class.forName(JDBC_DRIVER); // Establecemos la conexion connect = DriverManager.getConnection(DB_URL + "user=" + USUARIO + " &password=" + CONTA); System.out.println("Nos hemos conectado a la BBDD"); stmt = connect.createStatement(); String sql = "ALTER DATABASE ilerna MODIFY NAME = tema2"; stmt.executeUpdate(sql); } catch (SQLException e) { //continuación codigo } finally { stmt.close(); } Una vez creado el objeto, podemos usarlo para ejecutar consultas SQL. Este objeto tiene tres métodos importantes para ello: execute(): se usará para ejecutar consultas dinámicas SQL. executeUpdate(): se usa cuando hacemos insert, delete o update. executeQuery(): se usa para realizar select. 93 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) Debemos tener en cuenta que, cada vez que creemos este tipo de objeto, tendremos que cerrarlos, por lo que es ne- cesario utilizar el método close() para cerrar la conexión. 2.3.3.2. Creación de objetos PreparedStatement Esta interfaz hereda de Statement, y nos dará mucha más funcionalidad respecto del objeto anterior. Este objeto nos ofrecerá más flexibilidad, permitiendo pasarle dinámica- mente argumentos al objeto. Podremos usar todas las sentencias mencionadas anteriormente, igual que en el objeto Statement. PreparedStatement pstmt = null; try { Estudiante estudiante = new Estudiante(); String SQL = "select from Estudiante where id=? And apellido = ?"; pstmt = conn.prepareStatement(SQL); pstmt.setInt(1, estudiante.getId()); pstmt.setString(2, estudiante.getApellido()); //continuación de código }catch (SQLException e) { //continuación de código } finally { pstmt.close(); } En JDBC, los parámetros que vemos en la sentencia SQL están representados por un símbolo de interrogación, conocido como marcador de parámetro. Tendremos que proporcionar valores para cada parámetro antes de eje- cutar la consulta. Estos valores los podremos asignar con los datos de nuestro objeto Java, en este caso, el objeto “Estudiante” con los getters getId() y getApellido(), que recogerán el valor de este atributo y nos servirán para asignárselo de manera dinámica. Si no se asignan los ar- gumentos, se lanzará una excepción de tipo SQLException. Este tipo de objeto también dispone de los mismos méto- dos que el objeto que hemos visto en el apartado anterior, pero, como diferencia, a estos métodos podemos pasarles parámetros. execute(String sql): se usa para ejecutar consultas diná- micas SQL. executeUpdate(String sql): se usa cuando hacemos in- sert, delete o update. executeQuery(String sql): se usa para realizar select. 94 Acceso a datos Si nos fijamos, al final del ejemplo también tenemos un close() para este tipo de objetos, ya que no es diferente de lo mencionado anteriormente. Siempre que abramos un objeto para interactuar con una BBDD, tendremos que cerrarlo al final de su uso. 2.3.3.3. Creación de objetos ResultSet ResultSet es otra interfaz que podemos utilizar para re- coger el resultado de una de nuestras consultas. Solo podemos utilizar este tipo de objeto para seleccionar datos, ya que no permite actualizar datos, es decir, solo se usa en las sentencias select. Se caracteriza por devolver una tabla de datos según columnas y filas. Para obtener los datos de la consulta, se tendrá que acceder con un get más el tipo de dato al que queramos acceder. Por ejemplo, getInt devolverá un integer, mientras que un getString devolverá un varchar. Aquí tenemos una relación de datos que podemos obtener en nuestras consultas. Tipo de get Tipo de dato de la BBDD getInt Integer getLong Big int getFloat Real getDouble Float getBignum BIT getString Varchar/char getDate Date getTime Time getTimestamp Time stamp getObject Cualquier otro tipo Tabla 13. Tabla con los get más comunes del objeto ResultSet Se caracteriza también por solo avanzar hacia delante con los datos que obtiene, por tanto, no podremos recorrerlos hacia atrás. Para ver cómo funciona, aquí mostramos un breve ejemplo de implementación. Tal y como apreciamos en el ejemplo, primero necesitamos un objeto PreparedStatement para realizar la conexión y preparar la consulta. A continuación, creamos un ResultSet 95 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) que nos permitirá ejecutar la consulta y que es donde guar- daremos los datos obtenidos. Como vemos, para ejecutar la consulta solo necesitamos que el objeto PreparedSta- tement llame al método executeQuery() y que el objeto ResultSet recoja el resultado. Con el método next() recorre- mos los resultados hacia delante y podemos ir accediendo a ellos según su tipo, como hemos explicado anteriormente. 2.3.4. Ejecución de sentencias de descripción de datos CONCEPTO Entendemos como sentencias de descripción de datos (data definition language) todas aquellas que permitan manipular la estructura de nuestra base de datos. Se trata de una manera de definir ciertos comandos de datos. Estas sentencias pueden ser: CREATE: se encarga de crear bases de datos o tablas. DROP: la usaremos para borrar bases de datos o tablas. ALTER: la usaremos para la modificación de tablas o ba- ses de datos. RENAME: usaremos esta sentencia para cambiar el nom- bre de nuestra base de datos o de una tabla. //Paso 1 establecer conexión + pasar la consulta PreparedStatement s= con.prepareStatement(“select id, dni, nombre from Estudiantes”); //Paso 2. Definir resultSet y ejecutar la consulta ResultSet resultado = s.executeQuery(); //Paso 3. Imprimir el resultado while(resultado.next()){ System.out.printIn(“Id: “ + resultado.getInt()); System.out.printIn(“dni: “ + resultado.getString()); System.out.printIn(“nombre: “ + resultado.getString()); } //Paso 4: Cerramos los objetos que usamos para realizar la conexión y obtener resultado resultado.close(); s.close(); Estos comandos son exclusivamente para hacer modifica- ciones en las tablas, no para realizar consultas en MySQL. JDBC nos da opciones para poder hacer este tipo de sen- tencias mediante el código Java. 96 Acceso a datos 2.3.4.1. Crear tablas o bases de datos El comando CREATE se puede usar para crear una tabla o una base de datos. Normalmente, ya tendremos creada la base de datos, pero si en alguna ocasión se tiene que rea- lizar, se puede hacer mediante la aplicación. Aquí tenemos la estructura teórica que podemos adaptar para nuestras sentencias y necesidades. La estructura básica de estas sentencias son estas, a modo de recordatorio: //Para crear una base de datos CREATE DATABASE nombre_base_datos; //Para crear una tabla CREATE TABLE nombre_tabla ( Columna_1 tipo_dato, Columna_2 tipo_dato, ... ); Si tenéis dudas de los tipos de datos permitidos en la crea- ción de sentencias SQL, aquí podéis encontrar un enlace con el resumen de los que existen: BUSCA EN LA WEB https://bit.ly/3nAmKJu 97 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) Con CREATE DATABASE podremos crear nuevas bases de datos. Dentro de esa base de datos, podremos crear las tablas para nuestra aplicación. Se crearán bases de datos nuevas siempre que sea necesario, aunque lo más habitual es crear nuevas tablas. En primer lugar, hemos creado un método que se encargará de realizar la creación de una nueva base de datos. Segui- damente, se define Connection, que nos permitirá realizar la conexión a la base de datos con nuestra clase auxiliar. Este es el paso 1 que vemos en el ejemplo. En segundo lugar, tenemos un objeto Statement. Como hemos explicado en apartados anteriores, este tipo es muy útil para cualquier tipo de operación con base de datos y es el más simple que existe, por este motivo elegimos de- finir esta clase. Cuando la conexión esté realizada con el objeto Connection, podremos llamar al método createSta- tement(), que preparará un objeto Statement para poder enviar nuestras operaciones SQL a la base de datos. A continuación, tenemos que crear en un string la sentencia SQL que permitirá crear la base de datos. Y una vez creado, le pasaremos el string al método executeUpdate() del ob- jeto Statement creado. public void crecionBaseDeDatos() throws Exception { Connection conn = null; Statement stmt = null; try { //Paso 1.Previamente habremos realizado la conexión conn = conector.conector(); //Paso 2. Creamos un nuevo objeto con la conexión stmt = conn.createStatement(); //Paso 3. Definimos la sentencia de crear una nueva base de datos String sql = "CREATE DATABASE ejemplo"; //Paso 4. Ejecutar la sentencia stmt.executeUpdate(sql); }catch(SQLException se){ //Gestionamos los posibles errores que puedan surgir durante la ejecu- cion de la insercion se.printStackTrace(); }catch(Exception e){ //Gestionamos los posibles errores e.printStackTrace(); }finally{ //Paso 5. Cerrar el objeto en uso y la conexión stmt.close(); conn.close(); } } } 98 Acceso a datos Para la creación de tablas, el proceso es exactamente el mismo, pero cambia el tipo de sentencia utilizado. En todos los casos, debemos tener en cuenta que tenemos que englobar todo el procedimiento en un try-catch para controlar los posibles errores que se puedan producir. Los más usuales serán: Error de conexión a la base de datos. Error en la sentencia SQL que hemos definido. Error en cerrar los objetos en uso. Para ello, debemos utilizar SQLException como el primer catch, ya que este tipo de error es más específico. En se- gundo lugar, deberíamos tener un Exception para poder controlar cualquier otro error que no tenga que ver con la base de datos. 99 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) public void crecionTabla() throws Exception { Connection conn = null; Statement stmt = null; try { conn = conector.conector(); stmt = conn.createStatement(); String sql ="CREATE TABLE estudiante (" + " id INT PRIMARY KEY AUTO_INCREMENT;" + " dni VARCHAR(50) NOT NULL," + " nombre VARCHAR(50) NOT NULL," + " apellido VARCHAR(100)," + " edad INT DEFAULT 10;" + ");"; stmt.executeUpdate(sql); }catch(SQLException se){ se.printStackTrace(); }catch(Exception e){ e.printStackTrace(); }finally{ stmt.close(); conn.close(); } } } 2.3.4.2. Modificar tablas o bases de datos Para la modificación de tablas o de bases de datos utiliza- remos la sentencia ALTER. ALTER es la operación SQL que se encargará de realizar cambios dentro de la base de datos o de la tabla que especifiquemos, siempre que esos datos existan. Nos permitirá añadir, cambiar o eliminar campos de una tabla, o también renombrar una tabla. Estas son las estructuras básicas para realizar este tipo de operaciones. //Para modificar la base de datos ALTER DATABASE nombre_base_de_datos MODIFY NAME=nuevo_nombre; //Para añadir columnas ALTER TABLE nombre_tabla ADD COLUMN nombre_columna tipo_dato; //Para modificar columnas ALTER TABLE nombre_tabla MODIFY COLUMN nombre_columna tipo_dato; //Para borrar una columna ALTER TABLE nombre_tabla DROP COLUMN nombre_columna; Las opciones con esta sentencia son bastante amplias: podremos modificar columnas, añadirlas o hasta borrarlas. Aquí podemos ver un par de ejemplos prácticos: 100 Acceso a datos public void modificarBaseDatos() throws Exception { Statement stmt = null; try { conn = conector.conector(); System.out.println(“Nos hemos conectado a la BBDD”); stmt = conn.createStatement(); String sql = “ALTER DATABASE ilerna MODIFY NAME = tema2”; stmt.executeUpdate(sql); }catch (Exception e) { System.out.println(“Se ha producido un error.”); } finally { stmt.close(); conn.close(); } } Como vemos, en este ejemplo seguimos la misma es- tructura que en el apartado anterior, solo modificamos la SQL. Esta modificará el nombre de nuestra base de datos a “tema2”. Statement stmt = null; try { //Paso 1: Realizamos la conexión conn = conector.conector(); System.out.println("Nos hemos conectado a la BBDD"); //Paso 2: Preparamos el objeto Statement stmt = conn.createStatement(); //Paso 3:Modificacion de la base de datos, borrar 2 columnas String sql = "ALTER TABLE estudiante DROP COLUMN dni, DROP COLUMN edad;"; stmt.executeUpdate(sql); }catch(SQLException se){ //Gestionamos los posibles errores que puedan surgir durante la ejecu- ción de la inserción se.printStackTrace(); } catch (Exception e) { System.out.println("Se ha producido un error."); } finally { stmt.close(); conn.close(); } En primer lugar, tenemos una sentencia que se encargará de borrar dos columnas. Se puede especificar borrar dos columnas una después de la otra, con una separación de una coma. Para modificar una tabla el procedimiento es el mismo: utilizamos siempre el objeto Statement y ejecuta- mos el método executeUpdate(). 101 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) Podemos sustituir la sentencia SQL por cualquier otra, como podrían ser una de estas dos: sql = "ALTER TABLE estudiante ADD Email varchar(255);"; sql = "ALTER TABLE estudiante COMMENT = 'Comentario de prueba'"; En la primera solo ponemos una descripción a la tabla “es- tudiantes”, y en la segunda ejecutamos la sentencia ALTER TABLE para añadir una nueva columna. Solo son ejemplos de posibles usos de la sentencia ALTER TABLE. Como veis, es bastante fácil y versátil y admite muchas otras opciones. Las posibilidades son bastante amplias, según lo que necesitemos. La estructura en nuestra clase Java debe ser la misma que hemos ido planteando a lo largo de los temas. 2.3.4.3. Modificar tablas o bases de datos El comando DROP es utilizado para borrar bases de datos o tablas dentro de nuestra base de datos. Cuando ejecutamos este comando, todos los datos que contenga la BBDD o la tabla también se borran. Como recordatorio, aquí tenemos la estructura básica para las dos opciones de DROP: //Para borrar una base de datos DROP DATABASE nombre_base_datos; //Para borrar una tabla DROP TABLE nombre_tabla; La estructura es muy simple y adaptarlo a la estructura JDBC será parecido a lo que venimos realizando con los otros comandos. Como podemos apreciar, también usare- mos la interfaz Statement. La sentencia DROP, al no 102 Acceso a datos devolver ningún registro ni información, es ideal para este tipo de interfaz, ya que está especialmente diseñada para operaciones sencillas con la base de datos. Statement stmt = null; try { // Paso 1: Realizamos la conexión conn = conector.conector(); System.out.println("Nos hemos conectado a la BBDD"); // Paso 2. Crear objeto y llamar a la conexión stmt = conn.createStatement(); // Paso 3. Crear estructura de la sentencia String sql = "DROP TABLE estudiante"; // Paso 4. Ejecucion stmt.executeUpdate(sql); } catch (SQLException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { // Paso 5. Cerrar objetos abiertos try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } Para borrar del todo una base de datos, seguiremos el mismo procedimiento. Todas las tablas de esa base de datos se borrarán del todo. No es una sentencia que vaya- mos a usar a menudo, pero está bien conocerla. Statement stmt = null; try { // Paso 1: Realizamos la conexión conn = conector.conector(); System.out.println("Nos hemos conectado a la BBDD"); // Paso 2. Crear objeto y llamar a la conexión stmt = conn.createStatement(); // Paso 3. Crear estructura de la sentencia String sql = "DROP DATABASE ilerna;"; // Paso 4. Ejecucion stmt.executeUpdate(sql); } catch (SQLException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { // Paso 5. Cerrar objetos abiertos try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } 103 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) 2.3.4.4. Renombrar tablas El comando RENAME nos permitirá renombrar una tabla, es decir, modificar el nombre por otro. Este tipo de operación no producirá ningún cambio en los registros que contenga esa tabla, solo se modificará el nombre de la tabla. La es- tructura que deberemos seguir es esta: //Para renombrar una tabla RENAME TABLE nombre_tabla TO Nuevo_nombre_tabla; Como hemos visto en ejemplos anteriores, el procedimien- to es el mismo, solo cambia la sentencia SQL. Connection conn = null; Statement stmt = null; try { // Paso 1: Realizamos la conexion conn = conector.conector(); //Paso 2. Crear objeto y llamar a la conexión stmt = conn.createStatement(); //Paso 3. Crear estructura de la sentencia String sql = "RENAME TABLE ilerna to prueba;"; //Paso 4. Ejecución stmt.executeUpdate(sql); }catch(SQLException se){ se.printStackTrace(); }catch(Exception e){ //Gestionamos los posibles errores e.printStackTrace(); }finally{ //Cerramos la connexion try{ if(stmt!=null) stmt.close(); conn.close(); }catch(SQLException se){ System.out.println("No se ha podido cerrar la conexión."); } } 104 Acceso a datos 2.3.5. Ejecución de sentencias de modificación de datos Entendemos como sentencias de modificación de datos todas aquellas que actualicen o modifiquen de algún modo uno o más registros en una tabla. Este tipo de comando SQL contempla la manipulación de los datos presentes en la base de datos, y serán prácticamente la gran mayoría de los comandos conocidos por SQL: INSERT: añadirá una fila en la tabla de base de datos in- dicada. DELETE: se encarga de borrar información de una tabla de la base de datos. UPDATE: se encarga de modificar los registros de una ta- bla de la base de datos. Estas sentencias nos permitirán borrar datos, insertar nue- vos registros o actualizar uno existente. El procedimiento para crear estas sentencias dentro de nuestra aplicación Java es parecido a los ejemplos anteriores, cambiará la consulta. Vamos a ver algunos ejemplos: 2.3.5.1. Inserción de datos en una tabla Esta es una sentencia que nos permitirá insertar nuevos registros en una tabla de nuestra base de datos. Esta será una de las opciones más usadas. La implementación en nuestra aplicación será de forma muy parecida a los ejem- plos ya explicados con anterioridad. //Para insertar datos a la bbdd INSERT INTO nombre_tabla (nombre_columna, nombre_columna) VALUES (valor, valor); 105 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) Como vemos, para realizar un nuevo registro también te- nemos que crear un objeto Statement que nos servirá para llamar al método createStatement(). La sintaxis del INSERT la crearemos mediante un string, tal y como veníamos ha- ciendo hasta ahora, y se lo pasaremos al método executeUpdate(). Connection conn = null; PreparedStatement stmt = null; try { //Utiliza la clase auxiliar que hemos creado para establecer conexión con bbdd conn = conector.conector(); System.out.println(“Nos hemos conectado a la BBDD”); String sql = “INSERT INTO Estudiante (id, dni, nombre, apellido, edad) VALUES (22, ‘11111111H’, ‘Zara’, ‘Ali’, 18)”; stmt.executeUpdate(sql); }catch(SQLException se){ se.printStackTrace(); }catch(Exception e){ e.printStackTrace(); }finally{ try{ if(stmt!=null) stmt.close(); conn.close(); }catch(SQLException se){ System.out.println(“No se ha podido cerrar la conexión.”); } } 106 Acceso a datos Existe otra posibilidad de inserción de datos, con datos dinámicos de nuestra aplicación. Las aplicaciones suelen interactuar con los usuarios que las utilizan, por tanto, muchos de los datos serán cambiantes y esa información nunca será del todo fija. Por ello, existe la posibilidad de realizar sentencias SQL dinámicas. La estructura será muy parecida a lo que es- tábamos usando hasta ahora, pero en lugar del objeto Statement usaremos el objeto PreparedStatement. Este tipo de objeto está especialmente diseñado para poder realizar operaciones con sentencias dinámicas. Connection conn = null; PreparedStatement stmt = null; try { //Utiliza la clase auxiliar que hemos creado para establecer conexión con bbdd conn = conector.conector(); System.out.println(“Nos hemos conectado a la BBDD”); String sql = “INSERT INTO Estudiantes (id, dni, nombre, apellido, edad) VALUES (?, ?, ?, ?, ?)”; //Imaginemos que viene con datos Estudiante estudiante = new Estudiante(); //Prepararemos la query para que coja los datos de manera dinamica. stmt = conn.prepareStatement(sql); stmt.setInt(1, estudiante.getId()); stmt.setString(2, estudiante.getDni()); stmt.setString(3, estudiante.getNombre()); stmt.setString(4, estudiante.getApellido()); stmt.setInt(5, estudiante.getEdad()); stmt.executeUpdate(sql); }catch(SQLException se){ se.printStackTrace(); }catch(Exception e){ e.printStackTrace(); }finally{ try{ if(stmt!=null) stmt.close(); conn.close(); }catch(SQLException se){ System.out.println(“No se ha podido cerrar la conexión.”); } } Normalmente, cuando se llama al método que contiene esta parte del código, se le pasará un objeto “Estudiante” con los datos del usuario que nos servirá para ir seteando la sentencia. 107 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) 2.3.5.2. Actualización de datos de una tabla La sentencia UPDATE es conocida por permitir actualizar valores de una tabla concreta de la base de datos. Recorda- mos cómo se realiza un UPDATE: //Para actualizar datos a la bbdd UPDATE nombre_tabla SET nombre_columna =valor, nombre_columna =valor2 WHERE nombre_columna =valor; Este tipo de sentencia se puede ejecutar del mismo modo que en el ejemplo del INSERT. Tenemos dos posibilidades: con una sentencia ya preescrita en nuestro código o con datos dinámicos. Así se implementaría: Connection conn = null; Statement stmt = null; try { // Paso 1: Realizamos la conexión //Paso 2. Crear objeto y llamar a la conexión conn = conector.conector(); System.out.println(“Nos hemos conectado a la BBDD”); //Paso 3. Crear estructura de la sentencia String sql = “UPDATE estudiante SET dni = ‘00000000T’ WHERE id = ‘12’”; //Paso 4. Ejecución stmt = conn.createStatement(); }catch(SQLException se){ //Gestionamos los posibles errores que puedan surgir durante la ejecucion de la insercion se.printStackTrace(); }catch(Exception e){ //Gestionamos los posibles errores e.printStackTrace(); }finally{ //Paso 5. Cerrar objetos abiertos try{ if(stmt!=null) stmt.close(); conn.close(); }catch(SQLException se){ System.out.println(“No se ha podido cerrar la conexión.”); } } Para un UPDATE con sentencia fija, usaremos el objeto Sta- tement, ya que es una operación sencilla. El procedimiento es exactamente igual que con otras sentencias: deberemos meter todo el código entre el try-catch para poder contro- lar los errores que puedan surgir. Para un UPDATE con datos dinámicos usaremos un objeto PreparedStatement, que nos va a permitir añadir dinámicamente los valores que nos interesen a nuestra sentencia. 108 Acceso a datos Connection conn = null; PreparedStatement stmt = null; try { // Paso 1: Realizamos la conexion conn = conector.conector(); System.out.println(“Nos hemos conectado a la BBDD”); //Paso 2. Crear estructura de la sentencia, ponemos interrogantes a los datos que serán dinamicos String sql = “UPDATE estudiante SET dni = ? WHERE id = ?”; //Estos datos en una aplicación iran variando, ahora los seteamos para que contenga valor Estudiante estudiante = new Estudiante(); estudiante.setId(1); estudiante.setDni(“00000000T”); //Prepararemos la query para que coja los datos de manera dinamica. stmt = conn.prepareStatement(sql); // Paso 4. Asignamos los valores del objeto que queramos guardar stmt.setString(1, estudiante.getDni()); stmt.setInt(2, estudiante.getId()); //Paso 5. Ejecución stmt.execute(); }catch(SQLException se){ //Gestionamos los posibles errores que puedan surgir durante la ejecucion de la insercion se.printStackTrace(); }catch(Exception e){ //Gestionamos los posibles errores e.printStackTrace(); }finally{ //Cerramos la connexion try{ if(stmt!=null) stmt.close(); conn.close(); }catch(SQLException se){ System.out.println(“No se ha podido cerrar la conexión.”); } } 109 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) 2.3.5.3. Eliminación de datos de una tabla La sentencia DELETE nos servirá para eliminar registros de nuestra base de datos, ya sea porque no queremos que estén o porque ya no los necesitemos. La sentencia se rea- lizaría siguiendo esta estructura: //Para borrar datos a la bbdd DELETE FROM nombre_tabla WHERE nombre_columna =valor; Cuando realizamos un DELETE, siempre tenemos que poner un WHERE para poder filtrar mejor los datos que queremos borrar de la tabla, si no es así, se borrarán todos los registros. También tenemos la posibilidad de realizar la eliminación de los dos modos explicados anteriormente. Aquí vemos dos ejemplos: Connection conn = null; Statement stmt = null; try { conn = conector.conector(); System.out.println("Nos hemos conectado a la BBDD"); stmt = conn.createStatement(); String sql = "DELETE FROM estudiante WHERE dni = '00000000T'"; stmt.executeUpdate(sql); } catch (SQLException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } 110 Acceso a datos Al ejecutar la sentencia, si no borra ningún registro porque no encuentra el DNI no se producirá ningún error, simple- mente no borrará ningún registro. En este caso, también debemos englobar todo el código entre un try-catch para poder controlar los errores que se puedan generar. PreparedStatement sentencia = null; try { // Paso 1. Previamente habremos realizado la conexion conn = conector.conector(); System.out.println("Nos hemos conectado a la BBDD"); // Paso 2. Crear estructura de la sentencia, ponemos interrogantes a los datos que serán dinámicos String consulta = "DELETE FROM estudiante WHERE dni =?" // Paso 3. Pasamos la consulta al objeto PreparedStatement sentencia = conn.prepareStatement(consulta); // Paso 4. Asignamos los valores del objeto que queramos guardar // Imaginemos que el objeto estudiante esta ya declarado y con da- tos. Estudiante estudiante = new Estudiante(); sentencia.setString(1, estudiante.getDni()); // Paso 5. Ejecucion sentencia.execute(); } catch (SQLException e) { System.out.println(e.getCause()); }catch (Exception e) { System.out.println(e.getCause()); } finally { // Paso 6. cerramos la conexión y el objeto en uso try { sentencia.close(); } catch (SQLException e) { e.printStackTrace(); } } Este ejemplo es una implementación de un DELETE con una sentencia dinámica. El objetivo es borrar los registros de la tabla “Estudiante” que tenga como DNI el valor que con- tenga el estudiante.getDni(). El valor será dinámico porque cada objeto “Estudiante” tendrá un valor distinto. Podréis ver cómo realizar este tipo de consultas en los ejer- cicios de GitLab. 111 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) 2.3.6. Ejecución de consultas Una de las opciones que tenemos para interactuar con las bases de datos son las consultas, también conocidas como SELECT. Es una de las sentencias más utilizadas, ya que nos permitirá seleccionar datos según los criterios que le indiquemos. La estructura básica de una consulta debería ser así: //Para borrar datos a la bbdd SELECT * FROM nombre_tabla WHERE nombre_columna =valor; Desde JDBC tenemos múltiples opciones para poder reali- zar consultas, tanto una consulta ya preestablecida en el código como una consulta dinámica que irá cambiando a medida que cambien los datos que le indiquemos. Connection conn = null; Statement stmt = null; try { conn = conector.conector(); System.out.println("Nos hemos conectado a la BBDD"); String sql = "SELECT * FROM Estudiante"; stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql); while (rs.next()) { int id = rs.getInt("id"); String nombre = rs.getString("nombre"); String apellido = rs.getString("apellido"); String dni = rs.getString("dni"); System.out.print("ID: " + id); System.out.print(", Nombre: " + nombre); System.out.print(", Apellido: " + apellido); System.out.println(", DNI: " + dni); } rs.close(); } catch (SQLException se) { se.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (stmt != null) stmt.close(); } catch (SQLException se) { System.out.println("No se ha podido cerrar la conexion"); } } El procedimiento de implementación es bastante sencillo y se asemeja al que ya hemos visto con anterioridad. 112 Acceso a datos Como podemos ver, hay dos opciones para realizar con- sultas. La primera, simplemente creando una selección genérica de los registros de una tabla. Como en ejemplos anteriores, el primer paso será realizar la conexión con nuestra clase auxiliar para realizar la conexión con la base de datos. Seguidamente, crearemos un string con la sentencia de consulta que queramos realizar. En este caso, seleccio- naremos todos los registros de la tabla “Estudiante”. Para realizar esta acción, llamaremos al método de la conexión createStatement(), que devolverá un objeto Statement que usaremos más adelante. En este ejemplo, introduciremos la interfaz ResultSet, que se utiliza para almacenar las filas seleccionadas de una tabla y nos va a permitir recorrer la lista y buscar las diferentes columnas de cada registro. A continuación, para ejecutar la consulta, tendremos que pasarle la SQL al método executeQuery(). Una vez realiza- do, con el método next() de la interfaz ResultSet podremos recorrer los resultados. El ejercicio se encarga de buscar cada columna e imprimirlo en consola. Para finalizar, tendremos que tener en cuenta que el código debe englobarse en un try-catch para controlar los posibles errores. Además, tendremos que recordar cerrar todos los objetos usados. La segunda opción de implementación de una consul- ta consiste en realizar consultas dinámicas con datos variables. Como hemos visto con anterioridad, podemos realizar dicha consulta con la interfaz PreparedStatement y seteando posteriormente el dato que nos interese. En este caso, usamos un PreparedStatement porque vamos a reali- zar una consulta dinámica, pero guardaremos el resultado en un ResultSet. El objeto ResultSet tiene un método que 113 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) nos permitirá recorrer los resultados uno a uno y tratar los datos obtenidos: ese método es next(), el cual ya lo hemos visto en el ejemplo anterior. Como veremos en el ejemplo, podremos acceder a los datos y tratarlos de la manera que deseemos. Connection conexion = null; PreparedStatement sentencia = null; Estudiante estudiante = new Estudiante(); try { conexion = conector.conector(); System.out.println("Nos hemos conectado a la BBDD"); String consulta = "select * from estudiante where nombre = ? "; sentencia = conexion.prepareStatement(consulta); sentencia.setString(1, estudiante.getNombre()); ResultSet rs = sentencia.executeQuery(); while (rs.next()) { int id = rs.getInt("id"); int dni = rs.getInt("dni"); String nombre = rs.getString("nombre"); System.out.print("ID: " + id); System.out.print(", DNI: " + dni); System.out.print(", NOMBRE: " + nombre); } rs.close(); sentencia.close(); } catch (SQLException se) { se.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { conexion.close(); } 2.3.7. Ejecución de procedimientos almacenados en la base de datos En este apartado, os enseñaremos a ejecutar un procedi- miento desde una aplicación Java con JDBC. CONCEPTO Un procedimiento es un subproceso o un subprograma que podemos crear en nuestra base de datos para que se ejecute cuando el usuario haga una llamada a este procedimien- to. Es conocido también como procedure. 114 Acceso a datos Un procedure tiene un nombre, una lista de parámetros y diferentes operaciones SQL. Este tipo de procedimiento solo es aceptado en bases de datos de tipo relacional. Un procedimiento tiene que seguir esta estructura: DELIMITER $$ DROP PROCEDURE IF EXISTS `nombre_bbbdd`.`nombre_procedure` $$ CREATE PROCEDURE `nombre_bbbdd`.`nombre_procedimiento` (parámetros) BEGIN Seccion_declaracion Parte_execucion_operaciones_SQL END $$; DELIMITER ; El procedimiento para este ejercicio quedará más o menos así: DELIMITER $$ DROP PROCEDURE IF EXISTS `ILERNA`.`ObtenerDatosEstudiante` $$ CREATE PROCEDURE `ILERNA`.`ObtenerDatosEstudiante` (IN ES_ID INT, OUT ES_DNI VARCHAR(255)) BEGIN SELECT DNI INTO ES_DNI FROM ESTUDIANTE WHERE ID = ES_ID; END $$ DELIMITER ; Crear un procedure se parece un poco a crear un método: tenemos que darle un nombre y entre paréntesis hay pa- rámetros. A nuestro procedimiento le daremos el nombre “ObtenerDatosEstudiante” e irá envuelto entre los símbolos de acento, y delante situaremos también entre acentos el nombre de la BBDD en la que queremos guardarlo junto con un punto. A continuación, definiremos los parámetros. Cada pará- metro irá separado por una coma y se diferencia entre dos tipos: uno IN y otro OUT. El IN significará que ese paráme- tro se usará dentro del procedimiento y el OUT será el valor que devolverá la función y que luego recogeremos. El primer parámetro es ES_ID acompañado de IN, eso sig- nifica que se usará dentro del procedure, y luego indica el tipo, que será un int. Con esta información se nos indica que ese ES_ID será el identificador único de la fila de ese registro y equivaldrá al identificador del objeto “Estudiante”. 115 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) Connection conn = null; Statement stmt = null; try { // Paso 1: Realizamos la conexion conn = conector.conector(); System.out.println("Nos hemos conectado a la BBDD"); //Paso2: definimos el procedimiento String procedure = "DELIMITER $$" + "DROP PROCEDURE IF EXISTS ILERNA`.`ObtenerDatosEstudiante` $$" + "CREATE PROCEDURE `ILERNA`.`ObtenerDatosEstudiante` " + " (IN ES_ID INT, OUT ES_DNI VARCHAR(255))" + "BEGIN" + " SELECT DNI INTO ES_DNI" + " FROM ESTUDIANTE" + " WHERE ID = ES_ID;" + "END $$" + "DELIMITER ;"; // Paso 3: Creamos el objeto Statement de la conexion stmt = conn.createStatement(); //Paso 4: ejecutamos la sql stmt.execute(procedure); } catch (SQLException ex) { System.out.println("Se ha producido un error en la ejecucion de la SQL: " + ex.getMessage()); } finally { try { stmt.close(); } catch (SQLException ex) { System.out.println("Se ha producido un error al cerrar la conexion: " + ex.getMessage()); } } Para crear este procedimiento desde nuestra aplicación, seguiremos los pasos de ejercicios anteriores. Los pasos son los mismos, solo cambiará la SQL, como vemos en el ejemplo de arriba. Una vez creado, ya podemos usarlo en nuestra aplicación. En primer lugar, tendremos que tener creado un proce- dimiento en nuestra base de datos para que podamos llamarlo desde nuestra aplicación. En el ejemplo, tenemos una sentencia sencilla de un procedimiento para nuestro objeto “Estudiante” que nos devolverá información alma- cenada en esa tabla. 116 Acceso a datos Connection conn = null; CallableStatement llamadaProcedure =null; Estudiante estudiante = new Estudiante(); try { // Paso 1: Realizamos la conexion conn = conector.conector(); System.out.println("Nos hemos conectado a la BBDD"); String sql = "{call ObtenerDatosEstudiante (?,?)}"; // Paso 2: Llamada al procedimiento almacenado, tiene que existir en la BBDD llamadaProcedure = conn.prepareCall(sql); //Paso 3: Definimos el parametro OUT llamadaProcedure.registerOutParameter(2, Types.VARCHAR); //Paso 4: definimos el parametro IN llamadaProcedure.setInt(1, estudiante.getId()); //Paso 5: Ejecuta el procedimiento almacenado boolean resultado = llamadaProcedure.execute(); if(resultado) { ResultSet lista = llamadaProcedure.getResultSet(); while (lista.next()) { //Obtenemos el DNI String dni = lista.getString("dni"); System.out.println(dni); } } } catch (SQLException ex) { System.out.println("Se ha producido un error en la ejecucion de la SQL: " + ex.getMessage()); } Para llamar a un procedimiento, usaremos esta sentencia: call nombreProcedimiento (?,?); Los interrogantes equivaldrán a cada parámetro de la fun- ción. Si hay más de dos, se pondrán más. En este ejemplo, usamos un CallStatement, que es una interfaz preparada para realizar este tipo de operaciones. Rellenaremos este nuevo objeto con la llamada al método prepareCall(), pasándole la SQL creada con la llamada al procedimiento. Tras esto, deberemos pasarle al procedi- miento los valores de los parámetros, para lo que usamos el método registerOutParameter() para los parámetros OUT. En este caso, es nuestro segundo parámetro, por eso indicamos un 2 y el tipo varchar. Para definir el valor del parámetro INT, haremos un setInt() indicando que es el parámetro 1 y el valor del ID del estudiante. A continua- 117 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) ción, tendremos que ejecutar la sentencia con el método execute(). Si devuelve true, guardaremos los resultados en un ResultSet para poder obtener los registros. Siempre que exista un resultado, recorreremos la lista, guardaremos el valor en un string e imprimiremos el valor por consola. Es muy importante que, para que este ejercicio funcione, tengamos creado nuestro procedimiento en la base de datos. No es necesario realizarlo mediante la aplicación, se puede crear directamente en la base de datos. 2.3.8. Gestión de transacciones CONCEPTO Las transacciones son las acciones que nos permiten controlar cuándo y cómo se aplican los cambios en nuestra base de datos Las transacciones se encargan de tratar una o más senten- cias SQL, y si una de ellas falla, toda la transacción falla. En JDBC, las transacciones se realizan de manera automáti- ca por defecto, pero existe la posibilidad de ejecutarlas de manera manual. Esto nos puede aportar una mejora del rendimiento de la aplicación, manteniendo la integridad del proceso de negocio. Para habilitar las transacciones de ma- nera manual, el objeto Connect tiene un método llamado setAutoCommit(), al cual tendremos que asignarle el valor false para poder establecer las transacciones manuales. Commit: se guardan los datos DATOS Transacción Rollback: no se guardan los datos Ilustración 3. Esquema del funcionamiento de las transacciones. Existen tres métodos del objeto Connect que nos serán muy útiles a la hora de realizar transacciones: setAutoCommit(boolean): permitirá modificar la tran- sacción automática. commit(): efectuará la transacción. Sin la ejecución de este método, no se guardarán los datos en nuestra base de datos. rollback(): es el método que cancela la transacción, nos será útil cuando se produzca alguna excepción. 118 Acceso a datos Para ver cómo se ejecutan, aquí podemos ver un ejemplo: Savepoint savepoint1 = null; try{ // Paso 1. Previamente habremos realizado la conexion //Paso 2. Creamos un nuevo objeto Statement y estableemos la tran- saccion manual conn.setAutoCommit(false); Statement stmt = conn.createStatement(); //Paso 3. Creamos un nuevo Savepoint savepoint1 = conn.setSavepoint("punto de backup"); String consulta = "INSERT INTO Estudiante (id, dni, nombre, ape- llido, edad) VALUES (22, '11111111H', 'Zara', 'Ali', 18)"; // Paso 4. Ejecucion stmt.executeUpdate(consulta); //Paso 5. Prueba de insert mal hecho consulta = "INSERTED IN Estudiante VALUES (107, 22, 'Juan', 'ejem- plo')"; stmt.executeUpdate(consulta); // Paso 6. Haremos commit siempre que no se produzca error. conn.commit(); }catch(SQLException se){ conn.rollback(savepoint1); } 119 Tema 2: Gestión de conectores (desarrollo de aplicaciones que gestionan información en bases de datos relacionales) Para realizar transacciones manuales, debemos establecer el autoCommit a false. De esta manera, tendremos control sobre qué se persiste en base de datos y qué no. Si eje- cutamos una consulta sin que devuelva error, podremos seguir con el programa. En cambio, si se produce un error, el commit() no se realizará. En el ejemplo, ninguno de los INSERTS se va a guardar, porque la segunda sentencia está mal construida. Para evitar perder datos al producirse una excepción, existe la posibilidad de realizar guardados en medio de diferentes sentencias, se los llama Savepoints. Savepoint savepoint1 = null; try{ // Paso 1. Previamente habremos realizado la conexión //Paso 2. Creamos un nuevo objeto Statement y estableemos la tran- saccion manual conn.setAutoCommit(false); Statement stmt = conn.createStatement();