Operaciones sobre la base de datos

Transacciones

Todas las operaciones de modificación de los datos contenidos en una base de datos deben realizarse una vez que se inicie una transacción que se debe obtener a partir del objeto EntityManager que se haya obtenido tras la conexión con dicha base de datos.

Suponiendo que se ha almacenado el objeto EntityManager en una variable llamada em:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("AgendaContactosPU");
EntityManager em = emf.createEntityManager();

Se puede obtener iniciar una transacción sobre dicho EntityManager con una sentencia como esta:

em.getTransaction().begin();

Tras iniciar la transacción se podrá realizar cualquier número de operaciones de modificación de los datos contenidos en la base de datos, como inserciones, modificaciones y borrado.

Una vez realizadas las operaciones deseadas, se deben confirmar realizando su volcado definitivo a la base de datos con una orden commit usando la misma transacción que se inició anteriormente:

em.getTransaction().commit();

En cambio, si tras iniciar una serie de operaciones de modificación de los datos contenidos en la base de datos, se desea cancelar dichos cambios, se puede indicar la orden rollback que cancelará todas las operaciones pendientes de volcar a la base de datos. Ten en cuenta que esta acción sólo tendrá efecto si no se ha realizado un commit anteriormente sobre los datos pendientes de volcar a la base de datos.

em.getTransaction().rollback();

Por tanto, la secuencia habitual para realizar operaciones de modificación de los datos será parecida a la siguiente estructura:

em.getTransaction().begin();
// Añadir aquí las operaciones de modificación de la base de datos
em.getTransaction().commit();

Inserción de objetos

Para practicar su funcionamiento crea unos objetos Provincia y almacénalos en la base de datos. Si observas la clase Provincia que se había generado de manera automática anteriormente, dispones de los siguientes métodos constructores:

Provincia()

Provincia(Integer id)

Provincia(Integer id, String nombre)

Es decir, que se puede crear un objeto Provincia sin indicar ningún dato para sus propiedades obligatorias, o bien, sólo su id, o por último su id y su nombre. Ten en cuenta que se había creado la tabla Provincia de manera que el id sea autonumérico, por lo que si se crea un nuevo objeto Provincia especificando un valor concreto para su id, éste no se tendrá en cuenta. Crea el objeto Provincia para "Cádiz":

Provincia provinciaCadiz = new Provincia(0, "Cádiz");

Crea ahora otra provincia para "Sevilla", utilizando ahora el método constructor sin parámetros. Deberás usar el método setNombre que ofrece la clase Provincia, para asignarle el nombre:

Provincia provinciaSevilla = new Provincia();
provinciaSevilla.setNombre("Sevilla");

Ahora, para que ambos objetos se almacenen en la base de datos deberás iniciar la transacción, utilizar el método persist sobre los objetos, y por último volcar los cambios a la base de datos:

Provincia provinciaCadiz = new Provincia(0, "Cádiz");

Provincia provinciaSevilla = new Provincia();
provinciaSevilla.setNombre("Sevilla");

em.getTransaction().begin();
em.persist(provinciaCadiz);
em.persist(provinciaSevilla);
em.getTransaction().commit();

Comprobar el contenido de la base de datos desde NetBeans

Una manera de ver el contenido de las tablas de la base de datos puede ser desde el mismo entorno de desarrollo NetBeans. Puedes utilizar la conexión que se había creado con la base de datos en la parte inicial de este tutorial.

Screen Shot 2017 04 06 at 20.30.05 498d3

En caso de que la conexión se encuentre activa, deberás realizar una desconexión para luego volver a conectar si quieres ver las últimas modificaciones que se hayan realizado en los datos.

Screen Shot 2017 04 06 at 20.36.22 1d68b

Para ver los datos que se han almacenado en la tabla Provincia, despliega el árbol de la conexión hasta acceder a dicha tabla y desde su menú contextual selecciona la opción View Data.

Screen Shot 2017 04 06 at 20.38.57 5aae0

Si todo ha ido correctamente podrás ver que se ha ejecutado una sentencia SELECT para las primeras 100 filas de la tabla, y la parte inferior se muestra el resultado de la consulta. Observa que en esa misma ventana se ofrecen botones para insertar y eliminar registros, así como guardar y cancelar las modificaciones que también se puede hacer sobre los datos existentes.

Screen Shot 2017 04 06 at 20.44.54 c0757

Comprobar el contenido de la base de datos desde Java

Crea en tu proyecto una nueva clase de tipo Java Main Class (para que pueda ser ejecutado). Desde el menú contextual del paquete, elige: New > Java Main Class, o si no aparece: New > Other > Categoría Java > Java Main Class. Esta clase se va a utilizar para probar cómo consultar el contenido de la tabla Provincia, mostrándolo por la salida estándar, por lo que un buen nombre para esta clase podría ser ConsultaProvincias.java. Recuerda que puedes ejecutar una determinada clase (siempre que tenga método main) usando la opción Run File desde menú contextual.

Para hacer una consulta a la base de datos (SELECT) se debe crear un objeto de tipo Query (javax.persistence.Query). Una de las maneras más sencillas de obtener un objeto Query es utilizando una de las consultas predefinidas que se han generado automáticamente en las clases entidad. Por ejemplo, en la clase Provincia se han creado las siguientes consultas:

@NamedQueries({
    @NamedQuery(name = "Provincia.findAll", query = "SELECT p FROM Provincia p")
    , @NamedQuery(name = "Provincia.findById", query = "SELECT p FROM Provincia p WHERE p.id = :id")
    , @NamedQuery(name = "Provincia.findByCodigo", query = "SELECT p FROM Provincia p WHERE p.codigo = :codigo")
    , @NamedQuery(name = "Provincia.findByNombre", query = "SELECT p FROM Provincia p WHERE p.nombre = :nombre")})

Como puedes ver, hay consultas para obtener los registros de todas las provincias (Provincia.findAll), o bien hacer una selección en función de su id (Provincia.findById), código (Provincia.findByCodigo) o nombre (Provincia.findByNombre).

Para obtener la lista completa de las provincias que se han almacenado en la tabla, crea la consulta de la siguiente manera:

Query queryProvincias = em.createNamedQuery("Provincia.findAll");

Una vez creado el objeto Query, se debe ejecutar la consulta correspondiente llamando al método getResultList de la clase Query. Dicho método retorna un objeto List (java.util.List) al que conviene indicar el tipo de objetos (Provincia) que va a almacenar, que corresponderá al tipo de contenido de la tabla consultada.

List<Provincia> listProvincias = queryProvincias.getResultList();

A partir de ese momento, en la lista obtenida se encontrarán todos los objetos que existían en la tabla de la base de datos. Así que las siguientes instrucciones dependerán de la acción que se quiera realizar sobre dichos objetos. En nuestro caso, podemos probar a mostrar por la salida estándar la propiedad nombre de las provincias. Recuerda que en la clases entidad que se han generado, existen métodos get y set para cada propiedad, por lo que existe un método getNombre, que retorna el valor almacenado en esa propiedad.

public String getNombre()

Para recorrer todo el contenido de una lista se puede utilizar el formato de la sentencia for al que se le especifica el nombre de la lista a recorrer precedido de un nombre de variable donde se irá almacenando sucesivamente cada objeto contenido en la lista. Por ejemplo, prueba lo siguiente, y observa que aparecen en la salida estándar los nombre de las provincias que se encuentran almacenadas en la base de datos:

for(Provincia provincia : listProvincias) {
    System.out.println(provincia.getNombre());
}

Esto mismo podría hacerse con un bucle for tradicional, indicando un índice que permita obtener cada una de las posiciones de la lista (desde 0 hasta el tamaño de la lista - 1). Para ello, se usará el método size() de los objeto List, que retorna el número de objetos contenidos en la lista, y el método get(), que retorna el objeto de que se encuentra en la posición que se indique por parámetro. Este formato tiene la ventaja de poder recorrer sólo una parte de la lista si se desea.

for(int i=0; i<listProvincias.size(); i++) {
    Provincia provincia = listProvincias.get(i);
    System.out.println(provincia.getNombre());
}

Obtener determinados objetos

Si en lugar de querer obtener todos los objetos de una tabla se desean obtener sólo aquellos objetos que cumplen una determinada condición, pueden usarse las otras consultas predefinidas. Por ejemplo, para obtener las provincias con un nombre determinado puede usarse la consulta "Provincia.findByNombre":

@NamedQuery(name = "Provincia.findByNombre", query = "SELECT p FROM Provincia p WHERE p.nombre = :nombre")

Como puedes observar, en la parte final de la sentencia SELECT se incluye la cláusula WHERE donde se hace referencia al parámetro :nombre al que habrá que asignarle el valor del nombre de la provincia que se desea buscar. Esto se hará usando el método setParameter del objeto Query, al que se le debe indicar el nombre del parámetro y el valor a asignarle.

Por ejemplo, para obtener los objetos Provincia cuyo nombre sea "Cádiz", mostrando en la salida estándar el ID del registro y el nombre de la Provincia:

Query queryProvinciaCadiz = em.createNamedQuery("Provincia.findByNombre");
queryProvinciaCadiz.setParameter("nombre", "Cádiz");
List<Provincia> listProvinciasCadiz = queryProvinciaCadiz.getResultList();
for(Provincia provinciaCadiz : listProvinciasCadiz) {
    System.out.print(provinciaCadiz.getId() + ": ");
    System.out.println(provinciaCadiz.getNombre());
}

Además del método getResultList(), la clase Query ofrece métodos como getSingleResult(), cuando se sabe que sólo se va obtener un único objeto como respuesta a la consulta, getFirstResult(), para obtener únicamente el primer objeto que cumpla la consulta, getMaxResults(), para conocer el número de resultados de la consulta, etc. Consulta la documentación en cada caso para conocer su uso.

En caso de querer obtener un determinado objeto conociendo el valor de su clase primaria (en este caso el ID), se puede usar el método find() de la clase EntityManager, al que se debe indicar por parámetro la clase correspondiente al objeto a obtener, así como el valor de su clave primaria. Retornará null si no se ha encontrado ningún objeto con esa clave primaria.

<T> T find(Class<T> entityClass, Object primaryKey)

Por ejemplo, para obtener el objeto Provincia cuyo ID sea 2:

Provincia provinciaId2 = em.find(Provincia.class, 2);
if(provinciaId2 != null) {
    System.out.print(provinciaId2.getId() + ": ");
    System.out.println(provinciaId2.getNombre());    
} else {
    System.out.println("No hay ninguna provincia con ID=2");
}

Modificación de objetos

Si se dispone de una variable que haga referencia a un objeto que se encuentre previamente almacenado en la base de datos, se pueden realizar cambios en la información que contienen para luego volcar dichos cambios en la base de datos. Se debe utilizar el método merge de EntityManager indicando como parámetro el objeto que se desea actualizar en la base de datos, teniendo en cuenta que previamente se deben haber realizado los cambios oportunos en los valores de sus propiedades.

Por ejemplo, asigna el código "11" (ojo, no se trata del ID) a aquellos objetos que se encuentren cuyo nombre sea "Cádiz", y actualiza los cambios en la base de datos.

Query queryProvinciaCadiz = em.createNamedQuery("Provincia.findByNombre");
queryProvinciaCadiz.setParameter("nombre", "Cádiz");
List<Provincia> listProvinciasCadiz = queryProvinciaCadiz.getResultList();
em.getTransaction().begin();
for(Provincia provinciaCadiz : listProvinciasCadiz) {
    provinciaCadiz.setCodigo("11");
    em.merge(provinciaCadiz);
}
em.getTransaction().commit();

Comprueba que los cambios han tenido efecto viendo el contenido de la tabla en la base de datos.

Eliminación de objetos

Para eliminar un determinado objeto que se encontrara almacenado en la base de datos, se deberá usar el método remove de EntityManager, al que se le pasará como parámetro el objeto a eliminar.

void remove(Object entity)

Por ejemplo, para eliminar el objeto Provincia cuyo ID sea 2:

Provincia provinciaId2 = em.find(Provincia.class, 2);
if(provinciaId2 != null) {
    em.remove(provinciaId2);
} else {
    System.out.println("No hay ninguna provincia con ID=2");
}