lunes, 3 de julio de 2017

Tutorial: comunicación entre Java y una base de datos H2 (con ejemplos): Parte 1 - JDBC

La problemática de comunicación entre las aplicaciones Java y las bases de datos ha sido actual desde el primer año de la existencia del lenguaje Java. Hoy vamos a mirar, cómo con años ha evolucionado el método de esta comunicación haciendo una aplicación "enterprise" simple.

Principio
Tenemos una empresa que tiene unos empleadores (tabla Employee). Cadauno tiene su dirección (tabla Address). Y cadauno tiene sus proyectos en cuyos está trabajando (tabla Projects). Visto que cada proyecto puede tener varios empleadores y cada empleador puede trabajar en varios proyectos - tenemos una relación del tipo ManyToMany y eso lógicamente nos hace crear una tabla intermedia - una tabla con asociaciones entre empleadores y proyectos - la llamamos EmplProj:

EmployeeID ProjectID
1                    1
1                    2
2                    2
3                    3
4                    4

Se ve que el empleador #1 trabaja con los proyectos #1 y #2, al mismo tiempo con el proyecto #2 están trabajando el empleador #1 y el empleador #2.
El esquema relacional de nuestra base de datos:





Como la base de datos utilizamos una BD "incorporada" que se llama H2.

Configuración de la BD:

Para que funcione la conexión, no olvides de importar el JAR del driver sqljdbc4.jar en tu proyecto. En Eclipse:
- selecciona tu proyecto con el botón derecho del ratón
- selecciona "Propriedades" (lo de mas abajo)
- en la ventanilla vas a "Java Build Path", Libraries, "Add External Jar"
- selecciona tu file y dale a "Apply"

Empezamos por JDBC - mas simple.

Creation del proyecto en Eclipse
Voy a escribir en pseudocódigo, un código "esquemático" (que no es para nada completo: no contiene ni declaraciones, ni excepciones, ni "if", etc), solo para que se entiende mejor la lógica. El código original (y correcto) podeis bajar desde aqui: https://github.com/cyberglad/JdbcTutorial

En Eclipse dale a "File->New->Other" y "Maven->Maven Project". Selecciona la cajita "Create simple project (skip archetype selection)", en GroupID pon "com.example" y "ArtifactID" -"JdbcTutorial", el resto deja como es, clica "ok". Asi te crea une estructura bonita com carpetas "src/main" para el código y "src/test" para las pruebas:




Creamos nuestra utilidad de conexión JDBC que llamaremos Util.java . Clicamos con el botón derecho en la carpeta "src/main/java" y creamos un paquete "bl" ("business lógica"). Ahí creamos una nueva clase Util.java.


package bl;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
/**
 * Created by glady on 30.06.2017.
 */
public class Util
{
    // JADE PROD
    private     String URL = "jdbc:sqlserver://test;instanceName=JCAPS;database=TEST"; // ;user=xxx;password=xxx";
    private     String USERNAME = "test";
    private     String PASSWORD = "test";
    private     String DRIVER = "com.microsoft.sqlserver.jdbc.SQLServerDriver";

    public  Connection getConnection() {
        Connection connection = null;
        try {
            Class.forName(DRIVER);
            connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            System.out.println("Connection ok");
        } catch (Exception e) {
            System.out.println("Error: " + e.getMessage());
        }
        return connection;
    }
}
Vamos a crear directamente un test para probar, si funciona bien nuestra conexión. En la carpeta "src/test/java" creamos un paquete "service" ("botón derecho->"New"->"Package") y dentro una nueva clase "ConnectionTest".

Copia y pega el código siguiente:
package service;

import java.sql.SQLException;
import org.junit.Test;
import bl.Util;

public class ConnectionTest {
 @Test
        public void test() throws SQLException {
  new Util().getConnection();
 }
}
Ahora lanzaremos nuestro test dando el botón derecho en la clase ConnectionTest y "Run as"->"JUnit Test". SO todo va bien, veremos una ventanita JUnit con una raya verde y otra ventanita Console que dice "Connection ok":


Ahora en la carpeta "src/main/java", donde ya tenemos el paquete "bl" con nuestra clase Util.java creamos un nuevo paquete "service" y una clase AddressService.java:


Nos sirve para comunicar con la clase Util.java para ejecutar las operaciones en la base de datos: insertar información, actualizar información y eliminar información.

Es esquema es muy simple:
El principio es esto. Para cada operación en la BD tenemos que:
- obtenir la conexion desde Util.java
- abrirla
- ejecutar la operación necesaria
Como tenemos 4 operaciones, nos toca escribir 4 veces lo mismo, cambiando la consulta SQL.

DAO
Vamos a hacerlo bien y añadimos a esta cadena otra cosa bonita (y programáticamente correcta) que se llama DAO: una interfaz Java para definir las operaciones genéricas que hace el servicio que hemos definido.

El código es trivial:
public interface AddressDAO {
    void add(Address address) throws SQLException;
    List
getAll() throws SQLException; Address getById(Long id) throws SQLException; void update(Address address) throws SQLException; void remove(Address address) throws SQLException; }
Para que necesitamos eso? En verdad no hace nada, solo nos deja visualizar mejor que operaciones esta haciendo cada servicio. Y entonces nuestra clase de Servicio va extender esta interfaz implementando los métodos "create/insert/update/delete/":

public class AddressService implements AddressDAO
{
  public void insert(Address address)
  {
    //la consulta SQL
    String sql = "INSERT INTO table .....";
    //preparamos la connexion
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    //insertamos los parámetros
    preparedStatement.setParam("ID", ...)
    preparedStatement.setParam("STREET", ...)
    //...//
    //executamos
    preparedStatement.executeUpdate();
  }
  public List select()
  {
    //creamos la consulta
    String sql = "SELECT .... FROM table";
    ResultSet resultSet = statement.executeQuery(sql);
    while (resultSet.next()) {
      //ponemos cada linea de datos obtenida de la tabla en un objeto
      Address address = new Address();
      address.setId(resultSet.getLong("ID"));
      address.setCountry(resultSet.getString("COUNTRY"));
      //....//
      //ponemos este objeto en una lista
      addressList.add(address);
    }
    //devolvemos la lista
    return addressList
  }
  public void delete(Address address)
  {
    String sql = "DELETE FROM TABLE WHERE ID = ?";
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    preparedStatement.setParam("ID", ...)
    preparedStatement.executeUpdate();
  }
  public void update(Address address)
  {
    String sql = "UPDATE table set Street = ? WHERE ID = ?";
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    preparedStatement.setLong("ID", ...)
    preparedStatement.setString("STREET",...)
    preparedStatement.executeUpdate();
  }
}

Necesitaremos 4 interfaces DAOs (para Employee, Address, Project y EmplProj) y 4 clases-servicios que las implementan.

Unidades
Ahora toca crear una clase para cada unidad (que corresponde a cada tabla) donde las propriedades representan las columnas de la tabla. Así todo es bonito y las capas de lógica de unidades esta separada de la capa de la persistencia (como tiene que ser):
public class Address {

    private Long id;
    private String country;
    private String city;
    private String street;
    private String postCode;
    //aquí vienen los getters y setters para cada propriedad
}



Nota, que hemos hecho desde el inicio la separación de la lógica en capas (Entity, Service), porque asi podemos luego solo cambiar la tecnología que utilizaremos para comunicar con la BD - la estructura queda la misma. La próxima vez miraremos el mismo proyecto, pero utilizamos Hibernate.

No hay comentarios:

Publicar un comentario