Hibernate.org Community Documentation

- Anterior
- Siguiente

## Capítulo 11. Trabajo con objetos

Hibernate es una solución completa de mapeo objeto/relacional que no sólo proteje al desarrollador de los detalles del sistema de administración de la base datos subyacente, sino que además ofrece administración de estado de objetos. Contrario a la administración de `declaraciones` SQL en capas comunes de persistencia JDBC/SQL, esta es una vista natural orientada a objetos de la persistencia en aplicaciones Java.

En otras palabras, los desarrolladores de aplicaciones de Hibernate siempre deben pensar en el estado de sus objetos, y no necesariamente en la ejecución de declaraciones SQL. Hibernate se ocupa de esto y es sólamente relevante para el desarrollador de la aplicación al afinar el rendimiento del sistema.

## 11.1. Estados de objeto de Hibernate

Hibernate define y soporta los siguientes estados de objeto:

- Transitorio - un objeto es transitorio si ha sido recién instanciado utilizando el operador `new`, y no está asociado a una `Session` de Hibernate. No tiene una representación persistente en la base de datos y no se le ha asignado un valor identificador. Las instancias transitorias serán destruídas por el recolector de basura si la aplicación no mantiene más una referencia. Utiliza la `Session` de Hibernate para hacer un objeto persistente (y deja que Hibernate se ocupe de las declaraciones SQL que necesitan ejecutarse para esta transición).

- Persistente - una instancia persistente tiene una representación en la base de datos y un valor identificador. Puede haber sido guardado o cargado, sin embargo, por definición, se encuentra en el ámbito de una `Session`. Hibernate detectará cualquier cambio realizado a un objeto en estado persistente y sincronizará el estado con la base de datos cuando se complete la unidad de trabajo. Los desarrolladores no ejecutan declaraciones `UPDATE` manuales, o declaraciones `DELETE` cuando un objeto se debe poner como transitorio.

- Separado - una instancia separada es un objeto que se ha hecho persistente, pero su `Session` ha sido cerrada. La referencia al objeto todavía es válida, por supuesto, y la instancia separada podría incluso ser modificada en este estado. Una instancia separada puede ser re-unida a una nueva `Session` más tarde, haciéndola persistente de nuevo (con todas las modificaciones). Este aspecto habilita un modelo de programación para unidades de trabajo de ejecución larga que requieren tiempo-para-pensar por parte del usuario. Las llamamos transaccciones de aplicación, por ejemplo, una unidad de trabajo desde el punto de vista del usuario.

Discutiremos ahora los estados y transiciones de estados (y los métodos de Hibernate que disparan una transición) en más detalle.

## 11.2. Haciendo los objetos persistentes

Las instancias recién instanciadas de una clase persistente, Hibernate las considera como transitorias. Podemos hacer una instancia transitoria persistente asociándola con una sesión:

    DomesticCat fritz = new DomesticCat();

    fritz.setColor(Color.GINGER);

    fritz.setSex('M');

    fritz.setName("Fritz");

    Long generatedId = (Long) sess.save(fritz);

Si `Cat` tiene un identificador generado, el identificador es generado y asignado al `cat` cuando se llama a `save()`. Si `Cat` tiene un identificador `assigned`, o una clave compuesta, el identificador debe ser asignado a la instancia de `cat` antes de llamar a `save()`. También puede utilizar `persist()` en vez de `save()`, con la semántica definida en el borrador de EJB3.

- `persist()` hace que una instancia transitoria sea persistente. Sin embargo, no garantiza que el valor identificador sea asignado a la instancia persistente inmediatamente, la tarea puede tener lugar durante el vaciado. `persist()` también garantiza que no ejecutará una declaración `INSERT` si se llama por fuera de los límites de una transacción. Esto es útil en conversaciones largas con un contexto extendido sesión/persistencia.

- `save()` sí garantiza el retorno de un identificador. Si se tiene que ejecutar un INSERT para obtener el identificador ( por ejemplo, generador "identidad", no "secuencia"), este INSERT tiene lugar inmediatamente sin importar si se encuentra dentro o fuera de una transacción. Esto es problemático en una conversación larga con un contexto extendido sesión/persistencia.

Opcionalmente, puede asignar el identificador utilizando una versión sobrecargada de `save()`.

    DomesticCat pk = new DomesticCat();

    pk.setColor(Color.TABBY);

    pk.setSex('F');

    pk.setName("PK");

    pk.setKittens( new HashSet() );

    pk.addKitten(fritz);

    sess.save( pk, new Long(1234) );

Si el objeto que hace persistente tiene objetos asociados (por ejemplo, la colección `kittens` en el ejemplo anterior), estos objetos pueden ser hechos persistentes en cualquier orden que quiera a menos de que tenga una restricción `NOT NULL` sobre una columna clave foránea. Nunca hay riesgo de violar restricciones de clave foránea. Sin embargo, puede que usted viole una restricción `NOT NULL` si llama a `save()` sobre los objetos en el orden equivocado.

Usualmente no se preocupe de este detalle, pues muy probablemente utilizará la funcionalidad de persistencia transitiva de Hibernate para guardar los objetos asociados automáticamente. Entonces, ni siquiera tienen lugar violaciones de restricciones `NOT NULL` - Hibernate se ocupará de todo. Más adelante en este capítulo se discute la persistencia transitiva.

## 11.3. Cargando un objeto

Los métodos `load()` de `Session` le proporcionan una forma de recuperar una instancia persistente si ya conoce su identificador. `load()` toma un objeto clase y carga el estado dentro de una instancia recién instanciada de esa clase, en un estado persistente.

    Cat fritz = (Cat) sess.load(Cat.class, generatedId);

    // you need to wrap primitive identifiers

    long id = 1234;

    DomesticCat pk = (DomesticCat) sess.load( DomesticCat.class, new Long(id) );

Alternativamente, puede cargar estado dentro de una instancia dada:

    Cat cat = new DomesticCat();

    // load pk's state into cat

    sess.load( cat, new Long(pkId) );

    Set kittens = cat.getKittens();

Note que `load()` lanzará una excepción irrecuperable si no hay una fila correspondiente en la base de datos. Si la clase se mapea con un proxy, `load()` sólo retorna un proxy no inicializado y no llamará realmente a la base de datos hasta que invoque un método del proxy. Este comportamiento es muy útil si desea crear una asociación a un objeto sin cargarlo realmente de la base de datos. Además permite que múltiples instancias sean cargadas como un lote si se define `batch-size` para el mapeo de la clase.

Si no tiene la certeza de que existe una fila correspondiente, debe utilizar el método `get()`, que llama a la base de datos inmediatamente y devuelve nulo si no existe una fila correspondiente.

    Cat cat = (Cat) sess.get(Cat.class, id);

    if (cat==null) {

        cat = new Cat();

        sess.save(cat, id);

    }

    return cat;

Incluso puede cargar un objeto utilizando un `SELECT ... FOR UPDATE` de SQL, usando un `LockMode`. Consulte la documentación de la API para obtener más información.

    Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);

Ninguna instancia asociada o colección contenida es seleccionada para actualizacion - `FOR UPDATE`, a menos de que decida especificar `lock` o `all` como un estilo de cascada para la asociación.

Es posible volver a cargar un objeto y todas sus colecciones en cualquier momento, utilizando el método `refresh()`. Esto es útil cuando se usan disparadores de base de datos para inicializar algunas de las propiedades del objeto.

    sess.save(cat);

    sess.flush(); //force the SQL INSERT

    sess.refresh(cat); //re-read the state (after the trigger executes)

How much does Hibernate load from the database and how many SQL `SELECT`s will it use? This depends on the fetching strategy. This is explained in Sección 21.1, “Estrategias de recuperación”.

## 11.4. Consultas

Si no conoce los identificadores de los objetos que está buscando, necesita una consulta. Hibernate soporta un lenguaje de consulta orientado a objetos (HQL) fácil de usar pero potente a la vez. Para la creación de consultas programáticas, Hibernate soporta una funcionalidad sofisticada de consulta de Criteria y Example (QBC y QBE). También puede expresar su consulta en el SQL nativo de su base de datos, con soporte opcional de Hibernate para la conversión del conjunto de resultados a objetos.

### 11.4.1. Ejecución de consultas

Las consultas HQL y SQL nativas son representadas con una instancia de `org.hibernate.Query`. Esta interfaz ofrece métodos para ligar parámetros, manejo del conjunto resultado, y para la ejecución de la consulta real. Siempre obtiene una `Query` utilizando la `Session` actual:

    List cats = session.createQuery(

        "from Cat as cat where cat.birthdate < ?")

        .setDate(0, date)

        .list();

    List mothers = session.createQuery(

        "select mother from Cat as cat join cat.mother as mother where cat.name = ?")

        .setString(0, name)

        .list();

    List kittens = session.createQuery(

        "from Cat as cat where cat.mother = ?")

        .setEntity(0, pk)

        .list();

    Cat mother = (Cat) session.createQuery(

        "select cat.mother from Cat as cat where cat = ?")

        .setEntity(0, izi)

        .uniqueResult();]]

    Query mothersWithKittens = (Cat) session.createQuery(

        "select mother from Cat as mother left join fetch mother.kittens");

    Set uniqueMothers = new HashSet(mothersWithKittens.list());

Una consulta se ejecuta usualmente invocando a `list()`. El resultado de la consulta será cargado completamente dentro de una colección en memoria. Las instancias de entidad recuperadas por una consulta se encuentran en estado persistente. El método `uniqueResult()` ofrece un atajo si sabe que su consulta retornará sólamente un objeto. Las consultas que hacen uso de una recuperación temprana de colecciones usualmente retornan duplicados de los objetos raíz, pero con sus colecciones inicializadas. Puede filtrar estos duplicados a través de un `Set`.

#### 11.4.1.1. Iteración de resultados

Ocasionalmente, puede lograr un mejor rendimiento al ejecutar la consulta utilizando el método `iterate()`. Esto ocurrirá usualmente si espera que las instancias reales de entidad retornadas por la consulta estén ya en la sesión o en el caché de segundo nivel. Si todavía no están en caché, `iterate()` será más lento que `list()` y podría requerir muchas llamadas a la base de datos para una consulta simple, usualmente 1 para la selección inicial que sólamente retorna identificadores y n selecciones adicionales para inicializar las instancias reales.

    // fetch ids

    Iterator iter = sess.createQuery("from eg.Qux q order by q.likeliness").iterate();

    while ( iter.hasNext() ) {

        Qux qux = (Qux) iter.next();  // fetch the object

        // something we couldnt express in the query

        if ( qux.calculateComplicatedAlgorithm() ) {

            // delete the current instance

            iter.remove();

            // dont need to process the rest

            break;

        }

    }

#### 11.4.1.2. Consultas que devuelven tuplas

Las consultas de Hibernate a veces retornan tuplas de objetos. Cada tupla se retorna como un array:

    Iterator kittensAndMothers = sess.createQuery(

                "select kitten, mother from Cat kitten join kitten.mother mother")

                .list()

                .iterator();

    while ( kittensAndMothers.hasNext() ) {

        Object[] tuple = (Object[]) kittensAndMothers.next();

        Cat kitten = (Cat) tuple[0];

        Cat mother = (Cat) tuple[1];

        ....

    }

#### 11.4.1.3. Resultados escalares

Las consultas pueden especificar una propiedad de una clase en la cláusula `select`. Pueden incluso llamar a funciones de agregación SQL. Las propiedades o agregaciones son considerados resultados "escalares" y no entidades en estado persistente.

    Iterator results = sess.createQuery(

            "select cat.color, min(cat.birthdate), count(cat) from Cat cat " +

            "group by cat.color")

            .list()

            .iterator();

    while ( results.hasNext() ) {

        Object[] row = (Object[]) results.next();

        Color type = (Color) row[0];

        Date oldest = (Date) row[1];

        Integer count = (Integer) row[2];

        .....

    }

#### 11.4.1.4. Ligado de parámetros

Los métodos en `Query` se proporcionan para enlazar valores a los parámetros con nombre o parámetros `?` de estilo JDBC. Al contrario de JDBC, Hibernate numera los parámetros desde cero. Los parámetros con nombre son identificadores de la forma `:name` en la cadena de la consulta. Las ventajas de los parámetros con nombre son las siguientes:

- los parámetros con nombre son insensibles al orden en que aparecen en la cadena de consulta

- pueden aparecer múltiples veces en la misma petición

- son auto-documentados

    //named parameter (preferred)

    Query q = sess.createQuery("from DomesticCat cat where cat.name = :name");

    q.setString("name", "Fritz");

    Iterator cats = q.iterate();

    //positional parameter

    Query q = sess.createQuery("from DomesticCat cat where cat.name = ?");

    q.setString(0, "Izi");

    Iterator cats = q.iterate();

    //named parameter list

    List names = new ArrayList();

    names.add("Izi");

    names.add("Fritz");

    Query q = sess.createQuery("from DomesticCat cat where cat.name in (:namesList)");

    q.setParameterList("namesList", names);

    List cats = q.list();

#### 11.4.1.5. Paginación

Si necesita especificar enlaces sobre su conjunto de resultados, el número máximo de filas que quiere recuperar y/o la primera fila que quiere recuperar, puede utilizar los métodos de la interfaz `Query`:

    Query q = sess.createQuery("from DomesticCat cat");

    q.setFirstResult(20);

    q.setMaxResults(10);

    List cats = q.list();

Hibernate sabe cómo traducir este límite de consulta al SQL nativo de su DBMS.

#### 11.4.1.6. Iteración deslizable

Si su controlador JDBC soporta `ResultSet`s deslizables, la interfaz `Query` se puede utilizar para obtener un objeto `ScrollableResults` que permite una navegación flexible de los resultados de consulta.

    Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " +

                                "order by cat.name");

    ScrollableResults cats = q.scroll();

    if ( cats.first() ) {

        // find the first name on each page of an alphabetical list of cats by name

        firstNamesOfPages = new ArrayList();

        do {

            String name = cats.getString(0);

            firstNamesOfPages.add(name);

        }

        while ( cats.scroll(PAGE_SIZE) );

        // Now get the first page of cats

        pageOfCats = new ArrayList();

        cats.beforeFirst();

        int i=0;

        while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1) );

    }

    cats.close()

Note que se requiere una conexión de base de datos abierta y un cursor para esta funcionalidad. Utilice `setMaxResult()`/`setFirstResult()` si necesita la funcionalidad de paginación fuera de línea.

#### 11.4.1.7. Externalización de consultas con nombre

Queries can also be configured as so called named queries using annotations or Hibernate mapping documents. `@NamedQuery` and `@NamedQueries` can be defined at the class level as seen in Ejemplo 11.1, “Defining a named query using @NamedQuery” . However their definitions are global to the session factory/entity manager factory scope. A named query is defined by its name and the actual query string.

Ejemplo 11.1. Defining a named query using `@NamedQuery`

    @Entity

    @NamedQuery(name="night.moreRecentThan", query="select n from Night n where n.date >= :date")

    public class Night {

        ...

    }

    public class MyDao {

        doStuff() {

            Query q = s.getNamedQuery("night.moreRecentThan");

            q.setDate( "date", aMonthAgo );

            List results = q.list();

            ...

        }

        ...

    }

Using a mapping document can be configured using the `<query>` node. Remember to use a `CDATA` section if your query contains characters that could be interpreted as markup.

Ejemplo 11.2. Defining a named query using `<query>`

    <query name="ByNameAndMaximumWeight"><![CDATA[

        from eg.DomesticCat as cat

            where cat.name = ?

            and cat.weight > ?

    ] ]></query>

Parameter binding and executing is done programatically as seen in Ejemplo 11.3, “Parameter binding of a named query”.

Ejemplo 11.3. Parameter binding of a named query

    Query q = sess.getNamedQuery("ByNameAndMaximumWeight");

    q.setString(0, name);

    q.setInt(1, minWeight);

    List cats = q.list();

El código real del programa es independiente del lenguaje de consulta utilizado. También puede definir consultas SQL nativas en metadatos, o migrar consultas existentes a Hibernate colocándolas en archivos de mapeo.

Observe además que una declaración de consulta dentro de un elemento `<hibernate-mapping>` necesita de un nombre único global para la consulta, mientras que una declaración de consulta dentro de un elemento `<class>` se hace única automáticamente al agregar el nombre completamente calificado de la clase. Por ejemplo, `eg.Cat.ByNameAndMaximumWeight`.

### 11.4.2. Filtración de colecciones

Un filtro de colección es un tipo especial de consulta que puede ser aplicado a una colección persistente o array. La cadena de consulta puede referirse a `this`, lo que quiere decir el elemento de la colección actual.

    Collection blackKittens = session.createFilter(

        pk.getKittens(),

        "where this.color = ?")

        .setParameter( Color.BLACK, Hibernate.custom(ColorUserType.class) )

        .list()

    );

La colección devuelta es considerada un bag, y es una copia de la colección dada. La colección original no es modificada. Esto es lo opuesto a lo que implica el nombre "filtro", pero es consistente con el comportamiento esperado.

Observe que los filtros no requieren una cláusula `from` aunque pueden tener una si se necesita. Los filtros no están limitados a devolver los elementos de colección por sí mismos.

    Collection blackKittenMates = session.createFilter(

        pk.getKittens(),

        "select this.mate where this.color = eg.Color.BLACK.intValue")

        .list();

Incluso una consulta de filtro vacío es útil, por ejemplo, para cargar un subconjunto de elementos en una colección enorme:

    Collection tenKittens = session.createFilter(

        mother.getKittens(), "")

        .setFirstResult(0).setMaxResults(10)

        .list();

### 11.4.3. Consultas de criterios

HQL es extremadamente potente pero algunos desarrolladores prefieren construir consultas dinámicamente utilizando una API orientada a objetos, en vez de construir cadenas de consulta. Hibernate brinda una API intuitiva de consulta `Criteria` para estos casos:

    Criteria crit = session.createCriteria(Cat.class);

    crit.add( Restrictions.eq( "color", eg.Color.BLACK ) );

    crit.setMaxResults(10);

    List cats = crit.list();

The `Criteria` and the associated `Example` API are discussed in more detail in Capítulo 17, Consultas por criterios.

### 11.4.4. Consultas en SQL nativo

Puede expresar una consulta en SQL, utilizando `createSQLQuery()` y dejar que Hibernate administre el mapeo de los conjuntos de resultados a objetos. Puede llamar en cualquier momento a `session.connection()` y utilizar la `Connection` JDBC directamente. Si elige usar la API de Hibernate, tiene que encerrar los alias de SQL entre llaves:

    List cats = session.createSQLQuery("SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10")

        .addEntity("cat", Cat.class)

    .list();

    List cats = session.createSQLQuery(

        "SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " +

               "{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " +

        "FROM CAT {cat} WHERE ROWNUM<10")

        .addEntity("cat", Cat.class)

    .list()

SQL queries can contain named and positional parameters, just like Hibernate queries. More information about native SQL queries in Hibernate can be found in Capítulo 18, SQL Nativo.

## 11.5. Modificación de objetos persistentes

Las instancias persistentes transaccionales (por ejemplo, los objetos cargados, creados o consultados por la `Session`) pueden ser manipulados por la aplicación y cualquier cambio al estado persistente será persistido cuando se vacie la `Session`. Esto se discute más adelante en este capítulo. No hay necesidad de llamar a un método en particular (como `update()`, que tiene un propósito diferente) para hacer persistentes sus modificaciones. De modo que la forma más directa de actualizar el estado de un objeto es cargarlo con `load()` y luego manipularlo directamente, mientras la `Session` está abierta:

    DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) );

    cat.setName("PK");

    sess.flush();  // changes to cat are automatically detected and persisted

A veces este modelo de programación es ineficiente pues requiere un `SELECT` de SQL para cargar un objeto y un `UPDATE` de SQL para hacer persistente su estado actualizado en la misma sesión. Por lo tanto, Hibernate ofrece un enfoque opcional, utilizando instancias separadas.

## 11.6. Modificación de objetos separados

Muchas aplicaciones necesitan recuperar un objeto en una transacción, enviarla a la capa de UI para su manipulación, y entonces guardar los cambios en una nueva transacción. Las aplicaciones que usan este tipo de enfoque en un entorno de alta concurrencia usualmente utilizan datos versionados para asegurar el aislamiento de la unidad de trabajo "larga".

Hibernate soporta este modelo al proveer re-unión de instancias separadas utilizando los métodos `Session.update()` o `Session.merge()`:

    // in the first session

    Cat cat = (Cat) firstSession.load(Cat.class, catId);

    Cat potentialMate = new Cat();

    firstSession.save(potentialMate);

    // in a higher layer of the application

    cat.setMate(potentialMate);

    // later, in a new session

    secondSession.update(cat);  // update cat

    secondSession.update(mate); // update mate

Si el `Cat` con identificador `catId` ya hubiera sido cargado por `secondSession` cuando la aplicación intentó volver a unirlo, se habría lanzado una excepción.

Utilice `update()` si está seguro de que la sesión no tiene una instancia ya persistente con el mismo identificador. Utilice `merge()` si quiere fusionar sus modificaciones en cualquier momento sin consideración del estado de la sesión. En otras palabras, `update()` usualmente es el primer método que usted llamaría en una sesión actualizada, asegurando que la re-unión de sus instancias separadas es la primera operación que se ejecuta.

The application should individually `update()` detached instances that are reachable from the given detached instance only if it wants their state to be updated. This can be automated using transitive persistence. See Sección 11.11, “Persistencia transitiva” for more information.

El método `lock()` también le permite a una aplicación reasociar un objeto con una sesión nueva. Sin embargo, la instancia separada no puede haber sido modificada.

    //just reassociate:

    sess.lock(fritz, LockMode.NONE);

    //do a version check, then reassociate:

    sess.lock(izi, LockMode.READ);

    //do a version check, using SELECT ... FOR UPDATE, then reassociate:

    sess.lock(pk, LockMode.UPGRADE);

Note que `lock()` se puede utilizar con varios `LockMode`s. Consulte la documentación de la API y el capítulo sobre el manejo de transacciones para obtener mayor información. La re-unión no es el único caso de uso para `lock()`.

Other models for long units of work are discussed in Sección 13.3, “Control de concurrencia optimista”.

## 11.7. Detección automática de estado

Los usuarios de Hibernate han pedido un método de propósito general que bien guarde una instancia transitoria generando un identificador nuevo, o bien actualice/reúna las instancias separadas asociadas con su identificador actual. El método `saveOrUpdate()` implementa esta funcionalidad.

    // in the first session

    Cat cat = (Cat) firstSession.load(Cat.class, catID);

    // in a higher tier of the application

    Cat mate = new Cat();

    cat.setMate(mate);

    // later, in a new session

    secondSession.saveOrUpdate(cat);   // update existing state (cat has a non-null id)

    secondSession.saveOrUpdate(mate);  // save the new instance (mate has a null id)

La utilización y semántica de `saveOrUpdate()` parece ser confuso para los usuarios nuevos. Primero, en tanto no esté tratando de utilizar instancias de una sesión en otra sesión nueva, no debe necesitar usar `update()`, `saveOrUpdate()`, o `merge()`. Algunas aplicaciones enteras nunca usarán ninguno de estos métodos.

Usualmente `update()` o `saveOrUpdate()` se utilizan en el siguiente escenario:

- la aplicación carga un objeto en la primera sesión

- el objeto se pasa a la capa de UI

- se realizan algunas modificaciones al objeto

- el objeto se pasa abajo de regreso a la capa lógica de negocios

- la aplicación hace estas modificaciones persistentes llamando a `update()` en una segunda sesión

`saveOrUpdate()` hace lo siguiente:

- si el objeto ya es persistente en esta sesión, no haga nada

- si otro objeto asociado con la sesión tiene el mismo identificador, lance una excepción

- si el objeto no tiene ninguna propiedad identificadora, guárdelo llamando a `save()`

- si el identificador del objeto tiene el valor asignado a un objeto recién instanciado, guárdelo llamando a `save()`

- si el objeto está versionado por un `<version>` o `<timestamp>`, y el valor de la propiedad de versión es el mismo valor asignado a un objeto recién instanciado, guárdelo llamando a `save()`

- de otra manera actualice el objeto llamando a `update()`

y `merge()` es muy diferente:

- si existe una instancia persistente con el mismo identificador asignado actualmente con la sesión, copie el estado del objeto dado en la instancia persistente

- si no existe ninguna instancia persistente asociada a la sesión actualmente, intente cargarla desde la base de datos, o cree una nueva instancia persistente

- la instancia persistente es devuelta

- la instancia dada no se asocia a la sesión, permanece separada

## 11.8. Borrado de objetos persistentes

`Session.delete()` borrará el estado de un objeto de la base de datos. Sin embargo, su aplicación puede tener todavía una referencia a un objeto borrado. Lo mejor es pensar en `delete()` al hacer transitoria una instancia persistente.

    sess.delete(cat);

Puede borrar objetos en el orden que quiera, sin riesgo de violaciones de restricción de clave foránea. Aún es posible violar una restricción `NOT NULL` sobre una columna de clave foránea borrando objetos en un orden erróneo, por ejemplo, si borra el padre, pero olvida borrar los hijos.

## 11.9. Replicación de objetos entre dos almacenamientos de datos diferentes

A veces es útil poder tomar un grafo de la instancias persistentes y hacerlas persistentes en un almacenamiento de datos diferente, sin regenerar los valores identificadores.

    //retrieve a cat from one database

    Session session1 = factory1.openSession();

    Transaction tx1 = session1.beginTransaction();

    Cat cat = session1.get(Cat.class, catId);

    tx1.commit();

    session1.close();

    //reconcile with a second database

    Session session2 = factory2.openSession();

    Transaction tx2 = session2.beginTransaction();

    session2.replicate(cat, ReplicationMode.LATEST_VERSION);

    tx2.commit();

    session2.close();

El `ReplicationMode` determina cómo `replicate()` tratará los conflictos con filas existentes en la base de datos:

- `ReplicationMode.IGNORE`: ignora el objeto cuando existe una fila de la base de datos con el mismo identificador

- `ReplicationMode.OVERWRITE`: sobrescribe cualquier fila de la base de datos existente con el mismo identificador

- `ReplicationMode.EXCEPTION`: lanza una excepción si existe una fila de la base de datos con el mismo identificador

- `ReplicationMode.LATEST_VERSION`: sobrescribe la fila si su número de versión es anterior al número de versión del objeto, o de lo contrario ignora el objeto

Los casos de uso para esta funcionalidad incluyen reconciliar datos ingresados en instancias diferentes de bases de datos, actualizar información de configuración del sistema durante actualizaciones de producto, deshacer cambios realizados durante transacciones no-ACID y más.

## 11.10. Limpieza (flushing) de la sesión

A veces la `Session` ejecutará las declaraciones SQL necesarias para sincronizar el estado de la conexión JDBC con el estado de los objetos en la menoria. Este proceso, denominado vaciado (flush), ocurre por defecto en los siguientes puntos:

- antes de algunas ejecuciones de consulta

- desde `org.hibernate.Transaction.commit()`

- desde `Session.flush()`

Las declaraciones SQL se emiten en el siguiente orden:

1. todas las inserciones de entidades, en el mismo orden que los objetos correspondientes fueron guardados utilizando `Session.save()`

2. todas las actualizaciones de entidades

3. todas los borrados de colecciones

4. todos los borrados, actualizaciones e inserciones de elementos de colección

5. todas las inserciones de colecciones

6. todos los borrados de entidades, en el mismo orden que los objetos correspondientes fueron borrados usando `Session.delete()`

Una excepción es que los objetos que utilizan generación de ID `native` se insertan cuando se guardan.

Excepto cuando llama explícitamente a `flush()`, no hay en absoluto garantías sobre cuándo la `Session` ejecuta las llamadas JDBC, sólamente sobre el orden en que se ejecutan. Sin embargo, Hibernate garantiza que los métodos `Query.list(..)` nunca devolverán datos desactualizados o incorrectos.

It is possible to change the default behavior so that flush occurs less frequently. The `FlushMode` class defines three different modes: only flush at commit time when the Hibernate `Transaction` API is used, flush automatically using the explained routine, or never flush unless `flush()` is called explicitly. The last mode is useful for long running units of work, where a `Session` is kept open and disconnected for a long time (see Sección 13.3.2, “Sesión extendida y versionado automático”).

    sess = sf.openSession();

    Transaction tx = sess.beginTransaction();

    sess.setFlushMode(FlushMode.COMMIT); // allow queries to return stale state

    Cat izi = (Cat) sess.load(Cat.class, id);

    izi.setName(iznizi);

    // might return stale data

    sess.find("from Cat as cat left outer join cat.kittens kitten");

    // change to izi is not flushed!

    ...

    tx.commit(); // flush occurs

    sess.close();

During flush, an exception might occur (e.g. if a DML operation violates a constraint). Since handling exceptions involves some understanding of Hibernate's transactional behavior, we discuss it in Capítulo 13, Transacciones y concurrencia.

## 11.11. Persistencia transitiva

Es absolutamente incómodo guardar, borrar, o reunir objetos individuales, especialmente si trata con un grafo de objetos asociados. Un caso común es una relación padre/hijo. Considere el siguiente ejemplo:

Si los hijos en una relación padre/hijo pudieran ser tipificados en valor (por ejemplo, una colección de direcciones o cadenas), sus ciclos de vida dependerían del padre y no se requeriría ninguna otra acción para el tratamiento apropiado en "cascada" de los cambios de estado. Cuando se guarda el padre, los objetos hijo tipificados en valor también se guardan, cuando se borra el padre, se borran los hijos, etc. Esto funciona incluso para operaciones tales como el retiro de un hijo de la colección. Hibernate detectará esto y ya que los objetos tipificados en valor no pueden tener referencias compartidas entonces borrará el hijo de la base de datos.

Ahora considere el mismo escenario con los objetos padre e hijos siendo entidades, no tipos de valor (por ejemplo, categorías e ítems, o gatos padres e hijos). Las entidades tienen su propio ciclo de vida y soportan referencias compartidas. El eliminar una entidad de una colección no significa que se pueda borrar, y no hay por defecto ningún tratamiento en "cascada" del estado de una entidad a otras entidades asociadas. Hibernate no implementa por defecto la persistencia por alcance.

Para cada operación básica de la sesión de Hibernate - incluyendo `persist(), merge(), saveOrUpdate(), delete(), lock(), refresh(), evict(), replicate()` - existe un estilo de cascada correspondiente. Respectivamente, los estilos de cascada se llaman `create, merge, save-update, delete, lock, refresh, evict, replicate`. Si quiere que una operación sea tratada en cascada a lo largo de una asociación, debe indicar eso en el documento de mapeo. Por ejemplo:

    <one-to-one name="person" cascade="persist"/>

Los estilos de cascada pueden combinarse:

    <one-to-one name="person" cascade="persist,delete,lock"/>

Incluso puede utilizar `cascade="all"` para especificar que todas las operaciones deben ser tratadas en cascada a lo largo de la asociación. La `cascade="none"` predeterminada especifica que ninguna operación se tratará en cascada.

In case you are using annotatons you probably have noticed the `cascade` attribute taking an array of `CascadeType` as a value. The cascade concept in JPA is very is similar to the transitive persistence and cascading of operations as described above, but with slightly different semantics and cascading types:

- `CascadeType.PERSIST`: cascades the persist (create) operation to associated entities persist() is called or if the entity is managed

- `CascadeType.MERGE`: cascades the merge operation to associated entities if merge() is called or if the entity is managed

- `CascadeType.REMOVE`: cascades the remove operation to associated entities if delete() is called

- `CascadeType.REFRESH:` cascades the refresh operation to associated entities if refresh() is called

- `CascadeType.DETACH:` cascades the detach operation to associated entities if detach() is called

- `CascadeType.ALL`: all of the above

## Nota

CascadeType.ALL also covers Hibernate specific operations like save-update, lock etc...

A special cascade style, `delete-orphan`, applies only to one-to-many associations, and indicates that the `delete()` operation should be applied to any child object that is removed from the association. Using annotations there is no `CascadeType.DELETE-ORPHAN` equivalent. Instead you can use the attribute `orphanRemoval as seen in` Ejemplo 11.4, “@OneToMany with orphanRemoval”. If an entity is removed from a `@OneToMany` collection or an associated entity is dereferenced from a `@OneToOne` association, this associated entity can be marked for deletion if `orphanRemoval` is set to true.

Ejemplo 11.4. `@OneToMany` with `orphanRemoval`

    @Entity

    public class Customer {

       private Set<Order> orders;

       @OneToMany(cascade=CascadeType.ALL, orphanRemoval=true)

       public Set<Order> getOrders() { return orders; }

       public void setOrders(Set<Order> orders) { this.orders = orders; }

       [...]

    }

    @Entity

    public class Order { ... }

    Customer customer = em.find(Customer.class, 1l);

    Order order = em.find(Order.class, 1l);

    customer.getOrders().remove(order); //order will be deleted by cascade

Recomendaciones:

- It does not usually make sense to enable cascade on a many-to-one or many-to-many association. In fact the `@ManyToOne` and `@ManyToMany` don't even offer a `orphanRemoval` attribute. Cascading is often useful for one-to-one and one-to-many associations.

- If the child object's lifespan is bounded by the lifespan of the parent object, make it a life cycle object by specifying `cascade="all,delete-orphan"(`@OneToMany(cascade=CascadeType.ALL, orphanRemoval=true)`)`.

- En otro caso, puede que usted no necesite tratamiento en cascada en absoluto. Pero si piensa que va a estar trabajando frecuentemente con padre e hijos juntos en la misma transacción, y quiere ahorrarse algo de escritura en computador, considere el utilizar `cascade="persist,merge,save-update"`.

Mapear una asociación (ya sea una asociación monovaluada, o una colección) con `cascade="all"` marca la asociación como una relación del estilo padre/hijo en donde guardar/actualizar/borrar (save/update/delete) el padre causa el guardar/actualizar/borrar del hijo o hijos.

Furthermore, a mere reference to a child from a persistent parent will result in save/update of the child. This metaphor is incomplete, however. A child which becomes unreferenced by its parent is not automatically deleted, except in the case of a one-to-many association mapped with `cascade="delete-orphan"`. The precise semantics of cascading operations for a parent/child relationship are as follows:

- Si un padre pasa a `persist()`, se pasan todos los hijos a `persist()`

- Si un padre pasa a `merge()`, se pasan todos los hijos a `merge()`

- Si se pasa un padre a `save()`, `update()` o `saveOrUpdate()`, todos los hijos pasan a `saveOrUpdate()`

- Si un hijo transitorio o separado se vuelve referenciado por un padre persistente, le es pasado a `saveOrUpdate()`

- Si se borra un padre, se pasan todos los hijos a `delete()`

- Si un hijo deja de ser referenciado por un padre persistente, no ocurre nada especial - la aplicación debe borrar explícitamente el hijo de ser necesario - a menos que `cascade="delete-orphan"`, en cuyo caso se borra el hijo "huérfano".

Finalmente, note que las operaciones en cascadas se pueden aplicar a un grafo de objeto en tiempo de llamada o en tiempo de vaciado. Todas las operaciones, si se encuentran activadas se tratan en cascadas en entidades asociadas alcanzables cuando se ejecuta la operación. Sin embargo, `save-upate` y `delete-orphan` son transitivos para todas las entidades asociadas alcanzables durante el vaciado de la `Session`.

## 11.12. Utilización de metadatos

Hibernate requiere de un modelo de meta-nivel muy rico de todas las entidades y tipos de valor. Este modelo puede ser útil para la aplicación misma. Por ejemplo, la aplicación podría utilizar los metadatos de Hibernate para implementar un algoritmo "inteligente" de copia en profundidad que entienda qué objetos deben ser copiados (por ejemplo, tipos de valor mutables) y cuáles no (por ejemplo, tipos de valor inmutables y posiblemente las entidades asociadas).

Hibernate expone los metadatos por medio de las interfaces `ClassMetadata` y `CollectionMetadata` y la jerarquía `Type`. Las instancias de las interfaces de metadatos se pueden obtener de la `SessionFactory`.

    Cat fritz = ......;

    ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);

    Object[] propertyValues = catMeta.getPropertyValues(fritz);

    String[] propertyNames = catMeta.getPropertyNames();

    Type[] propertyTypes = catMeta.getPropertyTypes();

    // get a Map of all properties which are not collections or associations

    Map namedValues = new HashMap();

    for ( int i=0; i<propertyNames.length; i++ ) {

        if ( !propertyTypes[i].isEntityType() && !propertyTypes[i].isCollectionType() ) {

            namedValues.put( propertyNames[i], propertyValues[i] );

        }

    }

Copyright © 2004 Red Hat, Inc.

- AnteriorCapítulo 10. Mapeo de herencias
- Subir
- Inicio
- SiguienteCapítulo 12. Read-only entities