lunes, 15 de abril de 2013

JAVA & MySQL: Servicios web con acceso a datos persistentes


La mayoría de las veces que hacemos una invocación a un servicio suele ser para trabajar con una base de datos. Son pocas las veces en las que el servicio web es una mera función publicada que no necesita acceso al backend para consultar datos.
En el ejemplo de hoy, crearemos una base de daros con MySQL, con una tabla, y expondremos un servicio web que realizará las operaciones de consultar, borrar e insertar registros en esa base de datos.
MySQL
MySQL es la más que conocida base de datos open source más conocida y usada. Nos la podemos descargar de aquí: http://dev.mysql.com/downloads/.

Lo bueno que tiene las últimas versiones (solo lo he probado para Windows) es que te instala no solo el motor de la base de datos, sino un montón de herramientas y utilidades que nos serán de gran ayuda a la hora de trabajar con la base de datos, como su Workbench (gestor).

Montando nuestra base de datos
Una vez instalado, abrimos el workbench y crearemos un nuevo Schema.



Indicamos el nombre y pulsamos sobre Apply.



Esto nos abrirá un asistente que nos mostrará la query SQL para la creación del Schema:


Aplicamos y base de datos creada.




A continuación crearemos nuestra tabla, que será de lo más simple:



Llamaremos a la tabla DemoTable, y esta tendrá dos columnas, ID (autonumerico, PK y not null) y TEXTO (varchar de 45 y not null).



De nuevo, al pulsar sobre apply nos mostrará la query a ejecutar, y al aceptar nos la creará.

CREATE  TABLE `demo`.`DemoTable` (
  `ID` INT NOT NULL AUTO_INCREMENT ,
  `TEXTO` VARCHAR(45) NOT NULL ,
  PRIMARY KEY (`ID`) );

A continuación insertaremos un registro para ver que todo va bien:

INSERT INTO demo.DemoTable (TEXTO) VALUES ('PRUEBA');

Y consultamos si se ha ingresado:

SELECT * FROM demo.DemoTable;


Con esto, ya solo nos queda crear el servicio web.

Creación del servicio web con NetBeans
Abrimos nuestro NetBeans 7.x y creamos el proyecto web y un servicio web por defecto, tal y como aprendimos en el post JAVA: Crear un servicio web básico con NetBeans 7.x, pero con un detalle importante: cuando estemos creando el proyecto y nos pregunte por los frameworks que queremos añadir, seleccionaremos Hibernate.




Una vez tenemos esto, lo principal que tenemos que hacer es crearnos nuestro archivo de persistencia (Persistence Unit).



Si no nos sale directamente, pulsamos sobre Others y lo encontraremos en Persistence > Persitence Unit.



Dejaremos los datos por defecto (recordad bien el nombre que le ponemos a la unidad!!!) y crearemos un data source para nuestro MySQL.



A continuación, indicaremos el nombre del JNDI (MySQL_Pruebas) y en Database connection indicaremos uno nuevo.



Esto nos abrirá un nuevo asistente, donde seleccionaremos el driver MySQL


Pulsaremos siguiente y configuraremos nuestra conexión, que si es a local son los que vienen por defecto, con la consideración de indicar la base de datos (demo) y la password que indicamos en la instalación de MySQL (si queremos usar otro usuario, también nos vale).

Aceptamos todo y ya tendremos nuestra conexión creada y el archivo de persistencia listo.



Este archivo, que nos ha creado en la carpeta Condiguration Files, debemos copiarlo a WEB-INF:



Ahora, crearemos una Entity Class from Database (si no nos sale en el menú contextual, de nuevo debemos ir a Others > Persitence > Entity Class from Database). 



En el asistente, que por defecto nos muestra el data source que tenemos configurado en nuestra Persistence Unit, veremos las tablas que tenemos creadas. Seleccionamos la que queremos y tras indicar el nombre del paquete (com.demobdyws.persistence) nos creará la clase.







A continuación deberemos crear una clase que controle la tabla, y esta será una clase JPA Controller. De nuevo pulsamos sobre New > Other > Persistence > JPA Controller from Entity Classes.




Seleccionamos nuestra Entity Class



E indicamos el paquete (com.demobdyws.persistence.controller) y finalizamos.



 Esto nos crea un montón de clases, pero a nosotros solo nos interesa la DemotableJpaController.

A continuación creamos nuestro servicio web DemoBDWS en el paquete com.demobdyws.webservice



A continuación, en este servicio añadiremos un método (getCount) que nos devolverá un String indicando el número de resultados que tenemos en la tabla.

@WebMethod(operationName = "getCount")
public String getCount() {...}


Lo primero que tenemos que hacer es crear una instancia del controlador JPA al que llamaremos controller. Entre sus parámetros de entrada, le pasaremos un EntityManager que crearemos en base a nuestra unidad de persistencia (por defecto habrá sido nombrada como demoBDyWSPU) a partir de la función createEntityManagerFactory de la clase Persistence.

    DemotableJpaController controller =
            new DemotableJpaController(null,
            Persistence.createEntityManagerFactory("demoBDyWSPU"));


Una vez creado nuestro controlador, este dispone de métodos para crear, obtener registros, etc. Nosotros usaremos el find<Entidad>Entities() que nos devuelve todos los registros en una lista, de la cual sacaremos el número de registros en la tabla.

    List<Demotable> lista =
            controller.findDemotableEntities();

El código será el siguiente:

@WebMethod(operationName = "getCount")
public String getCount() {
    String res = "resultado: ";

    //creamos el controlador
    DemotableJpaController controller =
            new DemotableJpaController(null,
            Persistence.createEntityManagerFactory("demoBDyWSPU"));

    //obtenemos todos los registros
    List<Demotable> lista =
            controller.findDemotableEntities();

    //mostramos el número de registros
    res += lista.size();
    return res;
}

Con esto ya podemos probar el servicio de consulta.

Crear registros desde el servicio web
Para la inserción, nuestro JPA Controller dispone de un método create.

public void create(Demotable demotable) throws RollbackFailureException, Exception {
    EntityManager em = null;
    try {
        utx.begin();
        em = getEntityManager();
        em.persist(demotable);
        utx.commit();
    } catch (Exception ex) {
        try {
            utx.rollback();
        } catch (Exception re) {
            throw new RollbackFailureException("An error occurred attempting to roll back the transaction.", re);
        }
        throw ex;
    } finally {
        if (em != null) {
            em.close();
        }
    }
}

Este método utiliza la transacción utx que recibe en el constructor (de tipo UserTransaction), pero nosotros, como le estamos pasando dicha transacción a NULL cambiaremos el código para simplificarlo.

En este caso, la transacción la sacaremos del EntityManager (em) y su función getTransaction(), tanto para el commit como para el begin, por lo que nuestro método create quedará así:

public void create(Demotable demotable) throws RollbackFailureException, Exception {
    EntityManager em = null;
    try {
        //utx.begin(); //comentado para controlar con el EM la trans
        em = getEntityManager();
        em.getTransaction().begin(); //iniciamos la transaccion
        em.persist(demotable);
        //utx.commit(); //comentado para controlar con el EM la trans
        em.getTransaction().commit();//hacmeos commit de la transaccion
    } catch (Exception ex) {
        try {
            utx.rollback();
        } catch (Exception re) {
            throw new RollbackFailureException("An error occurred attempting to roll back the transaction.", re);
        }
        throw ex;
    } finally {
        if (em != null) {
            em.close();
        }
    }
}

Lo mismo aplicará para el resto de métodos del JPA Controller (destroy, edit…), de forma que incluso podremos eliminar el recibir el objeto de tipo UserTransaction en el constructor.

Para probar la creación, añadiremos a nuestro servicio web el siguiente método:

@WebMethod(operationName = "insertar")
public String insertar(@WebParam(name = "texto") String texto) throws RollbackFailureException {
    String res = "resultado: ";

    //creamos el controlador
    DemotableJpaController controller =
            new DemotableJpaController(null,
            Persistence.createEntityManagerFactory("demoBDyWSPU"));

    //creamos el objeto
    Demotable newReg = new Demotable(0, texto);

    //invocamos al create del controlador
    try {
        controller.create(newReg);
        res += newReg.getId().toString();
    } catch (Exception ex) {
        res = "Excepcion: " + ex.toString();
    }


    return res;
}

¡Y listo! Os pongo un ejemplo de invocación desde el SOAPUI y de su resultado:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://webservice.demobdyws.com/">
   <soapenv:Header/>
   <soapenv:Body>
      <web:insertar>
         <texto>prueba ws</texto>
      </web:insertar>
   </soapenv:Body>
</soapenv:Envelope>

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <ns2:insertarResponse xmlns:ns2="http://webservice.demobdyws.com/">
         <return>resultado: 2</return>
      </ns2:insertarResponse>
   </S:Body>
</S:Envelope>


El código JAVA lo tenéis disponible aquí.