Hibernate Introdução Caio Nakashima [email protected] [email protected] 1 Primeira Aplicação • Será criada uma aplicação simples baseado no console do Hibernate. • O banco de dados neste exemplo poderá armazenar eventos que se deseja atender e informações sobre o anfitrião destes eventos. • O primeiro passo é configurar a pasta de desenvolvimento e colocar todas as bibliotecas Java necessárias nele. • Abaixar a distribuição do Hibernate do site http://www.hibernate.org ou da pasta do servidor. 2 Bibliotecas necessárias • +lib – – – – – – – – – – – antlr.jar cglib-full.jar asm.jar asm-attrs.jars commons-collections.jar commons-logging.jar ehcache.jar hibernate3.jar jta.jar dom4j.jar log4j.jar 3 Bibliotecas necessárias • Extrair o pacote e colocar todas as bibliotecas necessárias que se encontram em /lib para a pasta /lib da pasta de desenvolvimento. • A lista da transparência anterior é o conjunto mínimo das bibliotecas necessárias. • É interessante consultar o arquivo README.txt da pasta /lib da distribuição do Hibernate para maiores informações sobre as bibliotecas adicionais de terceiros. 4 Classe • Será criada uma classe que representa um evento que se deseja armazenar no banco de dados. • A classe deste exemplo é uma classe JavaBean com algumas propriedades. • Observe que a classe utiliza o padrão de nomes do JavaBean para as propriedades dos métodos get e set, assim como a visibilidade dos campos privados. • Esta é uma recomendação de projeto, mas não é obrigatório. • Hibernate pode acessar campos diretamente, o benefício de métodos de acesso é a robustez para refactoring. 5 import java.util.Date; public class Event { private Long id; private String title; private Date date; 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; } } Event.java 6 Identificadores • A propriedade ID carrega o valor do identificador único para um evento particular. • Todas as classes de persistência de entidades necessitam de uma propriedade identificadora para utilizar o conjunto completo das características do Hibernate. • Em geral não se manipula o identificador de um objeto, então o método set deve ser privado. – Somente o Hibernate pode atribuir valores aos identificadores quando um objeto é salvo. • Observa-se que o Hibernate pode acessar métodos de acesso public, private e protected assim como os campos diretamente. 7 import java.util.Date; public class Event { private Long id; private String title; private Date date; 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; } } Event.java Identificadores, definição das interfaces 8 Construtor • É necessário um construtor sem argumentos para todas as classes de persistência. – Hibernate tem que criar uma classe utilizando Java Reflection. • O construtor pode ser privado, porém a visibilidade do pacote (package) é requerida pelo runtime proxy generation e uma recuperação de dados eficiente sem a instrumentação de bytecode. • Para o exemplo colocar a fonte Java em uma pasta denominada /src na pasta de desenvolvimento. +lib <Hibernate and third-party libraries> +src Event.java 9 import java.util.Date; public class Event { private Long id; private String title; private Date date; 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; } } Event.java Construtores 10 Arquivo de mapeamento • É necessário especificar para o Hibernate como carregar e armazenar objeto da classe de persistência. • O arquivo de mapeamento faz este papel – Especifica qual tabela do banco de dados deve ser acessado – Quais colunas da tabela devem ser utilizadas. <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> [...] </hibernate-mapping> 11 Arquivo DTD • Pode-se utilizar o arquivo DTD do Hibernate para a auto complementação dos elementos de mapeamento XML e atributos em um editor de programas ou IDE. • Sugere-se abrir o arquivo DTD para verificar todos os elementos, atributos e valores padrão. • Hibernate não carregará o arquivo DTD da internet, mas primeiro procura-o no classpath da aplicação. – Esta localizado em .\src\org\hibernate 12 hibernate-mapping • Entre duas tags hibernate de mapeamento, incluir uma classe de elemento. • Todas as classes de persistência de entidade necessitam um mapeamento para uma tabela em uma tabela do banco de dados SQL. • Abaixo definiu-se como persistir e carregar objetos da classe Event para a tabela EVENTS, cada instância representada por uma linha desta tabela. <hibernate-mapping> <class name="Event" table="EVENTS"> </class> </hibernate-mapping> 13 Identificador único de entidade • Para mapear o identificador único para a chave primária da tabela, é necessário configurar a estratégia de geração dos valores para serem colocadas na coluna da chave primária. <hibernate-mapping> <class name="Event" table="EVENTS"> <id name="id" column="EVENT_ID"> <generator class="increment"/> </id> </class> </hibernate-mapping> 14 Chave primária • O elemento ID é a declaração da propriedade do identificador. – name="id" • declara o name da propriedade Java-Hibernate que será utilizada para métodos SET e GET para acessar dos dados. • O atributo column faz a ligação para o Hibernate qual coluna da tabela EVENTS será utilizado como chave primária. – column="EVENT_ID" • O elemento generator especifica qual estratégia para gerador de dados. – Neste exemplo está sendo utilizado o método de incremento inmemory, muito útil para teste. • Hibernate também suporta geração de valor automáticos pelo banco de dados, assim como identificadores atribuídos pela aplicação. 15 Propriedades de persistência • Por padrão nenhuma propriedade da classe é considerada para persistência. • O nome do atributo da propriedade do elemento indica para o Hibernate qual método get e set deve ser utilizado. • Por que a propriedade de mapeamento data incluir o atributo da coluna e a propriedade title não? • Sem o atributo coluna Hibernate por padrão utiliza a propriedade nome como o nome da coluna. • Date é uma palavra reservada na maioria dos banco de dados, assim é melhor mapear com um nome diferente. 16 Event.hbm.xml <hibernate-mapping> <class name="Event" table="EVENTS"> <id name="id" column="EVENT_ID"> <generator class="increment"/> </id> <property name="date" type="timestamp" column="EVENT_DATE"/> <property name="title"/> </class> </hibernate-mapping> 17 Forma de mapeamento • Por que o atributo title não tem definido seu tipo? – Os tipos declarados e utilizados no arquivo de mapeamento não são tipos de dados Java. – Também não são tipos de dados SQL. • São tipos denominados Tipos de Dados de Mapeamento Hibernate. – Ele converte tipos de dados Java para tipos de dados SQL e vice versa. • Hibernate tenta determinar a conversão correta e mapear tipos de atributos que não estão presentes no arquivo de mapeamento. • Em alguns casos detecção automática de tipo de dados, utilizando Reflection on Java Class, pode não ter um valor padrão conforme esperado. – Este é o caso da propriedade date. – Hibernate não sabe se a propriedade deve ser mapeada para o tipo DATE ou TIMESTAMP ou TIME do SQL. 18 Event.hbm.xml • O arquivo de mapeamento deve ser salvo com o nome Event.hbm.xml, na pasta onde está armazenado a fonte do Event.java. • O nome do arquivo pode ser de livre escolha, mas o sufixo deve ser hbm.xml. • A estrutura de diretório deve ser: +lib <Hibernate and third-party libraries> +src Event.java Event.hbm.xml 19 Exercício • Criar um arquivo denominado Event.hbm.xml o mapeamento do arquivo. (Pasta src) <?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="Event" table="EVENTS"> <id name="id" column="EVENT_ID"> <generator class="increment"/> </id> <property name="date" type="timestamp" column="EVENT_DATE"/> <property name="title"/> </class> </hibernate-mapping> 20 Configurando para acessar Postgresql • Buscar no site do PostgreSql (http://jdbc.postgresql.org/download.html/) os drivers JDBC. • Colocar na pasta lib o arquivo • Criar na raiz da pasta de desenvolvimento uma pasta denominada data. +lib <Hibernate and third-party libraries> postgresql-8.1-404.jdbc2.jar +src Event.java Event.hbm.xml +data 21 Conexão com o banco de dados • Hibernate é a camada da aplicação que conecta-se com o banco de dados, assim ele necessita de informações sobre a conexão. • As conexões são efetivadas através de um grupo de conexões JDBC (JDBC connection pool) que deve ser configurado também. • A distribuição Hibernate contém inúmeras ferramentas de conexões JDBC de código aberto (open source JDBC connection). • Para a configuração do Hibernate pode utilizar o arquivo hibernate.properties ou hibernate.cfg.xml ou via aplicação. 22 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration3.0.dtd"> <hibernate-configuration> <session-factory> <!-- Database connection settings --> <property name="connection.driver_class"> org.postgresql.Driver </property> <property name="connection.url"> jdbc:postgresql://localhost:5432/postgres </property> <property name="connection.username">postgres</property> <property name="connection.password">1234</property> 23 <!-- JDBC connection pool (use the built-in) --> <property name="connection.pool_size">1</property> <!-- SQL dialect --> <property name="dialect"> org.hibernate.dialect.PostgreSQLDialect </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">create</property> <mapping resource="Event.hbm.xml"/> </session-factory> </hibernate-configuration> 24 <session-factory> • Este arquivo de configuração XML utiliza um arquivo DTD diferente. • Configura-se Hibernate's SessionFactory uma fábrica responsável por um banco de dados particular. • Se existirem mais de um banco de dados, utiliza-se inúmeras configurações <session-factory> geralmente um arquivo para cada configuração para facilitar a sua inicialização. 25 JDBC • Os primeiros 4 elementos (propriedades) contém as informações necessárias para a conexão JDBC. <property name="connection.driver_class"> org.postgresql.Driver </property> <property name="connection.url"> jdbc:postgresql://localhost:5432/postgres </property> <property name="connection.username">postgres</property> <property name="connection.password">1234</property> 26 Dialeto • Propriedade do elemento dialeto especifica uma variante SQL particular gerado pelo Hibernate <property name="dialect"> org.hibernate.dialect.PostgreSQLDialect </property> • A opção hbm2ddl.auto habilita a geração automática dos esquemas de banco de dados (database schemas) diretamente dentro de banco de dados. • Removendo-se esta opção de configuração pode-se desligar esta característica. • Pode-se redirecionar para um arquivo com a ajuda do SchemaExport (ANT) <property name="hbm2ddl.auto">create</property> 27 Event Mapping • Adiciona-se o(s) arquivo(s) de mapeamento para as classes de persistência. <mapping resource="Event.hbm.xml"/> • Copia-se este arquivo para o diretório fonte, assim completa o classpath. . • Hibernate automaticamente procura por um arquivo denominado hibernate.cfg.xml na raiz do classpath durante a inicialização. • Criar o arquivo de configuração do Hibernate (hibernate.cfg.xml). +lib <Hibernate and third-party libraries> +src Event.java Event.hbm.xml hibernate.cfg.xml 28 Inicialização • Na inicialização deve incluir a construção de um objeto SessionFactory para armazena-lo em algum lugar para fácil acesso pelo código da aplicação. • Uma SessionFactory pode abrir uma nova seção. • Uma seção representa uma unidade de trabalho thread simples (single thread), a SessionFactory é um objeto global de uma thread segura, instanciada uma única vez. • Será criada uma classe de ajuda HibernateUtil que cuidará da inicialização e fará com que a manipulação da seção seja conveniente. • Assim o padrão de seção ThreadLocal chamado é útil aqui, para manter a unidade de trabalho corrente com a thread corrente. 29 SessionFactory • Esta classe além de produzir uma seção global do SessionFactory em sua inicialização estática (chamada uma única vez pelo JVM quando a classe é carregada), mas também tem uma variável ThreadLocal para armazenar a seção da thread corrente. • Sempre que for chamado o método HibernateUtil.currentSession() retornará sempre a mesma unidade de trabalho Hibernate em um mesmo thread. • Uma chamda para o método HibernateUtil.closeSession() finaliza a unidade de trabalho corrente associada com um thread. 30 Nota: HibernateUtil • Esta classe não é necessária se for implementar o Hibernate em um servidor de aplicações J2EE: – Um seção será automaticamente limitado pela transação corrente JTA e pode-se procurar a SessionFactory através do JNDI. • Se utilizar o servidor de aplicação JBoss, pode se implementado como um serviço de gerenciamento de sistema e será automaticamente associada a SessionFactory para uma nome JNDI. build.xml +lib <Hibernate and third-party libraries> +src Event.java Event.hbm.xml HibernateUtil.java hibernate.cfg.xml +data 31 Registro de Ocorrências • Agora é necessário configurar o sistema de registro de ocorrências (logging system). • Hibernate utiliza commons logging e permite escolher entre Log4j e JDK 1.4 logging. • A maioria dos desenvolvedores preferem Log4j. – Copiar o arquivo log4j.properties para a pasta /src na mesma pasta do hibernate.cfg.xml. • A saída padrão das mensagens de inicialização são apresentadas em stout. 32 Armazenado e Carregando Objetos import org.hibernate.Transaction; import org.hibernate.Session; import java.util.Date; 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.sessionFactory.close(); } private void createAndStoreEvent(String title, Date theDate){ Session session = HibernateUtil.currentSession(); Transaction tx = session.beginTransaction(); Event theEvent = new Event(); theEvent.setTitle(title); theEvent.setDate(theDate); session.save(theEvent); tx.commit(); HibernateUtil.closeSession(); } } 33 • Cria-se um novo objeto Event, que manipula o Hibernate. • Hibernate cuida do SQL e executa as inserções no banco de dados. • Uma seção é uma simples unidade de trabalho. • Uma unidade de trabalho pode ser mais longa que uma simples transação no banco de dados. – Exemplo: uma unidade de trabalho que faz inúmeras chamadas http request/response em uma aplicação web. • Separar a transação com o banco de dados da unidade de trabalho e com a aplicação do ponto de vista do usuário é um dos conceitos básicos de projeto do Hibernate. 34 Application Transaction • Pode-de denominar uma unidade de trabalho longa que geralmente encapsula inúmeras transações pequenas de banco de dados como Application Transaction. • O API de Transação Hibernate, é opcional, mas é utilizado por sua portabilidade e conveniência. • Para manipular a transação do banco dados, chamando o método session.connection.commit(), deve-se atribuir um código específico para o ambiente de desenvolvimento. • Por padrão de configuração do Hibernate pode-se implantar a camada de persistência em qualquer banco de dados. • No capítulo 12 do Hibernate Reference Documentation pode-se obter mais informações sobre o assunto. 35 NetBeans Lib localizado em minLib do Hibernate – Postgresql Netbeans Eclipse 36 Eclipse Lib localizado em minLib do Hibernate – Postgresql 37 Para adicionar eventos • Para armazenar eventos, pode-se adicionar uma opção no método main: sf = new Configuration().configure().buildSessionFactory(); Session session = sf.openSession(); //Abre sessao Transaction tx = session.beginTransaction(); //Cria transacao //Cria objeto Evento Event theEvent = new Event(); theEvent.setTitle("Evento Novo"); theEvent.setDate(new Date()); session.save(theEvent); tx.commit(); //Fecha transacao session.close(); //Fecha sessao 13:45:09,734 DEBUG SchemaExport:301 - drop table EVENTS_POSJAVA 13:45:09,890 DEBUG SchemaExport:301 - drop sequence hibernate_sequence 13:45:09,906 DEBUG SchemaExport:301 - create table EVENTS_POSJAVA (EVENT_ID int8 not null, EVENT_DATE timestamp, title varchar(255), primary key (EVENT_ID)) 13:45:10,171 DEBUG SchemaExport:301 - create sequence hibernate_sequence 13:45:10,187 INFO SchemaExport:194 - schema export complete Hibernate: select nextval ('hibernate_sequence') Hibernate: insert into EVENTS_POSJAVA (EVENT_DATE, title, EVENT_ID) values (?, ?, ?) 38 listEvents() sf = new Configuration().configure().buildSessionFactory(); Session session = sf.openSession(); //Abre sessao List listaEventos = session.createQuery("from Event").list(); for (int i = 0; i < listaEventos.size(); i++) { Event theEvent = (Event) listaEventos.get(i); System.out.println(" Id:"+theEvent.getId()); System.out.println(" Titulo:"+theEvent.getTitle()); System.out.println(" Data: "+ theEvent.getDate()); } session.close(); //Fecha sessao • É utilizado aqui HQL (Hibernate Query Language) para consultar e carregar todos os objetos de Eventos existentes do banco de dados. • Hibernate gerará a expressão SQL apropriada e enviará para o banco de dados e populará os objetos Events com os dados. 39 Exercício • Desenvolver um código para criar, inserir e listar os dados da tabela abaixo: – A) PostgreSQL – B) MySQL Pessoa Pessoa_ID (PK) Nome Sexo Data_nascimento • Analise o exemplo Estado, que lista os dados da tabela estado. • Elabore um exemplo para listar o Municípios. 40