Hibernate – componentes, herança, e associações Jobson Ronan {[email protected]}

38
Hibernate – componentes, herança, e associações Jobson Ronan {[email protected]}

Transcript of Hibernate – componentes, herança, e associações Jobson Ronan {[email protected]}

Page 1: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Hibernate – componentes, herança, e associações

Jobson Ronan {[email protected]}

Page 2: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Objetivos

Aprender como realizar o mapeamento de modelos entidades que possuem associações, composições e herança com o hibernate

Page 3: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Granularidade alta (fina) Fine-grained == mais classes que tabelas

Mais coesão, mais reuso, modelo fácil de entender Freqüentemente o melhor modelo de dados para uma tabela traduz-se

em mais de uma classe Considere, por exemplo, uma tabela cliente com dois endereços:

endereco_fatura e endereco_entrega, cidade_fatura, cidade_entrega, etc. Uma classe Endereco, agruparia campos relativos ao endereço de um Cliente,

que teria propriedades enderecoFatura e enderecoEntrega como referências para dois objetos da classe Endereco

Uma coluna email poderia ser expandida em uma classe Email, para detalhar um tipo de dados e encapsular validação de e-mail em vez de ser uma mera propriedade String do Cliente

Page 4: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Tipos de objetos: entidade ou valor Um objeto do tipo entidade tem sua própria identidade de registro de banco

de dados Uma referência para uma entidade é tornada persistente como uma referência no

banco de dados (foreign key) Uma entidade pode existir independentemente de outra entidade

Um objeto do tipo valor não tem identidade de banco de dados Pertence a uma entidade Seu estado persistente é parte do registro da entidade que a possui Não têm identificadores ou propriedades de identificação O tempo de vida é limitado pelo tempo de vida da entidade que o possui Exemplos nativos: Strings, Integers, etc.

Um dos objetos de um registro tem uma identidade própria; os outros são dependentes dele No exemplo anterior: objetos do tipo Cliente são entidades, objetos do tipo Endereco e

Email são valores dependentes de um Cliente

Page 5: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Componentes Um componente em Hibernate é a parte dependente de um

objeto composto entidade-valor Uma composição ocorre quando uma entidade, que tem

identidade na tabela, está associada a objetos de valor que não têm identidade na tabela (seus dados são parte da tabela)

A remoção do registro no banco remove a entidade e seus componentes derivados (não confunda com cascade-delete)

Um componente é uma propriedade Meio termo entre uma propriedade de campo de dados (field) e

uma propriedade que é referência em relacionamento Componentes só existem como objetos, mas não como tabelas Não confunda com componente de software (arquitetura)

Page 6: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Componentes e tabelas Os atributos do

componente estão mapeados à mesma tabela que a classe composta

Page 7: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Mapeamento de componentes<class name="User" table="USER"> <id name="id“ column="USER_ID“ type="long"> <generator class="native"/> </id> <property name="username" column="USERNAME" type="string"/> <component name="homeAddress“ class="Address"> <property name="street" type="string" column="HOME_STREET" not^-null="true"/> <property name="city" type="string" column="HOME_CITY" not-null="true"/> <property name="zipcode" type="short" column="HOME_ZIPCODE" not-null="true"/> </component> <component name="billingAddress“ class="Address"> <property name="street" type="string" column="BILLING_STREET" not-null="true"/> <property name="city" type="string" column="BILLING_CITY" not-null="true"/> <property name="zipcode" type="short" column="BILLING_ZIPCODE" not-null="true"/> </component> ...</class>

Page 8: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Limitações de componentes Classes mapeadas como componentes têm várias

limitações Não podem ter seus objetos compartilhados (não há como referir-

se a eles pois não têm identidade própria) Não existe maneira elegante de representar uma referência nula

para um componente (é representado com valores nulos em todas as colunas do componente): se você gravar um componente não nulo com apenas valores nulos, Hibernate retorna null!

Page 9: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Mapeamento de herança Herança é o descasamento mais visível entre os mundos

relacional e orientado a objetos Mundo OO possui relacionamento “é um” e “tem um” Mundo relacional apenas possui relacionamento “tem um”

Há várias estratégias [Ambler 2002]* Uma tabela por classe concreta: modelo relacional ignora

herança e polimorfismo Uma tabela por hierarquia de classes: permite polimorfismo com

tabelas de-normalizadas mais uma coluna extra contendo informação de tipo

Uma tabela por subclasse: representa relacionamentos “é um” através de relacionamentos “tem um” (chave estrangeira)

Page 10: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Uma tabela por classe concreta

Page 11: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Uma tabela por classe concreta Ideal para classes que não fazem parte de uma hierarquia

ou que estão na raiz de uma hierarquia (nível mais alto) Essas classes não devem ser usadas em polimorfismo Uma declaração <class> para cada classe concreta; um atributo

table diferente para cada uma (igual a mapeamento simples) Desvantagens

Pouco suporte para associações polimórficas Queries polimórficos, executados em superclasses das classes

usadas causam múltiplos queries nas tabelas mapeadas às classes concretas

Dificulta evolução do esquema (mudanças semânticas em propriedades da superclasse afetam colunas de várias tabelas)

Page 12: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Queries gerados Os queries abaixo são conceituais (o SQL real gerado pelo

Hibernate pode ser diferente) Dois queries para fazer uma pesquisa na superclasse

BillingDetails (ineficiente!)select CREDIT_CARD_ID, OWNER, NUMBER, CREATED,

TYPE, ... from CREDIT_CARD where CREATED = ? select BANK_ACCOUNT_ID, OWNER, NUMBER, CREATED,

BANK_NAME, ... from BANK_ACCOUNT where CREATED=? Para fazer uma pesquisa numa classe concreta

select CREDIT_CARD_ID, TYPE, EXP_MONTH, EXP_YEAR from CREDIT_CARD where CREATED = ?

Page 13: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Uma tabela por hierarquia

Page 14: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Uma tabela por hierarquia Mapeia-se a hierarquia toda a uma única tabela

Tabela inclui uma coluna para identificar a classe (tipo); esta coluna (discriminator) não é mapeada a uma propriedade mas usada internamente pelo Hibernate

Há colunas para todas as propriedades de todas as classes da hierarquia A classe raiz é mapeada da forma convencional <class> Subclasses são mapeadas dentro de <class> como <subclass>

Vantagens Forma mais eficiente de implementar polimorfismo É simples de implementar, entender e evoluir

Desvantagens Colunas de propriedades declaradas em subclasses precisam aceitar valores

nulos (não pode ser declarada not-null)

Page 15: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Queries gerados

Exemplo de query polimórfico (conceitual) na superclasse; um só query recupera dados de todas as subclassesselect BILLING_DETAILS_ID, BILLING_DETAILS_TYPE,

OWNER, ..., CREDIT_CARD_TYPE, from BILLING_DETAILS where CREATED = ?

Exemplo de um query em uma classe concretaselect BILLING_DETAILS_ID, CREDIT_CARD_TYPE,

CREDIT_CARD_EXP_MONTH, ...from BILLING_DETAILSwhere BILLING_DETAILS_TYPE='CC' and CREATED = ?

Page 16: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Mapeamento<hibernate-mapping> <class name="BillingDetails" table="BILLING_DETAILS" discriminator-value="BD">

<id name="id" column="BILLING_DETAILS_ID" type="long"> <generator class="native"/> </id>

<discriminator column="BILLING_DETAILS_TYPE" type="string"/>

<property name="name" column="OWNER" type="string"/> ...

<subclass name="CreditCard" discriminator-value="CC"> <property name="type" column="CREDIT_CARD_TYPE"/> ... </subclass> ... </class></hibernate-mapping>

Page 17: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Uma tabela por subclasse

Page 18: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Uma tabela por subclasse Representa herança como relacionamentos de chave

estrangeira Cada subclasse que declara propriedades persistentes (inclusive

interfaces e classes abstratas) tem sua própria tabela Cada tabela possui colunas apenas para propriedades não-

herdadas, e uma chave primária que é chave estrangeira da superclasse

Criação de uma instância cria registros nas tabelas da superclasse e subclasse

A recuperação dos dados é realizada através de um join das tabelas

<joined-subclass> (que pode conter outros elementos <joined-subclass>) pode ser usada no lugar de ou dentro de <class>

Page 19: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Uma tabela por subclasse Vantagens

Modelo relacional normalizado Evolução e restrições de integridade simples Novas classes/tabelas criadas sem afetar classes/tabelas

existentes Desvantagens

Performance baixa em hierarquias complexas Mais difícil de codificar a mão (complicado de integrar com JDBC

legado)

Page 20: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Queries (conceituais) produzidos Bem mais complicados... Query na superclasse

select BD.BILLING_DETAILS_ID, BD.CREATED, CC.TYPE, ..., BA.BANK_SWIFT, ... case when CC.CREDIT_CARD_ID is not null then 1 when BA.BANK_ACCOUNT_ID is not null then 2 when BD.BILLING_DETAILS_ID is not null then 0end as TYPEfrom BILLING_DETAILS BD left join CREDIT_CARD CC on BD.BILLING_DETAILS_ID = CC.CREDIT_CARD_ID left join BANK_ACCOUNT BA on BD.BILLING_DETAILS_ID = BA.BANK_ACCOUNT_IDwhere BD.CREATED = ?

Query na subclasseselect BD.BILLING_DETAILS_ID, BD.CREATED, CC.TYPE, ... from CREDIT_CARD CC inner join BILLING_DETAILS BD on BD.BILLING_DETAILS_ID = CC.CREDIT_CARD_IDwhere CC.CREATED = ?

Page 21: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Qual estratégia?

Normalmente, usa-se uma combinação de estratégias Estratégias assumem desenvolvimento top-down (isto não é

integração de um sistema legado) Se não houver necessidade de queries polimórficos ou

associações, prefira Tabela por Classe Concreta Se houver necessidade de associações polimórficas, use...

... Tabela por Hierarquia se as classes tiverem poucas propriedades e for uma hierarquia simples

... Tabela por Subclasse se a hierarquia for mais complicada ou classes tiverem muitas propriedades (ou ainda se as restrições de Tabela por Hierarquia – como nulidade de colunas – forem inaceitáveis no modelo de dados)

Page 22: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Resumo: tags de mapeamento <component>

Uma composição (objeto associado é dependente) <subclass>

Usada para implementar a estratégia de mapeamento de herança “tabela por hierarquia de classe”

<joined-subclass> Usado para implementar a estratégia “table-per-subclass”

onde cada subclasse tem sua própria tabela <discriminator>

Coluna e propriedade usada na estratégia “tabela por hierarquia de classe” para identificar a subclasse

Page 23: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

XML de mapeamento

<?xml version="1.0"?><hibernate-mapping> <class name="BillingDetails“ table="BILLING_DETAILS"> <id name="id" column="BILLING_DETAILS_ID" type="long"> <generator class="native"/> </id> <property name="owner" column="OWNER" type="string"/> ...

<joined-subclass name="CreditCard" table="CREDIT_CARD"> <key column="CREDIT_CARD_ID"> <property name="type" column="TYPE"/> ... </joined-subclass>

... </class></hibernate-mapping>

Page 24: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Associações Associações no Hibernate funcionam da mesma maneira

que associações de objetos em Java Diferente de EJBs CMP, onde relacionamentos são gerenciados

pelo container Associações de objetos em Hibernate são unidirecionais e não

são gerenciadas pelo container Associações sempre são relacionamentos entre entidades

Algumas são implementadas via coleções Associações um-para-muitos são as mais comuns

Qualquer associação muitos-para-muitos pode ser implementada com um par de associações um-para-muitos: é mais simples!

Dá para realizar quase tudo apenas com <many-to-one>

Page 25: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Multiplicidade de associações A primeira classificação que fazemos de uma associação é

sua multiplicidade Exemplo

Há mais de um Lance (Bid) para um certo Item? Há mais de um Item para um certo Lance?

Conclusão Existe uma associação de muitos para um de Bid para Item Por ser bidirecional, podemos dizer que também existe uma

associação de um para muitos de Item para Bid: o item conhece todos os lances feitos para ele.

Page 26: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Uma simples associação

A associação unidirecional, muitos para um de Bid para Item é a mais simples possível A referência retornada por Bid.getItem() é mapeada à coluna

ITEM_ID na tabela BID, que é chave estrangeira da chave primária da tabela ITEM

public class Bid { ... private Item item; public void setItem(Item item) { this.item = item; } public Item getItem() { return item; } ... }

<class name="Bid" table="BID"> ... <many-to-one name="item" column="ITEM_ID" class="Item" not-null="true"/> </class>

Se houver um lance (bid) o item tem que existir

Page 27: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

<many-to-one> Many-to-one é uma associação comum

O modelo relacional é many-to-one Uma referência de objeto é many-to-one

Alguns atributos: name e column: nome da propriedade e coluna do

banco cascade: pode ter save-update, delete, all (informa que

tipo de operação será repetida no objeto da associação) outer-join: se true, objeto associado é carregado junto

com o objeto pai; se ausente, o comportamento ocorre se objeto associado não estiver definido como um proxy.

Page 28: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Tornando-a bidirecional

Como encontrar todos os lances para um dado item? Associação bidirecional um para muitos: use um set!

public class Item { ... private Set bids = new HashSet(); public void setBids(Set bids) { this.bids = bids; } public Set getBids() { return bids; } public void addBid(Bid bid) { bid.setItem(this); bids.add(bid); }...}

<class name="Item" table="ITEM"> ... <set name="bids“> <key column="ITEM_ID"/> <one-to-many class="Bid"/> </set> </class>

Page 29: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

<one-to-many> <set>

Este <set> contém <one-to-many>: uma coleção de registros – mapeado diretamente à tabela!

Representa uma relação um para muitos no modelo relacional Mapeamento direto: atributo class informa a classe (não é preciso

informar colunas nem nome da tabela – o mapeamento da classe referida já tem essa informação!)

Page 30: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Um para um

Uma associação um para um pode ser declarada com o elemento <one-to-one> e/ou <many-to-one>

Há dois tipos de relacionamentos um-para-um Associações de chave estrangeira unívocas: a chave

nunca se repete na classe associada – usa elemento <many-to-one>; usa também <one-to-one> se for bidirecional.

Associações de chave primária: os dois objetos compartilham a mesma chave primária – usa apenas elementos <one-to-one>

Page 31: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Associação muitos para muitos unidirecional

<class name=“Category” ...> ... <set name="items" table="CATEGORY_ITEM" lazy="true" cascade="save-update"> <key column="CATEGORY_ID"/> <many-to-many class="Item" column="ITEM_ID"/> </set>

Transaction tx = session.beginTransaction();

Category cat = (Category) session.get(Category.class, categoryId); Item item = (Item) session.get(Item.class, itemId); cat.getItems().add(item);

tx.commit();

Page 32: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Muitos para muitos bidirecional

<class name="org.hibernate.auction.Category"> <id name="id" column="ID"/> ... <set name="items" table="CATEGORY_ITEM"> <key column="CATEGORY_ID"/> <many-to-many class="org.hibernate.auction.Item" column="ITEM_ID"/> </set></class>

<class name="org.hibernate.auction.Item"> <id name="id" column="ID"/> ... <!-- inverse end --> <set name="categories" table="CATEGORY_ITEM"> <key column="ITEM_ID"/> <many-to-many class="org.hibernate.auction.Category“ column="CATEGORY_ID"/> </set></class>

Page 33: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Uso da associação bidirecional É preciso modificar os dois lados!

Transaction tx = session.beginTransaction();

Category cat = (Category) session.get(Category.class, categoryId);

Item item = (Item) session.get(Item.class, itemId);

cat.getItems().add(item);

item.getCategories().add(category);

tx.commit();

Page 34: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Associações polimórficas

Todas as associações mostradas até agora suportam polimorfismo Não é preciso fazer nada de especial para ter

polimorfismo no Hibernate

Page 35: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Coleções polimórficas: exemplo

<class name=“BillingDetails”> <many-to-one name="user" class="User" column="USER_ID"/>

<class name=“User” > ... <set name="billingDetails" lazy="true" cascade="save-update" inverse="true"> <key column="USER_ID"/> <one-to-many class="BillingDetails"/> </set>

CreditCard cc = new CreditCard();cc.setNumber(ccNumber);cc.setType(ccType);cc.setExpiryDate(ccExpiryDate);

Session s = f.openSession();Transaction tx = s.beginTransaction();

User user = (User) s.get(User.class, uid);// Call convenience methoduser.addBillingDetails(cc);

tx.commit(); s.close();

Session s = f.openSession();Transaction tx = s.beginTransaction();User user = (User) s.get(User.class, uid);Iterator i = user.getBillingDetails().iterator();while ( i.hasNext() ) { BillingDetails bd = (BillingDetails) i.next(); // CreditCard.pay() or BankAccount.pay() bd.pay(ccPaymentAmount);}tx.commit(); s.close();

Page 36: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Conclusões

Hibernate não ajuda só na implementação de modelos simples

Hibernate implementa facilmente coisas que levariam semanas

Associações, composições e heranças não são mais problemas para a camada de persistência

Page 37: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Referências

Hibernate in Action Hibernate reference manual caveatemptor.hibernate.org

Page 38: Hibernate – componentes, herança, e associações Jobson Ronan {jrjs@cin.ufpe.br}

Exercício 1

Implementar no Hibernate o seguinte modelo