510 likes | 693 Views
Hibernate. Java avançado – PCC Jobson Ronan {jrjs@cin.ufpe.br} Guilherme Kelly {gkmo@cin.ufpe.br}. O que é?. Hibernate é uma moderna solução de mapeamento objeto-realcional(ORM) Persistência tranparente (POJO/Java Beans) “Lazy Fetching” Uso de uma Cache
E N D
Hibernate Java avançado – PCC Jobson Ronan {jrjs@cin.ufpe.br} Guilherme Kelly {gkmo@cin.ufpe.br}
O que é? • Hibernate é uma moderna solução de mapeamento objeto-realcional(ORM) • Persistência tranparente (POJO/Java Beans) • “Lazy Fetching” • Uso de uma Cache • Três estratégias para o mapeamento de heranças
Pra que transparência? • Persistência sem impacto no código dos objetos de negócio • Qualquer classe pode ser uma classe persistente sem ser necessário implementar nenhuma classe ou interface • Classes persistentes podem ser usadas fora do contexto de persistência (ex Testes) • Total portabilidade sem dependências
Problemas dos BDRs • Modelagem • Não há polimorfismo • Não há herança • Lógica de negócio • Stored procedures -> perca de portabilidade
Vantagens dos RDBs • Trabalhar com grandes quantidades de dados • Busca, ordenação • Trabalhar com conjuntos de dados • Junções e agregações • Compartilhamento • Concorrência (Transações) • Muitas aplicações • Integridade • Restrições (Constraints) • Isolação de transações
Objetivo • Aproveitar-se das vantagens oferecidas pelos RDBs • Isso sem perder a orientação a objetos
Objetivo Real • Ter menos trabalho • Ter um DBA feliz
Em prática • Locadora em partes...
Em prática • Classe Persistente • Construtor default • Pares de Get´s e Set´s • Uma propriedade identificadora package br.org.citi.pec.locadora; public class Filme { private int codigo; private String nome; public int getCodigo() { return codigo; } private void setCodigo(int codigo) { this.codigo = codigo; } public String getNome() { return nome; } private void setNome(int nome) { this.nome = nome; } }
Em prática • Mapeamento XML • Metadado legível • Mapeamento de tabelas e colunas <?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> <classname="br.org.citi.pec.locadora.Filme" table="filmes"> <idname="codigo" column="filme_id"> <generatorclass="native" /> </id> <propertyname="nome" column="nome"/> </class> </hibernate-mapping>
Em prática • Salvando um objeto Session session = sessionFactory.openSession(); Transaction tx = s.beginTransaction(); Filme filme = new Filme(); filme.setNome(novoNome); session.save(filme); tx.commit(); session.close(); Também pode ser usado saveOrUpdate
Em prática • Carregando um objeto Session session = sessionFactory.openSession(); Filme filme = (Filme) session.load(Filme.class, new Integer(filmeId); session.close(); • Não use load para determinar se um objeto existe (uma exceção é lançada neste caso) • Use get. Este retorna null caso o objeto não exista
Em prática • Removendo um objeto Session session = sessionFactory.openSession(); Transaction tx = s.beginTransaction(); Filme filme = (Filme) session.get(Filme.class, new Integer(filmeId); session.delete(filme); tx.commit(); session.close();
Em prática • Atualizando um objeto (Dirty Checking) • Obetendo um filme e alterando seu nome Session session = sessionFactory.openSession(); Transaction tx = s.beginTransaction(); Filme filme = (Filme) session.get(Filme.class, new Integer(filmeId); filme.setNome(novoNome); tx.commit(); session.close(); Não é necessário uma chamada explicita do update
Estado dos Objetos • Alguns conceitos sobre objetos • Transiente. • Nunca persistido. Não associado a nenhuma sessão (Session) • Persistente • Associado a uma única sessão • Desacoplado (Detached) • Já persistido, mas não associado a nenhuma sessão
Estado dos Objetos • Dirty Checking só funciona em objetos persistentes • Use update para objetos desacoplados
Mais prática • Melhorando nosso modelo...
Mais prática • Classes persistentes package br.org.citi.pec.locadora; public class Cliente { private String login; private String CPF; private String nome; privateSet locacoes = new Hashset(); //... Get´s e Set´s }
Mais prática • Classes persistentes package br.org.citi.pec.locadora; public class Locacao { private int id; private Filme filme; private Cliente cliente; private Date dataLocacao; private Date datadevolucao; //... Get´s e Set´s }
Mais prática • XML... <?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> <classname="br.org.citi.pec.locadora.Cliente" table="Clientes"> <idname="login"/> <propertyname="nome" /> <propertyname="cpf" /> <setname="locacoes"> <keycolumn="cliente_login" /> <one-to-manyclass="br.org.citi.pec.locadora.Locacao"/> </set> </class> </hibernate-mapping>
Mais prática • XML... <?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> <classname="br.org.citi.pec.locadora.Locacao" table="Locacoes"> <idname="id"> <generatorclass="native" /> </id> <propertyname="dataLocacao" /> <propertyname="dataDevolucao" /> <many-to-one name="cliente" class="br.org.citi.pec.locadora.Cliente" column="cliente_login"/> <many-to-one name="filme" class="br.org.citi.pec.locadora.Filme" column="filme_id"/> </class> </hibernate-mapping>
Mais prática • Persistência transitiva Locacao locacao = new Locacao(); Locacao.setDataLocacao(new Date()); Session session = sessionFactory.openSession(); Transaction tx = s.beginTransaction(); Filme filme = (Filme) session.get(Filme.class, new Integer(filmeId); Cliente cliente = (Cliente) session.get(Cliente.class, login); locacao.setCliente(cliente); locacao.setFilme(filme); cliente.getLocacoes().add(locacao); tx.commit(); session.close();
Otimizando • Como as coleções são carregadas • Lazy fetching (Default) • Eager (Outer Join) fetching • Indicado nos XML, ou na própria consulta
Lazy fetching • SQL escondido: Cliente cliente = (Cliente) session.get(Cliente.class, login); SELECT … FROM CLIENTES C WHERE C.LOGIN = ? Iterator cliente = cliente.getLocacoes().iterate(); SELECT … FROM LOCACOES L WHERE L.LOGIN_CLIENTE = ? Filme filme = locacao.getFilme(); SELECT … FROM FILMES F WHERE F.FILME_ID = ?
Outer join fetching • SQL escondido: Cliente cliente = (Cliente) session.get(Cliente.class, login); SELECT … FROM CLIENTES C LEFT OUTER JOIN LOCACOES L ON L.LOGIN_CLIENTE = C.LOGIN LEFT OUTER JOIN FILME F ON L.FILME_ID = F.FILME_ID WHERE C.LOGIN = ?
Herança • Estratégias para o mapeamento de herança • Uma tabela por hierarquia de classes • Uma tabela por subclasse • Uma tabela por classe concreta
Herança • Uma tabela por subclasse <?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> <classname="br.org.citi.pec.locadora.Cliente" table="Clientes"> <idname="login" column="LOGIN" /> ... <joined-subclass name="br.org.citi.pec.locadora.ClienteEspecial" table="ClientesEspeciais"> <key column="LOGIN" /> <property ... /> ... </joined-subclass> </class> </hibernate-mapping>
Otimizando • Como otimizar? • Minimizando a leitura de linhas das tabelas • Minimizando a quantidade de comandos SQLs enviados
Otimizando • Minimizando a leitura de linhas das tabelas • Usar lazy fecthing • Minimizando a quantidade de comandos SQLs enviados • Usar outer join fetching
Otimizando • Problemas • Usar lazy fecthing • Problema dos n+1 selects (muitos selects!) • Usar outer join fetching • Problema do produto cartesiano (grandes conjuntos de dados)
Otimizando • Solucionando problemas de otimização • Stratégia de Fecthing definida em tempo de execução • Batch Fecthing • Utilizar um cache de segundo nível
Consultas • HIbernate Query Lanuage • Permite que se expresse quase tudo o que se precisa expressar em SQL, porém mais orientado a objetos • Três maneiras de se fazer consultas no Hibernate • HQL • Criteria • SQL nativo
HQL • Tornar SQL orientado a objetos • Classes e propriedades ao invés de Tabelas e colunas • Polimorfismo • Associações • Total suporte a operações relacionais • Joins • Projeções • Funções agregadas e agrupamento • Ordenação • SubQueries • Chamadas a funções SQL
HQL • A consulta mais simples possivel HQL from Filme • Devolva todos os filmes List filmes = session.createQuery(“from Filme”).list(); • As consultas podem ser paginadas Query query = session.createQuery(“from Filme”); query.setFirstResult(20);query.setMaxResults(30); List filmes = query.list();
HQL • Uma consulta com ordenação from Locacao l order by l.dataLocacao desc • Caso esteja interessado em um único resultado Query query = session.createQuery( “from Locacao l order by l.dataLocacao desc”); Locacao locacao = query.uniqueResult();
HQL • Uma consulta com joins select c from Cliente c left join [fetch] c.locacaoes l where l.dataLocacao > 3/5/2005 • Todos os clientes com suas locações efetuadas após o dia 3 • Definição da estratégia de fetching • Com o fetch: Coleções já inicializadas • Sem o fetch: lazy collections
HQL • HQL suporta os mesmos operadores que SQL • Operadores do HQL: • Comparação: =, <>, <, >, >=, <=, between, not between, in, not in • Nulidade: is null, is not null • Aritméticos: +, -, /, *, %, parênteses • O operador like funciona da mesma forma que SQL • Pode usar funções SQL se o banco suportar
HQL • Projeções + funções agregadas (Report Queries) select c.nome, count(l) from Cliente c, c.locacoes l where l.dataLocacao between(:inicio, :fim) group by c.nome order by count(l)
HQL • Os dados da consulta anterior serão retornados em List de Object[] • Consulta comum para a realização de relatórios • Podemos tornar o processo mais elegante • List list = session.createQuery( • “select new ClienteReportRow(c.nome, count(l)) ” + • “from Cliente c, c.locacoes l ” + • “where l.dataLocacao between(:inicio, :fim) ” + • “group by c.nome ” + • “order by count(l)”). • setDate(“inicion”, inicio). • setDate(“fim”, fim).list();
HQL • Dessa evitasse o uso de vetores de objetos • É necessário que exista uma classe e um construtor previamente definido
HQL • Consultas não precisam aparecer no código • Na verdade, muitas vezes é melhor que não apareçam • Podem ficar nos metadados e serem chamadas pelo nome • Usa-se o método getNamedQuery() • Mas antes, ela precisa ser declarada em algum arquivo de mapeamento List clientes = session.getNamedQuery(“findClienteByName”) .setString(“nome”, nome),list() <query name=“findClienteByName”><![CDATA[ from Cliente c where c.nome like :nome]] </query>
Modelagem OO • “Mais classes que tabelas” • Classse Endereco • Propriedade: rua, numero, bairro... • Rua, numero, bairro,... Colunas da tabela cliente <classname="br.org.citi.pec.locadora.Cliente" table="Clientes"> … <componentname=“endereco” class=“br.org.citi.pec.locadora.Endereco”> <property name=“rua” column=“rua”/> <property name=“numero” column=“numero”/> … </component> </class>
O que mais? • Hibernate também dá suporte a tipos de dados definidos pelo usuário • Interceptadores e EventListeners • Filtros • Definições explicitas dos SQLs de insert e update e load (hibernate 3) ...e mais um pouco!
Sobre configuração • Como obter um SessionFactory? • Programaticamente • Por arquivo XML
Configuração programática Configuration cfg = new Configuration() .setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect"). .setProperty("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver") .setProperty("hibernate.connection.url", "jdbc:hsqldb:file:hsqldb/data") .setProperty("hibernate.connection.username", "sa") .setProperty("hibernate.connection.password", "") .addClass(Filme.class) .addClass(Cliente.class) .addClass(Locacao.class); SessionFactory sessionFactory = cfg.buildSessionFactory();
Configuração XML hibernate.cfg.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <propertyname="hibernate.dialect"> org.hibernate.dialect.MySQLDialect</property> <propertyname="hibernate.connection.driver_class"> org.gjt.mm.mysql.Driver</property> <propertyname="hibernate.connection.username">root</property> <propertyname="hibernate.connection.password">ftBBvEdiC</property> <propertyname="hibernate.connection.url"> jdbc:mysql://localhost/lockar</property> <mappingclass="br.org.citi.pec.locadora.Filme"/> <mappingclass="br.org.citi.pec.locadora.Locacao"/> <mappingclass="br.org.citi.pec.locadora.Cliente"/> </session-factory> </hibernate-configuration> Configuration cfg = new Configuration().configure(); SessionFactory sessionFactory = cfg.buildSessionFactory();
Extras • Hbm2ddl (tool) • Gera o schema do BD a partir das XML de mapeamentos • Usado por linha de comando ou pelo ant <target name="schemaexport"> <taskdef name="schemaexport" classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"classpathref="project.lib.classpath" /> <schemaexport properties="hibernate.properties" quiet="yes" text="yes" drop="no" delimiter=";"output="${basedir}/sql/schema-export.sql"> <fileset dir="src"> <include name="**/*.hbm.xml" /> </fileset> </schemaexport> </target>
Exercícios • Ampliar nosso modelo