Hibernate.org Community Documentation

- Anterior
- Próxima

## Capítulo 1. Tutorial

Intencionado para novos usuários, este capítulo fornece uma introdução detalhada do Hibernate, começando com um aplicativo simples usando um banco de dados em memória. O tutorial é baseado num tutorial anterior desenvolvido por Michael Gloegl. Todo o código está contido no diretório `tutorials/web` da fonte do projeto.

## Importante

Este tutorial espera que o usuário tenha conhecimento de ambos Java e SQL. Caso você tenha um conhecimento limitado do JAVA ou SQL, é recomendado que você inicie com uma boa introdução àquela tecnologia, antes de tentar entender o Hibernate.

## Nota

Esta distribuição contém outro aplicativo de amostra sob o diretório de fonte do projeto `tutorial/eg`.

## 1.1. Parte 1 – A primeira aplicação Hibernate

Vamos supor que precisemos de uma aplicação com um banco de dados pequeno que possa armazenar e atender os eventos que queremos, além das informações sobre os hosts destes eventos.

## Nota

Mesmo que usando qualquer banco de dados do qual você se sinta confortável, nós usaremos HSQLDB (o em memória, banco de dados Java) para evitar a descrição de instalação/configuração de quaisquer servidores do banco de dados.

### 1.1.1. Configuração

O primeiro passo em que precisamos tomar é configurar o ambiente de desenvolvimento. Nós usaremos o "layout padrão" suportado por muitas ferramentas de construção, tais como Maven. Maven, em particular, possui um excelente recurso de descrição disto layout. Assim como este tutorial deve ser um aplicativo da web, nós criaremos e faremos uso dos diretórios `src/main/java`, `src/main/resources` e `src/main/webapp`.

Nós usaremos Maven neste tutorial, tirando vantagem destas capacidades de dependência transitiva assim como a habilidade de muitos IDEs de configurar automaticamente um projeto baseado no descritor maven.

    <project xmlns="http://maven.apache.org/POM/4.0.0"

             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

        <modelVersion>4.0.0</modelVersion>

        <groupId>org.hibernate.tutorials</groupId>

        <artifactId>hibernate-tutorial</artifactId>

        <version>1.0.0-SNAPSHOT</version>

        <name>First Hibernate Tutorial</name>

        <build>

             <!-- we dont want the version to be part of the generated war file name -->

             <finalName>${artifactId}</finalName>

        </build>

        <dependencies>

            <dependency>

                <groupId>org.hibernate</groupId>

                <artifactId>hibernate-core</artifactId>

            </dependency>

            <!-- Because this is a web app, we also have a dependency on the servlet api. -->

            <dependency>

                <groupId>javax.servlet</groupId>

                <artifactId>servlet-api</artifactId>

            </dependency>

            <!-- Hibernate uses slf4j for logging, for our purposes here use the simple backend -->

            <dependency>

                <groupId>org.slf4j</groupId>

                <artifactId>slf4j-simple</artifactId>

            </dependency>

            <!-- Hibernate gives you a choice of bytecode providers between cglib and javassist -->

            <dependency>

                <groupId>javassist</groupId>

                <artifactId>javassist</artifactId>

            </dependency>

        </dependencies>

    </project>

## Dica

It is not a requirement to use Maven. If you wish to use something else to build this tutorial (such as Ant), the layout will remain the same. The only change is that you will need to manually account for all the needed dependencies. If you use something like Ivy providing transitive dependency management you would still use the dependencies mentioned below. Otherwise, you'd need to grab all dependencies, both explicit and transitive, and add them to the project's classpath. If working from the Hibernate distribution bundle, this would mean `hibernate3.jar`, all artifacts in the `lib/required` directory and all files from either the `lib/bytecode/cglib` or `lib/bytecode/javassist` directory; additionally you will need both the servlet-api jar and one of the slf4j logging backends.

Salve este arquivo como `pom.xml` no diretório raiz do projeto.

### 1.1.2. A primeira Classe

Agora, iremos criar uma classe que representa o evento que queremos armazenar na base de dados. Isto é uma classe JavaBean simples com algumas propriedades:

    package org.hibernate.tutorial.domain;

    import java.util.Date;

    public class Event {

        private Long id;

        private String title;

        private Date date;

        public Event() {}

        public Long getId() {

            return id;

        }

        private void setId(Long id) {

            this.id = id;

        }

        public Date getDate() {

            return date;

        }

        public void setDate(Date date) {

            this.date = date;

        }

        public String getTitle() {

            return title;

        }

        public void setTitle(String title) {

            this.title = title;

        }

    }

Você pode ver que esta classe usa o padrão JavaBean para o nome convencional dos métodos de propriedade getter e setter, como também a visibilidade privada dos campos. Este é um padrão de projeto recomendado, mas não requerido. O Hibernate pode também acessar campos diretamente, o benefício para os métodos de acesso é a robustez para o refactoring.

A propriedade `id` mantém um único valor de identificação para um evento particular. Todas as classes persistentes da entidade (bem como aquelas classes dependentes de menos importância) precisam de uma propriedade de identificação, caso nós queiramos usar o conjunto completo de características do Hibernate. De fato, a maioria das aplicações, especialmente. aplicações web, precisam distinguir os objetos pelo identificador. Portanto, você deverá considerar esta, uma característica ao invés de uma limitação. Porém, nós normalmente não manipulamos a identidade de um objeto, conseqüentemente o método setter deverá ser privado. O Hibernate somente nomeará os identificadores quando um objeto for salvo. O Hibernate pode acessar métodos públicos, privados, e protegidos, como também campos públicos, privados, protegidos diretamente. A escolha é sua e você pode adaptar seu projeto de aplicação.

O construtor sem argumentos é um requerimento para todas as classes persistentes; O Hibernate precisa criar para você os objetos usando Java Reflection. O construtor pode ser privado, porém, a visibilidade do pacote é requerida para a procuração da geração em tempo de execução e recuperação eficiente dos dados sem a instrumentação de bytecode.

Salve este arquivo no diretório `src/main/java/org/hibernate/tutorial/domain`.

### 1.1.3. O mapeamento do arquivo

O Hibernate precisa saber como carregar e armazenar objetos da classe de persistência. É aqui que o mapeamento do arquivo do Hibernate entrará em jogo. O arquivo mapeado informa ao Hibernate, qual tabela no banco de dados ele deverá acessar, e quais as colunas na tabela ele deverá usar.

A estrutura básica de um arquivo de mapeamento é parecida com:

    <?xml version="1.0"?>

    <!DOCTYPE hibernate-mapping PUBLIC

            "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

            "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

    <hibernate-mapping package="org.hibernate.tutorial.domain">

    [...]

    </hibernate-mapping

    >

Note que o Hibernate DTD é muito sofisticado. Você pode usar isso para auto-conclusão no mapeamento XML dos elementos e funções no seu editor ou IDE. Você também pode abrir o arquivo DTD no seu editor. Esta é a maneira mais fácil de ter uma visão geral de todos os elementos e funções e dos padrões, como também alguns comentários. Note que o Hibernate não irá carregar o arquivo DTD da web, e sim da classpath da aplicação. O arquivo DTD está incluído no `hibernate-core.jar` (como também no `hibernate3.jar`, caso usando a vinculação de distribuição.

## Importante

Nós omitiremos a declaração do DTD nos exemplos futuros para encurtar o código. Isto, é claro, não é opcional.

Entre as duas tags `hibernate-mapping`, inclua um elemento `class`. Todas as classes persistentes da entidade (novamente, poderá haver mais tarde, dependências sobre as classes que não são classes-primárias de entidades) necessitam do tal mapeamento, para uma tabela na base de dados SQL:

    <hibernate-mapping package="org.hibernate.tutorial.domain">

        <class name="Event" table="EVENTS">

        </class>

    </hibernate-mapping>

Até agora, informamos o Hibernate sobre como fazer para persistir e carregar objetos da classe `Event` da tabela `EVENTS`, cada instância representada por uma coluna na tabela. Agora, continuaremos com o mapeamento de uma única propriedade identificadora para as chaves primárias da tabela. Além disso, como não precisamos nos preocupar em manipular este identificador, iremos configurar uma estratégia de geração de id’s do Hibernate para uma coluna de chave primária substituta:

    <hibernate-mapping package="org.hibernate.tutorial.domain">

        <class name="Event" table="EVENTS">

            <id name="id" column="EVENT_ID">

                <generator class="native"/>

            </id>

        </class>

    </hibernate-mapping>

O elemento `id` é a declaração de uma propriedade do identificador. O atributo do mapeamento `name="id"` declara que o nome da propriedade JavaBeans e informa o Hibernate a utilizar os métodos `getId()` and `setId()` para acessar a propriedade. A atributo da coluna informa o Hibernate qual coluna da tabela `EVENTS` mantém o valor de chave primária.

O elemento `generator` aninhado especifica a estratégia da geração do identificador (como os valores do identificador são gerados?). Neste caso, nós escolhemos `native`, do qual oferece um nível de portabilidade dependendo no dialeto do banco de dados configurado. O Hibernate suporta o banco de dados gerado, globalmente único, assim como a aplicação determinada, identificadores. A geração do valor do identificador é também um dos muitos pontos de extensão do Hibernate e você pode realizar o plugin na sua própria estratégia.

## Dica

`native` is no longer consider the best strategy in terms of portability. for further discussion, see Seção 28.4, “Geração do identificador”

Finalmente, incluiremos as declarações para as propriedades persistentes da classe no arquivo mapeado. Por padrão, nenhuma das propriedades da classe é considerada persistente:

    <hibernate-mapping package="org.hibernate.tutorial.domain">

        <class name="Event" table="EVENTS">

            <id name="id" column="EVENT_ID">

                <generator class="native"/>

            </id>

            <property name="date" type="timestamp" column="EVENT_DATE"/>

            <property name="title"/>

        </class>

    </hibernate-mapping>

Assim como com o elemento `id`, a função `name` do elemento `property` informa ao Hibernate qual método getter e setter deverá usar. Assim, neste caso, o Hibernate irá procurar pelos métodos `getDate()`, `setDate()`, `getTitle()` e `setTitle()`.

## Nota

Porque fazer o mapeamento da propriedade `date` incluído na função `column`, e no `title` não fazer? Sem a função `column` o Hibernate, por padrão, utiliza o nome da propriedade como o nome da coluna. Isto funciona bem para o `title`. Entretanto, o `date` é uma palavra-chave reservada na maioria dos bancos de dados, por isso seria melhor mapeá-lo com um nome diferente.

O mapeamento do `title` também não possui a função `type`. O tipo que declaramos e utilizamos nos arquivos mapeados, não são como você esperava, ou seja, funções de dados Java. Eles também não são como os tipos de base de dados SQL. Esses tipos podem ser chamados de Tipos de mapeamento Hibernate, que são conversores que podem traduzir tipos de dados do Java para os tipos de dados SQL e vice-versa. Novamente, o Hibernate irá tentar determinar a conversão correta e mapeará o `type` próprio, caso o tipo da função não estiver presente no mapeamento. Em alguns casos, esta detecção automática (que usa Reflection sobre as classes Java) poderá não ter o padrão que você espera ou necessita. Este é o caso com a propriedade `date`. O Hibernate não sabe se a propriedade, que é do `java.util.Date`, pode mapear para uma coluna do tipo `date` do SQL, `timestamp` ou `time`. Nós preservamos as informações sobre datas e horas pelo mapeamento da propriedade com um conversor `timestamp`.

## Dica

O Hibernate realiza esta determinação de tipo de mapeamento usando a reflexão quando os arquivos de mapeamentos são processados. Isto pode levar tempo e recursos, portanto se você inicializar o desempenho, será importante que você considere claramente a definição do tipo para uso.

Salve este arquivo de mapeamento como `src/main/resources/org/hibernate/tutorial/domain/Event.hbm.xml`.

### 1.1.4. Configuração do Hibernate

Nestas alturas, você deve possuir a classe persistente e seu arquivo de mapeamento prontos. É o momento de configurar o Hibernate. Primeiro, vamos configurar o HSQLDB para rodar no "modo do servidor".

## Nota

Nós realizamos isto para que aqueles dados permaneçam entre as execuções.

Nós utilizaremos o Maven exec plugin para lançar o servidor HSQLDB pela execução: `mvn exec:java -Dexec.mainClass="org.hsqldb.Server" -Dexec.args="-database.0 file:target/data/tutorial"`. Você pode ver ele iniciando e vinculando ao soquete TCP/IP, aqui será onde nossa aplicação irá se conectar depois. Se você deseja iniciar uma nova base de dados durante este tutorial, finalize o HSQLDB, delete todos os arquivos no diretório `target/data`, e inicie o HSQLBD novamente.

O Hibernate conectará ao banco de dados no lugar de sua aplicação, portanto ele precisará saber como obter as conexões. Para este tutorial nós usaremos um pool de conexão autônomo (ao invés de `javax.sql.DataSource`). O Hibernate vem com o suporte para dois terços dos pools de conexão JDBC de código aberto: c3p0 e proxool. No entanto, nós usaremos o pool de conexão interna do Hibernate para este tutorial.

## Cuidado

O pool de conexão interna do Hibernate não é recomendado para uso de produção. Ele possui deficiência em diversos recursos encontrados em qualquer pool de conexão apropriado.

Para as configurações do Hibernate, nós podemos usar um arquivo simples `hibernate.properties`, um arquivo mais sofisticado `hibernate.cfg.xml` ou até mesmo uma instalação programática completa. A maioria dos usuários prefere utilizar o arquivo de configuração XML:

    <?xml version='1.0' encoding='utf-8'?>

    <!DOCTYPE hibernate-configuration PUBLIC

            "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

            "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

    <hibernate-configuration>

        <session-factory>

            <!-- Database connection settings -->

            <property name="connection.driver_class"

    >org.hsqldb.jdbcDriver</property>

            <property name="connection.url"

    >jdbc:hsqldb:hsql://localhost</property>

            <property name="connection.username"

    >sa</property>

            <property name="connection.password"

    ></property>

            <!-- JDBC connection pool (use the built-in) -->

            <property name="connection.pool_size"

    >1</property>

            <!-- SQL dialect -->

            <property name="dialect"

    >org.hibernate.dialect.HSQLDialect</property>

            <!-- Enable Hibernate's automatic session context management -->

            <property name="current_session_context_class"

    >thread</property>

            <!-- Disable the second-level cache  -->

            <property name="cache.provider_class"

    >org.hibernate.cache.NoCacheProvider</property>

            <!-- Echo all executed SQL to stdout -->

            <property name="show_sql"

    >true</property>

            <!-- Drop and re-create the database schema on startup -->

            <property name="hbm2ddl.auto"

    >update</property>

            <mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>

        </session-factory>

    </hibernate-configuration

    >

## Nota

Perceba que este arquivo de configuração especifica um DTD diferente

Configure a `SessionFactory` do Hibernate. A SessionFactory é uma fábrica global responsável por uma base de dados particular. Se você tiver diversas bases de dados, use diversas configurações `<session-factory>`, geralmente em diversos arquivos de configuração, para uma inicialização mais fácil.

Os primeiros quatro elementos `property` contêm a configuração necessária para a conexão JBDC. O elemento `property` do dialeto especifica a variante do SQL particular que o Hibernate gera.

## Dica

In most cases, Hibernate is able to properly determine which dialect to use. See Seção 28.3, “Resolução do Dialeto” for more information.

O gerenciamento automático de sessão do Hibernate para contextos de persistência é bastante útil neste contexto. A opção `hbm2ddl.auto` habilita a geração automática de esquemas da base de dados, diretamente na base de dados. Isto também pode ser naturalmente desligado apenas removendo a opção de configuração ou redirecionado para um arquivo com ajuda do `SchemaExport` na tarefa do Ant. Finalmente, iremos adicionar os arquivos das classes de persistência mapeadas na configuração.

Salve este arquivo como `hibernate.cfg.xml` no diretório `src/main/resources`.

### 1.1.5. Construindo com o Maven

Nós iremos construir agora o tutorial com Maven. Você necessitará que o Maven esteja instalado; ele está disponível a partir do Maven download page. O Maven gravará o arquivo `/pom.xml` que criamos anteriormente, além de saber como executar algumas tarefas do projeto básico. Primeiro, vamos rodar o objetivo `compile` para nos certificarmos de que tudo foi compilado até agora:

    [hibernateTutorial]$ mvn compile
    [INFO] Scanning for projects...
    [INFO] ------------------------------------------------------------------------
    [INFO] Building First Hibernate Tutorial
    [INFO]    task-segment: [compile]
    [INFO] ------------------------------------------------------------------------
    [INFO] [resources:resources]
    [INFO] Using default encoding to copy filtered resources.
    [INFO] [compiler:compile]
    [INFO] Compiling 1 source file to /home/steve/projects/sandbox/hibernateTutorial/target/classes
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESSFUL
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 2 seconds
    [INFO] Finished at: Tue Jun 09 12:25:25 CDT 2009
    [INFO] Final Memory: 5M/547M
    [INFO] ------------------------------------------------------------------------

### 1.1.6. Inicialização e Auxiliares

É hora de carregar e armazenar alguns objetos `Event`, mas primeiro nós temos de completar a instalação com algum código de infraestrutura. Você precisa inicializar o Hibernate pela construção de um objeto `org.hibernate.SessionFactory` global e o armazenamento dele em algum lugar de fácil acesso para o código da aplicação. O `org.hibernate.SessionFactory` é usado para obter instâncias `org.hibernate.Session`. O `org.hibernate.Session` representa uma unidade de single-threaded de trabalho. O `org.hibernate.SessionFactory` é um objeto global thread-safe, instanciado uma vez.

Criaremos uma classe de ajuda `HibernateUtil`, que cuida da inicialização e faz acesso a uma `org.hibernate.SessionFactory` mais conveniente.

    package org.hibernate.tutorial.util;

    import org.hibernate.SessionFactory;

    import org.hibernate.cfg.Configuration;

    public class HibernateUtil {

        private static final SessionFactory sessionFactory = buildSessionFactory();

        private static SessionFactory buildSessionFactory() {

            try {

                // Create the SessionFactory from hibernate.cfg.xml

                return new Configuration().configure().buildSessionFactory();

            }

            catch (Throwable ex) {

                // Make sure you log the exception, as it might be swallowed

                System.err.println("Initial SessionFactory creation failed." + ex);

                throw new ExceptionInInitializerError(ex);

            }

        }

        public static SessionFactory getSessionFactory() {

            return sessionFactory;

        }

    }

Salve este código como `src/main/java/org/hibernate/tutorial/util/HibernateUtil.java`

Esta classe não só produz uma referência `org.hibernate.SessionFactory` global em seu inicializador estático, mas também esconde o fato de que utiliza um autônomo estático. Nós poderemos buscar pela referência `org.hibernate.SessionFactory` a partir do JNDI no servidor da aplicação ou qualquer outra localização para este assunto.

Se você der um nome à `SessionFactory` em seu arquivo de configuração, o Hibernate irá, de fato, tentar vinculá-lo ao JNDI sob aquele nome, depois que estiver construído. Outra opção melhor seria usar a implementação JMX e deixar o recipiente JMX capaz, instanciar e vincular um `HibernateService` ao JNDI. Essas opções avançadas são discutidas no documento de referência do Hibernate. Tais opções avançadas serão discutidas mais tarde.

Você precisará agora configurar um sistema de logging. O Hibernate usa logging comuns e lhe oferece a escolha entre o Log4j e o logging do JDK 1.4 . A maioria dos desenvolvedores prefere o Log4j: copie `log4j.properties` da distribuição do Hibernate no diretório `etc/`, para seu diretório `src`, depois vá em `hibernate.cfg.xml`. Dê uma olhada no exemplo de configuração e mude as configurações se você quiser ter uma saída mais detalhada. Por padrão, apenas as mensagens de inicialização do Hibernate são mostradas no stdout.

O tutorial de infra-estrutura está completo e nós já estamos preparados para algum trabalho de verdade com o Hibernate.

### 1.1.7. Carregando e salvando objetos

We are now ready to start doing some real work with Hibernate. Let's start by writing an `EventManager` class with a `main()` method:

    package org.hibernate.tutorial;

    import org.hibernate.Session;

    import java.util.*;

    import org.hibernate.tutorial.domain.Event;

    import org.hibernate.tutorial.util.HibernateUtil;

    public class EventManager {

        public static void main(String[] args) {

            EventManager mgr = new EventManager();

            if (args[0].equals("store")) {

                mgr.createAndStoreEvent("My Event", new Date());

            }

            HibernateUtil.getSessionFactory().close();

        }

        private void createAndStoreEvent(String title, Date theDate) {

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();

            session.beginTransaction();

            Event theEvent = new Event();

            theEvent.setTitle(title);

            theEvent.setDate(theDate);

            session.save(theEvent);

            session.getTransaction().commit();

        }

    }

Em `createAndStoreEvent()`, criamos um novo objeto de `Event`, e passamos para o Hibernate. O Hibernate sabe como tomar conta do SQL e executa `INSERT`s no banco de dados.

A org.hibernate.Session is designed to represent a single unit of work (a single atomic piece of work to be performed). For now we will keep things simple and assume a one-to-one granularity between a Hibernate org.hibernate.Session and a database transaction. To shield our code from the actual underlying transaction system we use the Hibernate `org.hibernate.Transaction` API. In this particular case we are using JDBC-based transactional semantics, but it could also run with JTA.

O que a `sessionFactory.getCurrentSession()` faz? Primeiro, você pode chamar quantas vezes e de onde quiser, assim que você receber sua `org.hibernate.SessionFactory`. O método `getCurrentSession()` sempre retorna à unidade de trabalho "atual". Você se lembra que nós mudamos a opção de configuração desse mecanismo para "thread" em nosso `src/main/resources/hibernate.cfg.xml`? Devido a esta configuração, o contexto de uma unidade de trabalho atual estará vinculada à thread Java atual que executa nossa aplicação.

## Importante

O Hibernate oferece três métodos da sessão atual. O método "thread" baseado não possui por interesse o uso de produção; ele é basicamente útil para prototyping e tutoriais tais como este. A sessão atual será discutida em mais detalhes mais tarde.

Um org.hibernate.Session começa quando for necessária, quando é feita a primeira chamada à `getCurrentSession()`. É então limitada pelo Hibernate para a thread atual. Quando a transação termina, tanto com commit quanto rollback, o Hibernate também desvincula a `Session` da thread e fecha isso pra você. Se você chamar `getCurrentSession()` novamente, você receberá uma nova `Session` e poderá começar uma nova unidade de trabalho.

Em relação ao escopo da unidade de trabalho, o Hibernate org.hibernate.Session deve ser utilizado para executar uma ou mais operações do banco de dados? O exemplo acima utiliza uma `Session` para cada operação. Isto é pura coincidência, o exemplo simplesmente não é complexo o bastante para mostrar qualquer outra abordagem. O escopo de um Hibernate org.hibernate.Session é flexível, mas você nunca deve configurar seu aplicativo para utilizar um novo Hibernate org.hibernate.Session para aoperação de banco de dados every. Portanto, mesmo que você o veja algumas vezes mais nos seguintes exemplos, considere session-per-operation como um anti-modelo. Um aplicativo da web real será demonstrado mais adiante neste tutorial.

See Capítulo 13, Transações e Concorrência for more information about transaction handling and demarcation. The previous example also skipped any error handling and rollback.

Para rodar isto, nós faremos uso do Maven exec plugin para chamar nossa classe com a instalação do classpath necessária: `mvn exec:java -Dexec.mainClass="org.hibernate.tutorial.EventManager" -Dexec.args="store"`

## Nota

Você precisa executar o `mvn compile` primeiramente.

Você deverá ver, após a compilação, a inicialização do Hibernate e, dependendo da sua configuração, muito log de saída. No final, você verá a seguinte linha:

    [java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)

Este é o `INSERT` executado pelo Hibernate.

Adicionamos uma opção para o método principal com o objetivo de listar os eventos arquivados:

            if (args[0].equals("store")) {

                mgr.createAndStoreEvent("My Event", new Date());

            }

            else if (args[0].equals("list")) {

                List events = mgr.listEvents();

                for (int i = 0; i < events.size(); i++) {

                    Event theEvent = (Event) events.get(i);

                    System.out.println(

                            "Event: " + theEvent.getTitle() + " Time: " + theEvent.getDate()

                    );

                }

            }

Nos também adicionamos um novo `listEvents() method is also added`:

        private List listEvents() {

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();

            session.beginTransaction();

            List result = session.createQuery("from Event").list();

            session.getTransaction().commit();

            return result;

        }

Here, we are using a Hibernate Query Language (HQL) query to load all existing `Event` objects from the database. Hibernate will generate the appropriate SQL, send it to the database and populate `Event` objects with the data. You can create more complex queries with HQL. See Capítulo 16, HQL: A Linguagem de Consultas do Hibernate for more information.

Agora podemos chamar nossa nova funcionalidade usando, novamente, o Maven exec plugin: `mvn exec:java -Dexec.mainClass="org.hibernate.tutorial.EventManager" -Dexec.args="list"`

## 1.2. Parte 2 - Mapeando associações

Nós mapeamos uma classe de entidade de persistência para uma tabela. Agora vamos continuar e adicionar algumas associações de classe. Primeiro iremos adicionar pessoas à nossa aplicação e armazenar os eventos em que elas participam.

### 1.2.1. Mapeando a classe Person

O primeira parte da classe `Person` parece-se com isto:

    package org.hibernate.tutorial.domain;

    public class Person {

        private Long id;

        private int age;

        private String firstname;

        private String lastname;

        public Person() {}

        // Accessor methods for all properties, private setter for 'id'

    }

Salve isto ao arquivo nomeado `src/main/java/org/hibernate/tutorial/domain/Person.java`

Após isto, crie um novo arquivo de mapeamento como `src/main/resources/org/hibernate/tutorial/domain/Person.hbm.xml`

    <hibernate-mapping package="org.hibernate.tutorial.domain">

        <class name="Person" table="PERSON">

            <id name="id" column="PERSON_ID">

                <generator class="native"/>

            </id>

            <property name="age"/>

            <property name="firstname"/>

            <property name="lastname"/>

        </class>

    </hibernate-mapping>

Finalmente, adicione o novo mapeamento à configuração do Hibernate:

    <mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>

    <mapping resource="org/hibernate/tutorial/domain/Person.hbm.xml"/>

Crie agora uma associação entre estas duas entidades. As pessoas (Person) podem participar de eventos, e eventos possuem participantes. As questões de design com que teremos de lidar são: direcionalidade, multiplicidade e comportamento de coleção.

### 1.2.2. Uma associação unidirecional baseada em Configuração

Iremos adicionar uma coleção de eventos na classe `Person`. Dessa forma, poderemos navegar pelos eventos de uma pessoa em particular, sem executar uma consulta explicitamente, apenas chamando `Person#getEvents`. As associações de valores múltiplos são representadas no Hibernate por um dos contratos do Java Collection Framework; aqui nós escolhemos um `java.util.Set`, uma vez que a coleção não conterá elementos duplicados e a ordem não é relevante em nossos exemplos:

    public class Person {

        private Set events = new HashSet();

        public Set getEvents() {

            return events;

        }

        public void setEvents(Set events) {

            this.events = events;

        }

    }

Antes de mapearmos esta associação, pense no outro lado. Claramente, poderíamos apenas fazer isto de forma unidirecional. Ou poderíamos criar outra coleção no `Event`, se quisermos navegar de ambas direções. Isto não é necessário, de uma perspectiva funcional. Você poderá sempre executar uma consulta explícita para recuperar os participantes de um evento em particular. Esta é uma escolha de design que cabe a você, mas o que é claro nessa discussão é a multiplicidade da associação: "muitos" válidos em ambos os lados, nós chamamos isto de uma associação muitos-para-muitos. Daqui pra frente, usaremos o mapeamento muitos-para-muitos do Hibernate:

    <class name="Person" table="PERSON">

        <id name="id" column="PERSON_ID">

            <generator class="native"/>

        </id>

        <property name="age"/>

        <property name="firstname"/>

        <property name="lastname"/>

        <set name="events" table="PERSON_EVENT">

            <key column="PERSON_ID"/>

            <many-to-many column="EVENT_ID" class="Event"/>

        </set>

    </class>

O Hibernate suporta todo tipo de mapeamento de coleção, sendo um `set` mais comum. Para uma associação muitos-para-muitos ou relacionamento de entidade n:m, é necessária uma tabela de associação. Cada linha nessa tabela representa um link entre uma pessoa e um evento. O nome da tabela é configurado com a função `table` do elemento `set`. O nome da coluna identificadora na associação, pelo lado da pessoa, é definido com o elemento `key`, o nome da coluna pelo lado dos eventos, é definido com a função `column` do `many-to-many`. Você também precisa dizer para o Hibernate a classe dos objetos na sua coleção (a classe do outro lado das coleções de referência).

O esquema de mapeamento para o banco de dados está a seguir:

        _____________        __________________
       |             |      |                  |       _____________
       |   EVENTS    |      |   PERSON_EVENT   |      |             |
       |_____________|      |__________________|      |    PERSON   |
       |             |      |                  |      |_____________|
       | *EVENT_ID   | <--> | *EVENT_ID        |      |             |
       |  EVENT_DATE |      | *PERSON_ID       | <--> | *PERSON_ID  |
       |  TITLE      |      |__________________|      |  AGE        |
       |_____________|                                |  FIRSTNAME  |
                                                      |  LASTNAME   |
                                                      |_____________|

### 1.2.3. Trabalhando a associação

Vamos reunir algumas pessoas e eventos em um novo método na classe `EventManager`:

        private void addPersonToEvent(Long personId, Long eventId) {

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();

            session.beginTransaction();

            Person aPerson = (Person) session.load(Person.class, personId);

            Event anEvent = (Event) session.load(Event.class, eventId);

            aPerson.getEvents().add(anEvent);

            session.getTransaction().commit();

        }

Após carregar um `Person` e um `Event`, simplesmente modifique a coleção usando os métodos normais de uma coleção. Como você pode ver, não há chamada explícita para `update()` ou `save()`; o Hibernate detecta automaticamente que a coleção foi modificada e que necessita ser atualizada. Isso é chamado de checagem suja automática, e você também pode usá-la modificando o nome ou a data de qualquer um dos seus objetos. Desde que eles estejam no estado persistent, ou seja, limitado por uma `Session` do Hibernate em particular, o Hibernate monitora qualquer alteração e executa o SQL em modo de gravação temporária. O processo de sincronização do estado da memória com o banco de dados, geralmente apenas no final de uma unidade de trabalho, normalmente apenas no final da unidade de trabalho, é chamado de flushing. No nosso código, a unidade de trabalho termina com o commit , ou rollback, da transação do banco de dados.

Você pode também querer carregar pessoas e eventos em diferentes unidades de trabalho. Ou você modifica um objeto fora de um `org.hibernate.Session`, quando não se encontra no estado persistente (se já esteve neste estado anteriormente, chamamos esse estado de detached). Você pode até mesmo modificar uma coleção quando esta se encontrar no estado detached:

        private void addPersonToEvent(Long personId, Long eventId) {

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();

            session.beginTransaction();

            Person aPerson = (Person) session

                    .createQuery("select p from Person p left join fetch p.events where p.id = :pid")

                    .setParameter("pid", personId)

                    .uniqueResult(); // Eager fetch the collection so we can use it detached

            Event anEvent = (Event) session.load(Event.class, eventId);

            session.getTransaction().commit();

            // End of first unit of work

            aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached

            // Begin second unit of work

            Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();

            session2.beginTransaction();

            session2.update(aPerson); // Reattachment of aPerson

            session2.getTransaction().commit();

        }

A chamada `update` cria um objeto persistente novamente, pode-se dizer que ele liga o objeto a uma nova unidade de trabalho, assim qualquer modificação que você faça neste objeto enquanto estiver no estado desanexado pode ser salvo no banco de dados. Isso inclui qualquer modificação (adição/exclusão) que você faça em uma coleção da entidade deste objeto.

Bem, isso não é de grande utilidade na nossa situação atual, porém, é um importante conceito que você pode criar em seu próprio aplicativo. No momento, complete este exercício adicionando uma ação ao método principal da classe `EventManager` e chame-o pela linha de comando. Se você precisar dos identificadores de uma pessoa ou evento - o método `save()` retornará estes identificadores (você poderá modificar alguns dos métodos anteriores para retornar aquele identificador):

            else if (args[0].equals("addpersontoevent")) {

                Long eventId = mgr.createAndStoreEvent("My Event", new Date());

                Long personId = mgr.createAndStorePerson("Foo", "Bar");

                mgr.addPersonToEvent(personId, eventId);

                System.out.println("Added person " + personId + " to event " + eventId);

            }

Este foi um exemplo de uma associação entre duas classes igualmente importantes: duas entidades. Como mencionado anteriormente, há outras classes e tipos dentro de um modelo típico, geralmente "menos importante". Alguns você já viu, como um `int` ou uma `String`. Nós chamamos essas classes de tipos de valores, e suas instâncias dependem de uma entidade particular. As instâncias desses tipos não possuem sua própria identidade, nem são compartilhados entre entidades. Duas pessoas não referenciam o mesmo objeto `firstname` mesmo se elas tiverem o mesmo objeto firstname. Naturalmente, os tipos de valores não são apenas encontrados dentro da JDK, mas você pode também criar suas classes como, por exemplo, `Address` ou `MonetaryAmount`. De fato, no aplicativo Hibernate todas as classes JDK são consideradas tipos de valores.

Você também pode criar uma coleção de tipo de valores. Isso é conceitualmente muito diferente de uma coleção de referências para outras entidades, mas em Java parece ser quase a mesma coisa.

### 1.2.4. Coleção de valores

Vamos adicionar uma coleção de endereços de e-mail à entidade `Person`. Isto será representado como um `java.util.Set` das instâncias `java.lang.String`:

        private Set emailAddresses = new HashSet();

        public Set getEmailAddresses() {

            return emailAddresses;

        }

        public void setEmailAddresses(Set emailAddresses) {

            this.emailAddresses = emailAddresses;

        }

Segue abaixo o mapeamento deste `Set`:

            <set name="emailAddresses" table="PERSON_EMAIL_ADDR">

                <key column="PERSON_ID"/>

                <element type="string" column="EMAIL_ADDR"/>

            </set>

A diferença comparada com o mapeamento anterior se encontra na parte `element`, que informa ao Hibernate que a coleção não contém referências à outra entidade, mas uma coleção de elementos do tipo `String`. O nome da tag em minúsculo indica que se trata de um tipo/conversor de mapeamento do Hibernate. Mais uma vez, a função `table` do elemento `set` determina o nome da tabela para a coleção. O elemento `key` define o nome da coluna de chave estrangeira na tabela de coleção. A função `column` dentro do elemento `element` define o nome da coluna onde os valores da `String` serão armazenados.

Segue abaixo o esquema atualizado:

      _____________        __________________
     |             |      |                  |       _____________
     |   EVENTS    |      |   PERSON_EVENT   |      |             |       ___________________
     |_____________|      |__________________|      |    PERSON   |      |                   |
     |             |      |                  |      |_____________|      | PERSON_EMAIL_ADDR |
     | *EVENT_ID   | <--> | *EVENT_ID        |      |             |      |___________________|
     |  EVENT_DATE |      | *PERSON_ID       | <--> | *PERSON_ID  | <--> |  *PERSON_ID       |
     |  TITLE      |      |__________________|      |  AGE        |      |  *EMAIL_ADDR      |
     |_____________|                                |  FIRSTNAME  |      |___________________|
                                                    |  LASTNAME   |
                                                    |_____________|

Você pode observar que a chave primária da tabela da coleção é na verdade uma chave composta, usando as duas colunas. Isso também implica que cada pessoa não pode ter endereços de e-mail duplicados, o que é exatamente a semântica que precisamos para um set em Java.

Você pode agora tentar adicionar elementos à essa coleção, do mesmo modo que fizemos anteriormente ligando pessoas e eventos. É o mesmo código em Java:

        private void addEmailToPerson(Long personId, String emailAddress) {

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();

            session.beginTransaction();

            Person aPerson = (Person) session.load(Person.class, personId);

            // adding to the emailAddress collection might trigger a lazy load of the collection

            aPerson.getEmailAddresses().add(emailAddress);

            session.getTransaction().commit();

        }

Desta vez não utilizamos uma consulta fetch (busca) para inicializar a coleção. Monitore o log SQL e tente otimizá-lo com árdua busca.

### 1.2.5. Associações bidirecionais

Agora iremos mapear uma associação bidirecional. Você fará uma associação entre o trabalho person e event de ambos os lados em Java. O esquema do banco de dados acima não muda, de forma que você continua possuir a multiplicidade muitos-para-muitos.

## Nota

Um banco de dados relacional é mais flexível que um linguagem de programação da rede, de maneira que ele não precisa de uma direção de navegação; os dados podem ser visualizados e restaurados de qualquer maneira.

Primeiramente, adicione uma coleção de participantes à classe `Event`:

        private Set participants = new HashSet();

        public Set getParticipants() {

            return participants;

        }

        public void setParticipants(Set participants) {

            this.participants = participants;

        }

Agora mapeie este lado da associação em `Event.hbm.xml`.

            <set name="participants" table="PERSON_EVENT" inverse="true">

                <key column="EVENT_ID"/>

                <many-to-many column="PERSON_ID" class="Person"/>

            </set

    >

Como você pode ver, esses são mapeamentos `set` normais em ambos documentos de mapeamento. Observe que os nomes das colunas em `key` e `many-to-many` estão trocados em ambos os documentos de mapeamento. A adição mais importante feita está na função `inverse="true"` no elemento `set` da coleção da classe `Event`.

Isso significa que o Hibernate deve pegar o outro lado, a classe `Person`, quando precisar encontrar informação sobre a relação entre as duas entidades. Isso será muito mais fácil de entender quando você analisar como a relação bidirecional entre as entidades é criada.

### 1.2.6. Trabalhando com links bidirecionais

Primeiro, tenha em mente que o Hibernate não afeta a semântica normal do Java. Como foi que criamos um link entre uma `Person` e um `Event` no exemplo unidirecional? Adicionamos uma instância de `Event`, da coleção de referências de eventos, à uma instância de `Person`. Então, obviamente, se quisermos que este link funcione bidirecionalmente, devemos fazer a mesma coisa para o outro lado, adicionando uma referência de `Person` na coleção de um `Event`. Essa "configuração de link de ambos os lados" é absolutamente necessária e você nunca deve esquecer de fazê-la.

Muitos desenvolvedores programam de maneira defensiva e criam métodos de gerenciamento de um link que ajustam-se corretamente em ambos os lados (como por exemplo, em `Person`):

        protected Set getEvents() {

            return events;

        }

        protected void setEvents(Set events) {

            this.events = events;

        }

        public void addToEvent(Event event) {

            this.getEvents().add(event);

            event.getParticipants().add(this);

        }

        public void removeFromEvent(Event event) {

            this.getEvents().remove(event);

            event.getParticipants().remove(this);

        }

Observe que os métodos set e get da coleção estão protegidos. Isso permite que classes e subclasses do mesmo pacote continuem acessando os métodos, mas evita que qualquer outra classe, que não esteja no mesmo pacote, acesse a coleção diretamente. Repita os passos para a coleção do outro lado.

E sobre o mapeamento da função `inverse`? Para você, e para o Java, um link bidirecional é simplesmente uma questão de configurar corretamente as referências de ambos os lados. O Hibernate, entretanto, não possui informação necessária para ajustar corretamente as instruções `INSERT` e `UPDATE` do SQL (para evitar violações de restrição) e precisa de ajuda para manipular as associações bidirecionais de forma apropriada. Ao fazer um lado da associação com a função `inverse`, você instrui o Hibernate para basicamente ignorá-lo, considerando-o uma cópia do outro lado. Isso é o necessário para o Hibernate compreender todas as possibilidades quando transformar um modelo de navegação bidirecional em esquema de banco de dados do SQL. As regras que você precisa lembrar são diretas: todas as associações bidirecionais necessitam que um lado possua a função `inverse`. Em uma associação de um-para-muitos, precisará ser o lado de "muitos", já em uma associação de muitos-para-muitos você poderá selecionar qualquer lado.

## 1.3. EventManager um aplicativo da web

Um aplicativo de web do Hibernate utiliza uma `Session` e uma `Transaction` quase do mesmo modo que um aplicativo autônomo. Entretanto, alguns modelos comuns são úteis. Nós agora criaremos um `EventManagerServlet`. Esse servlet lista todos os eventos salvos no banco de dados, e cria um formulário HTML para entrada de novos eventos.

### 1.3.1. Criando um servlet básico

Nós deveremos criar o nosso servket de processamento básico primeiramente. Uma vez que o servlet manuseia somente requisições `GET` do HTTP, o método que iremos implementar é `doGet()`:

    package org.hibernate.tutorial.web;

    // Imports

    public class EventManagerServlet extends HttpServlet {

        protected void doGet(

                HttpServletRequest request,

                HttpServletResponse response) throws ServletException, IOException {

            SimpleDateFormat dateFormatter = new SimpleDateFormat( "dd.MM.yyyy" );

            try {

                // Begin unit of work

                HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction();

                // Process request and render page...

                // End unit of work

                HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().commit();

            }

            catch (Exception ex) {

                HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().rollback();

                if ( ServletException.class.isInstance( ex ) ) {

                    throw ( ServletException ) ex;

                }

                else {

                    throw new ServletException( ex );

                }

            }

        }

    }

Salve esse servlet como `src/main/java/org/hibernate/tutorial/web/EventManagerServlet.java`

O modelo que estamos aplicando neste código é chamado session-per-request. Quando uma solicitação chega ao servlet, uma nova `Session` do Hibernate é aberta através da primeira chamada para `getCurrentSession()` em `SessionFactory`. Então uma transação do banco de dados é inicializada e todo acesso a dados deve ocorrer dentro de uma transação, não importando se o dado é de leitura ou escrita. Não se deve utilizar o modo auto-commit em aplicações.

Nunca utilize uma nova `Session` do Hibernate para todas as operações de banco de dados. Utilize uma `Session` do Hibernate que seja de interesse à todas as solicitações. Utilize `getCurrentSession()`, para que seja vinculado automaticamente à thread atual de Java.

Agora, as possíveis ações de uma solicitação serão processadas e uma resposta HTML será renderizada. Já chegaremos nesta parte.

Finalmente, a unidade de trabalho termina quando o processamento e a renderização são completados. Se ocorrer algum erro durante o processamento ou a renderização, uma exceção será lançada e a transação do banco de dados revertida. Isso completa o modelo `session-per-request`. Em vez de usar código de demarcação de transação em todo servlet você pode também criar um filtro servlet. Dê uma olhada no website do Hibernate e do Wiki para maiores informações sobre esse modelo, chamado Sessão Aberta na Visualização. Você precisará disto assim que você considerar renderizar sua visualização no JSP, não apenas num servlet.

### 1.3.2. Processando e renderizando

Vamos implementar o processamento da solicitação e renderização da página.

            // Write HTML header

            PrintWriter out = response.getWriter();

            out.println("<html><head><title>Event Manager</title></head><body>");

            // Handle actions

            if ( "store".equals(request.getParameter("action")) ) {

                String eventTitle = request.getParameter("eventTitle");

                String eventDate = request.getParameter("eventDate");

                if ( "".equals(eventTitle) || "".equals(eventDate) ) {

                    out.println("<b><i>Please enter event title and date.</i></b>");

                }

                else {

                    createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));

                    out.println("<b><i>Added event.</i></b>");

                }

            }

            // Print page

           printEventForm(out);

           listEvents(out, dateFormatter);

           // Write HTML footer

           out.println("</body></html>");

           out.flush();

           out.close();

O estilo deste código misturado com o Java e HTML, não escalariam em um aplicativo mais complexo, tenha em mente que estamos somente ilustrando os conceitos básicos do Hibernate neste tutorial. O código imprime um cabeçalho e nota de rodapé em HTML. Dentro desta página, são impressos um formulário para entrada de evento em HTML e uma lista de todos os evento no banco de dados. O primeiro método é trivial e somente produz um HTML:

        private void printEventForm(PrintWriter out) {

            out.println("<h2>Add new event:</h2>");

            out.println("<form>");

            out.println("Title: <input name='eventTitle' length='50'/><br/>");

            out.println("Date (e.g. 24.12.2009): <input name='eventDate' length='10'/><br/>");

            out.println("<input type='submit' name='action' value='store'/>");

            out.println("</form>");

        }

O método `listEvents()` utiliza a `Session` do Hibernate, limitado ao thread atual para executar uma consulta:

        private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {

            List result = HibernateUtil.getSessionFactory()

                    .getCurrentSession().createCriteria(Event.class).list();

            if (result.size() > 0) {

                out.println("<h2>Events in database:</h2>");

                out.println("<table border='1'>");

                out.println("<tr>");

                out.println("<th>Event title</th>");

                out.println("<th>Event date</th>");

                out.println("</tr>");

                Iterator it = result.iterator();

                while (it.hasNext()) {

                    Event event = (Event) it.next();

                    out.println("<tr>");

                    out.println("<td>" + event.getTitle() + "</td>");

                    out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");

                    out.println("</tr>");

                }

                out.println("</table>");

            }

        }

Finalmente, a ação `store`, é despachada ao método `createAndStoreEvent()`, que também utiliza a `Session` da thread atual:

        protected void createAndStoreEvent(String title, Date theDate) {

            Event theEvent = new Event();

            theEvent.setTitle(title);

            theEvent.setDate(theDate);

            HibernateUtil.getSessionFactory()

                    .getCurrentSession().save(theEvent);

        }

O servlet está completo agora. Uma solicitação ao servlet será processada com uma única `Session` e `Transaction`. Quanto antes estiver no aplicativo autônomo, maior a chance do Hibernate vincular automaticamente estes objetos à thread atual de execução. Isto lhe dá a liberdade para inserir seu código e acessar a `SessionFactory` como desejar. Geralmente, usaríamos um diagrama mais sofisticado e moveríamos o código de acesso de dados para os objetos de acesso dos dados (o modelo DAO). Veja o Hibernate Wiki para mais exemplos.

### 1.3.3. Implementando e testando

Para implementar este aplicativo em testes, nós devemos criar um Arquivo da Web (WAR). Primeiro, nós devemos definir o descritor WAR como `src/main/webapp/WEB-INF/web.xml`

    <?xml version="1.0" encoding="UTF-8"?>

    <web-app version="2.4"

        xmlns="http://java.sun.com/xml/ns/j2ee"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

        <servlet>

            <servlet-name>Event Manager</servlet-name>

            <servlet-class>org.hibernate.tutorial.web.EventManagerServlet</servlet-class>

        </servlet>

        <servlet-mapping>

            <servlet-name>Event Manager</servlet-name>

            <url-pattern>/eventmanager</url-pattern>

        </servlet-mapping>

    </web-app>

Para construir e implementar, chame seu diretório de projeto `ant war` e copie o arquivo `hibernate-tutorial.war` para seu diretório Tomcat `webapp`.

## Nota

If you do not have Tomcat installed, download it from http://tomcat.apache.org/ and follow the installation instructions. Our application requires no changes to the standard Tomcat configuration.

Uma vez implementado e com o Tomcat rodando, acesse o aplicativo em `http://localhost:8080/hibernate-tutorial/eventmanager`. Tenha a certeza de observar o log do Tomcat para ver o Hibernate inicializar quando a primeira solicitação chegar em seu servlet (o inicializador estático no `HibernateUtil` é chamado) e para obter o resultado detalhado caso exceções aconteçam.

## 1.4. Sumário

Este tutorial cobriu itens básicos de como escrever um aplicativo Hibernate autônomo simples e um aplicativo da web pequeno. A partir do Hibernate website você poderá encontrar mais tutoriais disponíveis.

Copyright © 2004 Red Hat, Inc.

- AnteriorPrefácio
- Acima
- Principal
- PróximaCapítulo 2. Arquitetura