Hibernate.org Community Documentation

- Anterior
- Próxima

## Capítulo 11. Trabalhando com objetos

O Hibernate é uma solução completa de mapeamento objeto/relacional que não apenas poupa o desenvolvedor dos detalhes de baixo nível do sistema de gerenciamento do banco de dados, como também oferece um gerenciamento de estado para objetos. Isto é, ao contrário do gerenciamento de `instruções` SQL em camadas de persistência JDBC/SQL comuns, uma visão natural da persistência orientada a objetos em aplicações Java.

Em outras palavras, desenvolvedores de aplicações Hibernate podem sempre considerar o estado de seus objetos, e não necessariamente a execução de instruções SQL. O Hibernate é responsável por esta parte e é relevante aos desenvolvedores de aplicações apenas quando estão ajustando o desempenho do sistema.

## 11.1. Estado dos objetos no Hibernate

O Hibernate define e suporta os seguintes estados de objetos:

- Transient - um objeto é transiente se ele foi instanciando usando apenas o operador `new` e não foi associado a uma `Session` do Hibernate. Ele não possui uma representação persistente no banco de dados e não lhe foi atribuído nenhum identificador. Instâncias transientes serão destruídas pelo coletor de lixo se a aplicação não mantiver sua referência. Use uma `Session` do Hibernate para tornar o objeto persistente (e deixe o Hibernate gerenciar as instruções SQL que serão necessárias para executar esta transição).

- Persistent - uma instância persistente possui uma representação no banco de dados e um identificador. Ela pode ter sido salva ou carregada, portanto ela se encontra no escopo de uma `Session`. O Hibernate irá detectar qualquer mudança feita a um objeto persistente e sincronizar o seu estado com o banco de dados quando completar a unidade de trabalho. Desenvolvedores não executam instruções manuais de `UPDATE`, ou instruções de `DELETE` quando o objeto se tornar transiente.

- Detached – uma instância desanexada é um objeto que foi persistido, mas sua `Session` foi fechada. A referência ao objeto continua válida, é claro, e a instância desanexada pode ser acoplada a uma nova `Session` no futuro, tornando-o novamente persistente (e todas as modificações sofridas). Essa característica habilita um modelo de programação para unidades de trabalho de longa execução, que requeira um tempo de espera do usuário. Podemos chamá-las de transações da aplicação, ou seja, uma unidade de trabalho do ponto de vista do usuário.

Agora iremos discutir os estados e suas transições (e os métodos do Hibernate que disparam uma transição) em mais detalhes.

## 11.2. Tornando os objetos persistentes

As instâncias recentemente instanciadas de uma classe persistente são consideradas transientes pelo Hibernate. Podemos transformar uma instância transiente em persistente associando-a a uma sessão:

    DomesticCat fritz = new DomesticCat();

    fritz.setColor(Color.GINGER);

    fritz.setSex('M');

    fritz.setName("Fritz");

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

Se `Cat` possui um identificador gerado, o identificador é gerado e atribuído à `cat` quando `save()` for chamado. Se `Cat` possuir um identificador `Associado`, ou uma chave composta, o identificador deverá ser atribuído à instância de `cat` antes que `save()` seja chamado. Pode-se usar também `persist()` ao invés de `save()`, com a semântica definida no novo esboço do EJB3.

- `persist()` faz uma instância transciente persistente. No entanto, isto não garante que o valor do identificador será determinado à instância persistente imediatamente, pois a determinação pode acontecer no período de limpeza. O `persist()` também garante que isto não executará uma declaração `INSERT` caso esta seja chamada fora dos limites da transação. Isto é útil em transações de longa-execução com um contexto de Sessão/persistência estendido.

- `save()` garante retornar um identificador. Caso um INSERT necessita ser executado para obter o identificador (ex.: gerador "identidade" e não "seqüência"), este INSERT acontece imediatamente, independente de você estar dentro ou fora da transação. Isto é problemático numa conversação de longa execução com um contexto de Sessão/persistência estendido.

Alternativamente, pode-se atribuir o identificador usando uma versão sobrecarregada 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) );

Se o objeto persistido tiver associado objetos (ex.: a coleção `kittens` no exemplo anterior), esses objetos podem se tornar persistentes em qualquer ordem que se queira, a não ser que se tenha uma restrição `NOT NULL` em uma coluna de chave estrangeira. Nunca há risco de violação de restrições de chave estrangeira. Assim, pode-se violar uma restrição `NOT NULL` se `save()` for usado nos objetos em uma ordem errada.

Geralmente você não precisa se preocupar com esses detalhes, pois muito provavelmente usará a característica de persistência transitiva do Hibernate para salvar os objetos associados automaticamente. Assim, enquanto uma restrição `NOT NULL` não ocorrer, o Hibernate tomará conta de tudo. Persistência transitiva será discutida mais adiante nesse mesmo capítulo.

## 11.3. Carregando o objeto

O método `load()` de uma `Session` oferece uma maneira de recuperar uma instância persistente se o identificador for conhecido. O `load()` escolhe uma classe do objeto e carregará o estado em uma instância mais recente dessa classe, em 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, pode-se carregar um estado em uma instância dada:

    Cat cat = new DomesticCat();

    // load pk's state into cat

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

    Set kittens = cat.getKittens();

Repare que `load()` irá lançar uma exceção irrecuperável se não houver na tabela no banco de dados um registro que combine. Se a classe for mapeada com um proxy, `load()` simplesmente retorna um proxy não inicializado e realmente não chamará o banco de dados até que um método do proxy seja invocado. Esse comportamento é muito útil para criar uma associação com um objeto sem que realmente o carregue do bando de dados. Isto também permite que sejam carregadas múltiplas instâncias como um grupo se o `batch-size` estiver definido para o mapeamento da classe.

Se você não tiver certeza da existência do registro no banco, você deve usar o método `get()`, que consulta o banco imediatamente e retorna um null se não existir o registro.

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

    if (cat==null) {

        cat = new Cat();

        sess.save(cat, id);

    }

    return cat;

Também pode-se carregar um objeto usando `SELECT ... FOR UPDATE`, usando um `LockMode`. Veja a documentação da API para maiores informações.

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

Note que quaisquer instâncias associadas ou que contenham coleções, não são selecionados `FOR UPDATE`, a não ser que você decida especificar um `lock` ou `all` como um estilo cascata para a associação.

É possível realizar o recarregamento de um objeto e todas as suas coleções a qualquer momento, usando o método `refresh()`.É útil quando os disparos do banco de dados são usados para inicializar algumas propriedades do 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 Seção 21.1, “Estratégias de Busca ”.

## 11.4. Consultando

Se o identificador do objeto que se está buscando não for conhecido, será necessário realizar uma consulta. O Hibernate suporta uma linguagem de consulta (HQL) orientada a objetos fáceis de usar, porém poderosos. Para criação via programação de consultas, o Hibernate suporta características sofisticadas de consulta por Critério e Exemplo (QBCe QBE). Pode-se também expressar a consulta por meio de SQL nativa do banco de dados, com suporte opcional do Hibernate para conversão do conjunto de resultados em objetos.

### 11.4.1. Executando consultas

Consultas HQL e SQL nativas são representadas por uma instância de `org.hibernate.Query`. Esta interface oferece métodos para associação de parâmetros, tratamento de conjunto de resultados e para a execução de consultas reais. Você pode obter uma `Query` usando a `Session` atual:

    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());

Geralmente uma consulta é executada ao invocar `list()`.O resultado da consulta será carregado completamente em uma coleção na memória. Instâncias de entidades recuperadas por uma consulta estão no estado persistente. O `uniqueResult()` oferece um atalho se você souber previamente, que a consulta retornará apenas um único objeto. Repare que consultas que fazem uso da busca antecipada (eager fetching) de coleções, geralmente retornam duplicatas dos objetos raiz, mas com suas coleções inicializadas. Pode-se filtrar estas duplicatas através de um simples `Set`.

#### 11.4.1.1. Interagindo com resultados

Ocasionalmente, pode-se obter um melhor desempenho com a execução de consultas, usando o método `iterate()`. Geralmente isso acontece apenas se as instâncias das entidades reais retornadas pela consulta já estiverem na sessão ou no cachê de segundo nível. Caso elas ainda não tenham sido armazenadas, `iterate()` será mais devagar do que `list()` e podem ser necessários vários acessos ao banco de dados para uma simples consulta, geralmente 1 para a seleção inicial que retorna apenas identificadores, e n consultas adicionais para inicializar as instâncias reais.

    // 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 retornam tuplas

Algumas vezes as consultas do Hibernate retornam tuplas de objetos. Cada tupla é retornada como uma matriz:

    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

As consultas devem especificar uma propriedade da classe na cláusula `select`. Elas também podem chamar funções SQL de agregações. Propriedades ou agregações são consideradas resultados agregados e não entidades no 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. Parâmetros de vínculo

Métodos em `Consulta` são oferecidos para valores de vínculo para parâmetros nomeados ou de estilo JDBC `?`. Ao contrário do JDBC, o Hibernate numera parâmetros a partir de zero. Parâmetros nomeados são identificadores da forma`:name` na faixa de consulta. As vantagens de parâmetros nomeados são:

- Parâmetros nomeados são insensíveis à ordem que eles ocorrem na faixa de consulta

- eles podem ocorrer em tempos múltiplos na mesma consulta

- eles são auto documentáveis

    //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. Paginação

Se você precisar especificar vínculos do conjunto de resultados, o máximo de números por linha que quiser recuperar e/ou a primeira linha que quiser recuperar, você deve usar métodos de interface `Consulta`:

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

    q.setFirstResult(20);

    q.setMaxResults(10);

    List cats = q.list();

O Hibernate sabe como traduzir esta consulta de limite para a SQL nativa de seu DBMS

#### 11.4.1.6. Iteração rolável

Se seu driver JDBC driver suportar `ResultSet`s roláveis, a interface da `Consulta` poderá ser usada para obter um objeto de `ScrollableResults`, que permite uma navegação flexível dos 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 uma conexão aberta de banco de dados (e cursor) é requerida para esta função, use `setMaxResult()`/`setFirstResult()` se precisar da função de paginação offline.

#### 11.4.1.7. Externando consultas nomeadas

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 Exemplo 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.

Exemplo 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.

Exemplo 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 Exemplo 11.3, “Parameter binding of a named query”.

Exemplo 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();

Note que o código de programa atual é independente da linguagem de consulta que é utilizada, você também pode definir as consultas SQL nativas no metadado, ou migrar consultas existentes para o Hibernate, colocando-os em arquivos de mapeamento.

Observe também que uma declaração de consulta dentro de um elemento `<hibernate-mapping>` requer um nome único global para a consulta, enquanto uma declaração de consulta dentro de um elemento de `<classe>` torna-se único automaticamente, aguardando o nome completo da classe qualificada, por exemplo: `eg.Cat.ByNameAndMaximumWeight`.

### 11.4.2. Filtrando coleções

Uma coleção filter é um tipo especial de consulta que pode ser aplicado a uma coleção persistente ou a uma matriz. A faixa de consulta pode referir-se ao `this`, significando o elemento de coleção atual.

    Collection blackKittens = session.createFilter(

        pk.getKittens(),

        "where this.color = ?")

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

        .list()

    );

A coleção retornada é considerada uma bolsa, e é a cópia da coleção dada. A coleção original não é modificada. Ela é oposta à implicação do nome "filtro", mas é consistente com o comportamento esperado.

Observe que os filtros não requerem uma cláusula `from` embora possam ter um, se requerido. Os filtros não são limitados a retornar aos elementos de coleção.

    Collection blackKittenMates = session.createFilter(

        pk.getKittens(),

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

        .list();

Até mesmo um filtro vazio é útil, ex.: para carregar um subconjunto em uma coleção enorme:

    Collection tenKittens = session.createFilter(

        mother.getKittens(), "")

        .setFirstResult(0).setMaxResults(10)

        .list();

### 11.4.3. Consulta por critério

O HQL é extremamente potente mas alguns desenvolvedores preferem construir consultas de forma dinâmica, utilizando um API de objeto, ao invés de construir faixas de consultas. O Hibernate oferece uma API de consulta de `Critério` intuitiva para estes 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 critérios.

### 11.4.4. Consultas em SQL nativa

Você pode expressar uma consulta em SQL utilizando `createSQLQuery()` e deixar o Hibernate tomar conta do mapeamento desde conjuntos de resultados até objetos. Note que você pode chamar uma `session.connection()` a qualquer momento e usar a `Connection` JDBC diretamente. Se você escolher utilizar a API Hibernate, você deve incluir as aliases SQL dentro de chaves:

    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. Modificando objetos persistentes

Instâncias persistentes transacionais (ou seja, objetos carregados, salvos, criados ou consultados pela `Session`) podem ser manipuladas pela aplicação e qualquer mudança para estado persistente será persistida quando a `Sessão` for limpa. Isto será discutido mais adiante neste capítulo. Não há necessidade de chamar um método em particular (como `update()`, que possui um propósito diferente) para fazer modificações persistentes. Portanto, a forma mais direta de atualizar o estado de um objeto é `carregá-lo()` e depois manipulá-lo diretamente, enquanto a `Sessão` estiver aberta:

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

    cat.setName("PK");

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

Algumas vezes, este modelo de programação é ineficiente, uma vez que ele requer ambos SQL `SELECT` (para carregar um objeto) e um SQL`UPDATE` (para persistir seu estado atualizado) na mesma sessão. Por isso, o Hibernate oferece uma abordagem alternativa, usando instâncias desanexadas.

## 11.6. Modificando objetos desacoplados

Muitas aplicações precisam recuperar um objeto em uma transação, enviá-lo para a camada UI para manipulação e somente então salvar as mudanças em uma nova transação. As aplicações que usam este tipo de abordagem em ambiente de alta concorrência, geralmente usam dados versionados para assegurar isolação durante a "longa" unidade de trabalho.

O Hibernate suporta este modelo, oferecendo re-acoplamentos das instâncias usando os métodos `Session.update()` ou`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

Se o `Cat` com identificador `catId` já tivesse sido carregado pela`segundaSessão` quando a aplicação tentou re-acoplá-lo, teria surgido uma exceção.

Use `update()` se você tiver certeza de que a sessão já não contém uma instância persistente com o mesmo identificador, e `merge()` se você quiser mesclar suas modificações a qualquer momento sem considerar o estado da sessão. Em outras palavras, `update()` é geralmente o primeiro método que você chama em uma nova sessão, assegurando que o re-acoplamento de suas instâncias seja a primeira operação executada.

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 Seção 11.11, “Persistência Transitiva” for more information.

O método `lock()` também permite que um aplicativo re-associe um objeto com uma nova sessão. No entanto, a instância desanexada não pode ser 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()` pode ser usado com diversos `LockMode`s, veja a documentação API e o capítulo sobre manuseio de transações para maiores informações. Re-acoplamento não é o único caso de uso para `lock()`.

Other models for long units of work are discussed in Seção 13.3, “Controle de concorrência otimista”.

## 11.7. Detecção automática de estado

Os usuários de Hibernate solicitaram um método geral, tanto para salvar uma instância transiente, gerando um novo identificador, quanto para atualizar/ re-acoplar as instâncias desanexadas associadas ao seu identificador atual. O método `saveOrUpdate()` implementa esta funcionalidade.

    // 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)

O uso e semântica do `saveOrUpdate()` parecem ser confusos para novos usuários. A princípio, enquanto você não tentar usar instâncias de uma sessão em outra nova sessão, não precisará utilizar `update()`, `saveOrUpdate()`, ou `merge()`. Algumas aplicações inteiras nunca precisarão utilizar estes métodos.

Geralmente, `update()` ou `saveOrUpdate()`são utilizados nos seguintes cenários:

- a aplicação carrega um objeto na primeira sessão

- o objeto é passado para a camada UI

- algumas modificações são feitas ao objeto

- o objeto é retornado à camada lógica de negócios

- a aplicação persiste estas modificações, chamando `update()` em uma segunda sessão.

`saveOrUpdate()` faz o seguinte:

- se o objeto já estiver persistente nesta sessão, não faça nada

- se outro objeto associado com a sessão possuir o mesmo identificador, jogue uma exceção

- se o objeto não tiver uma propriedade de identificador `salve-o()`

- se o identificador do objeto possuir o valor atribuído ao objeto recentemente instanciado, `salve-o()`

- se o objeto for versionado por um `<version>` ou `<timestamp>`, e o valor da propriedade da versão for o mesmo valor atribuído ao objeto recentemente instanciado, `salve()` o mesmo

- do contrário `atualize()` o objeto

e a `mesclagem()` é bastante diferente:

- se existir uma instância persistente com um mesmo identificador associado atualmente com a sessão, copie o estado do objeto dado para a instância persistente.

- se não existir uma instância persistente atualmente associada com a sessão, tente carregá-la a partir do banco de dados, ou crie uma nova instância persistente

- a instância persistente é retornada

- a instância dada não se torna associada com a sessão, ela permanece desanexada

## 11.8. Apagando objetos persistentes

A `Session.delete()` removerá um estado de objeto do banco de dados. É claro que seu aplicativo pode ainda reter uma referência à um objeto apagado. É melhor pensar em `delete()` como fazer uma instância persistente se tornar transiente.

    sess.delete(cat);

Você poderá deletar objetos na ordem que desejar, sem risco de violação de restrição da chave estrangeira. É possível violar uma restrição `NOT NULL` em uma coluna de chave estrangeira, apagando objetos na ordem inversa, ex.: se apagar o pai, mas esquecer de apagar o filho.

## 11.9. Replicando objeto entre dois armazenamentos de dados diferentes.

Algumas vezes é útil poder tirar um gráfico de instâncias persistentes e fazê-los persistentes em um armazenamento de dados diferente, sem gerar novamente valores de identificador.

    //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();

O `ReplicationMode` determina como o `replicate()` irá lidar com conflitos em linhas existentes no banco de dados:

- `ReplicationMode.IGNORE`: ignore o objeto quando houver uma linha de banco de dados existente com o mesmo identificador.

- `ReplicationMode.OVERWRITE`: subscreva uma linha de banco de dados existente com um mesmo identificador.

- `ReplicationMode.EXCEPTION`: jogue uma exceção se houver uma linha de banco de dados existente com o mesmo identificador.

- `ReplicationMode.LATEST_VERSION`: subscreva a linha se seu número de versão for anterior ao número de versão do objeto, caso contrário, ignore o objeto.

O caso de uso para este recurso inclui dados de reconciliação em instâncias de banco de dados diferentes, atualizando informações da configuração do sistema durante a atualização do produto, retornando mudanças realizadas durante transações não ACID entre outras funções.

## 11.10. Limpando a Sessão

De vez em quando, a `Session` irá executar as instruções SQL, necessárias para sincronizar o estado de conexão JDBC com o estado de objetos mantidos na memória. Este processo de flush, ocorre por padrão nos seguintes pontos:

- antes de algumas execuções de consultas

- a partir de`org.hibernate.Transaction.commit()`

- a partir de `Session.flush()`

As instruções SQL são editadas na seguinte ordem:

1. todas as inserções de entidade, na mesma ordem que os objetos correspondentes foram salvos usando `Session.save()`

2. todas as atualizações de entidades

3. todas as deleções de coleções

4. todas as deleções, atualizações e inserções de elementos de coleção.

5. todas as inserções de coleção

6. todas as deleções de entidade, na mesma ordem que os objetos correspondentes foram deletados usando `Session.delete()`

Uma exceção é que o objeto que utiliza a geração de ID `native` é inserido quando salvo.

Exceto quando você explicitamente `limpar()`, não há nenhuma garantia sobre quando a `Sessão` executará as chamadas de JDBC, somente se sabe a ordem na qual elas são executadas. No entanto, o Hibernate garante que a `Query.list(..)` nunca retornará dados antigos, nem retornará dados errados.

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 Seção 13.3.2, “Sessão estendida e versionamento 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, Transações e Concorrência .

## 11.11. Persistência Transitiva

É um tanto incômodo salvar, deletar ou reanexar objetos individuais, especialmente ao lidar com um grafo de objetos associados. Um caso comum é um relacionamento pai/filho. Considere o seguinte exemplo:

Se os filhos em um relacionamento pai/filho fossem do tipo valor (ex.: coleção de endereços ou strings), seus ciclos de vida dependeriam do pai e nenhuma ação seria requerida para "cascateamento" de mudança de estado. Quando o pai é salvo, os objetos filho de valor são salvos também, quando o pai é deletado, os filhos também serão deletados, etc. Isto funciona até para operações como remoção de filho da coleção. O Hibernate irá detectar isto e como objetos de valor não podem ter referências compartilhadas, irá deletar o filho do banco de dados.

Agora considere o mesmo cenário com objeto pai e filho sendo entidades, e não de valores (ex.: categorias e ítens, ou cats pai e filho). As entidades possuem seus próprios ciclos de vida, suportam referências compartilhadas (portanto, remover uma entidade da coleção não significa que possa ter sido deletada), e não existe efeito cascata de estado, por padrão, a partir de uma entidade para outras entidades associadas. O Hibernate não implementa persistência por alcance por padrão.

Para cada operação básica da sessão do Hibernate, incluindo`persistir(), mesclar(), salvarOuAtualizar(), deletar(), bloquear(), atualizar(), despejar(), replicar()`, existe um estilo cascata correspondente. Respectivamente, os estilos cascatas são nomeados `criar, mesclar, salvar-atualizar, deletar, bloquiar, atualizar, despejar, replicar`. Se desejar uma operação em cascata junto a associação, você deverá indicar isto no documento de mapeamento. Por exemplo:

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

Estilo cascata pode ser combinado:

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

Você pode até utilizar `cascade="all"` para especificar que todas as operações devem estar em cascata junto à associação. O padrão `cascade="none"` especifica que nenhuma operação deve estar em cascata.

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` Exemplo 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.

Exemplo 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

Recomendações:

- 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)`)`.

- Caso contrário, você pode não precisar realizar a cascata. Mas se você achar que irá trabalhar com o pai e filho juntos com freqüência, na mesma transação, e quiser salvar você mesmo, considere o uso do `cascata="persistir,mesclar,salvar-atualizar"`.

Ao mapear uma associação (tanto uma associação de valor único como uma coleção) com `casca de="all"`, a associação é demarcada como um relacionamento de estilo parent/child onde salvar/atualizar/deletar do pai, resulta em salvar/atualizar/deletar do(s) filho(s).

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:

- Se um pai é passado para `persist()`, todos os filhos são passados para `persist()`

- Se um pai é passado para `merge()`, todos os filhos são passados para `merge()`

- Se um pai for passado para `save()`, `update()` ou `saveOrUpdate()`, todos os filhos passarão para `saveOrUpdate()`

- Se um filho transiente ou desanexado se tornar referenciado pelo pai persistente, ele será passado para `saveOrUpdate()`

- Se um pai for deletado, todos os filhos serão passados para `delete()`

- Se um filho for diferenciado pelo pai persistente, nada de especial acontece - a aplicação deve explicitamente deletar o filho se necessário, a não ser que `casca de="delete-orphan"`, nos quais casos o filho "órfão" é deletado.

Finalmente, note que o cascateamento das operações podem ser aplicados a um grafo de objeto em tempo de chamada ou em tempo de limpeza. Todas as operações, se habilitadas, são colocadas em cascata para entidades associadas atingíveis quando a operação for executada. No entanto, `save-upate` e `delete-orphan` são transitivas para todas as entidades associadas atingíveis durante a limpeza da `Sessão`.

## 11.12. Usando metadados

O Hibernate requer um modelo muito rico, em nível de metadados, de todas as entidades e tipos de valores. De tempos em tempos, este modelo é muito útil à própria aplicação. Por exemplo, a aplicação pode usar os metadados do Hibernate, que executa um algoritmo "inteligente", que compreende quais objetos podem ser copiados (por exemplo, tipos de valores mutáveis) ou não (por exemplo, tipos de valores imutáveis e, possivelmente, entidades associadas).

O Hibernate expõe os metadados via interfaces `ClassMetadata` e `CollectionMetadata` e pela hierarquia `Type`. Instâncias das interfaces de metadados podem ser obtidas a partir do `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. Mapeamento de Herança
- Acima
- Principal
- PróximaCapítulo 12. Read-only entities