JPA Com Hibernate Aula 2 Paulo Ricardo Lisboa de Almeida 1
Modelo Necessário Modelo Necessário 2
Dados Transientes Quando criamos determinada propriedade em uma classe, mas essa propriedade não deve ser salva no banco de dados. Utilizar a notação @Transient Exemplo @Entity class Pessoa { @Id @GeneratedValue private Long id ; private Calendar nascimento ; @Transient private int idade ;//idade não precisa ser salva no banco, pois ela pode ser deduzida pela aplicação através da data de nascimento } 3
Mapeando Relacionamentos Feitos a partir de anotações 4
One to One Um para Um Anotação @OneToOne Ex.: um estado tem um governador e um governador governa somente um estado 5
One to One Um para Um @Entity public class Governador { @Id private Long cpf; private String nomegovernador; } @Entity public class Estado { @Id private String UF; private String nomeestado; @OneToOne @JoinColumn (name = "governador_cpf") private Governador governador ; } A anotação @JoinColumn especifica como é feito o join entre governador e estado. No exemplo, o atributo name especifica o nome da coluna que faz esse join na tabela estado (Chave estrangeira) 6
Many To One Muitos Para Um Anotação @ManyToOne Ex.: Muitos animais pertencem a uma pessoa, e uma pessoa pode ter muitos animais 7
Many To One Muitos Para Um @Entity @Table(name="ANIMAL") public class Animal {... @ManyToOne @JoinColumn(name = "pessoa_id") private Pessoa dono;... } 8
One To Many Um Para Muitos Anotação @OneToMany Ex.: Uma pessoa possui vários animais e um animal possui somente um dono. 9
One To Many Um Para Muitos @Entity @Table(name="pessoa") public class Pessoa { @OneToMany @JoinColumn(name="pessoa_id",referencedColumnName="pessoa _id") private List<Animal> animais;... } Em @JoinColumn - name indica o nome da coluna da tabela animal que referencia a pessoa - referencedcolumnname indica o nome da coluna de id da tabela pessoa referenciada pelo animal 10
Many To Many Muitos para Muitos Anotação @ManyToMany Ex.: Um animal pode possuir várias alergias, e uma alergia pode estar associada a vários animais. 11
Many To Many Muitos para Muitos @Entity @Table(name="ANIMAL") public class Animal {... @ManyToMany @JoinTable(name="alergiaanimal", joincolumns={@joincolumn(name="animal_id", referencedcolumnname="animal_id")}, inversejoincolumns={@joincolumn (name="alergia_id",referencedcolumnname="alergia_id")}) private List<Alergia> alergias; 12
Many To Many Muitos para Muitos @JoinTable define o nome da tabela que faz a ligação (no exemplo é a tabela alergiaanimal) joincolumns={@joincolumn(name="nome_fk_tab_ligacao", referencedcolumnname=" nome_chave_tab_original ")} define quais são as colunas da tabela que fazem a ligação entre a tabela sendo mapeada (animal) e a tabela de ligação (alergiaanimal) inversejoincolumns O mesmo que joincolumns, mas para as chaves da tabela sendo relacionada (alergia) 13
Relacionamentos Bidirecionais Em muitas ocasiões queremos acessar as entidades de ambos os lados dos relacionamentos. Ex.: Ao acessar uma pessoa, queremos ter acesso a sua lista de animais. Ao acessar um animal, queremos ter acesso ao seu dono Nos relacionamentos bidirecionais, temos uma classe que é a dona do relacionamento, a possui as informações sobre as chaves estrangeiras no banco de dados Geralmente o ManyToOne é o dono do relacionamento Na classe que não é dona do relacionamento, deve-se utilizar o atributo mappedby, que deve conter o nome do atributo que expressa o mesmo relacionamento na outra entidade. 14
Relacionamentos Bidirecionais public class Animal { @ManyToOne @JoinColumn(name = "pessoa_id") private Pessoa dono;... } public class Pessoa {... @OneToMany(mappedBy="dono") private List<Animal> animais;... } - No exemplo a classe animal é a dona do relacionamento com pessoa. - A classe pessoa especifica que a classe animal se relaciona com a classe pessoa através do atributo dono, já mapeado no banco de dados. 15
Mapeando Entidades com chaves compostas com @EmbeddedId No caso de chaves compostas, deve-se criar uma classe separada que deve conter somente as chaves primárias da tabela Ex.: Considere a tabela vacinaanimal, que possui 3 atributos chave (dos quais 2 também são chave estrangeira) e um atributo que não é chave. 16
Mapeando Entidades com chaves compostas com @EmbeddedId Cria-se uma classe para representar o ID que deve ser marcada como @Embeddable @Embeddable public class VacinaAnimalPK implements Serializable{... } private static final long serialversionuid = 1L; @Column(name="vacina_id") private Integer idvacina; @Column(name="animal_id") private Integer idanimal; private Date datavacinacao; 17
Mapeando Entidades com chaves compostas com @EmbeddedId A classe que representa o ID deve conter somente atributos nativos A especificação do JPA 2.0 diz que somente atributos nativos devem ser colocados nas classes de primary key. Por essa razão a classe representa idvacina e idanimal como inteiros e não como as classes vacina e animal 18
Mapeando Entidades com chaves compostas com @EmbeddedId Cria-se então a classe VacinaAnimal @Entity @Table(name="vacinaanimal") public class VacinaAnimal {... } @EmbeddedId private VacinaAnimalPK id; 19
Mapeando Entidades com chaves compostas com @EmbeddedId O bean é criado normalmente Porém o atributo que representa o id composto (classe VacinaAnimalPK) é marcado como @EmbeddedId. Esse método não irá funcionar a menos que a classe que representa o ID implemente os métodos hashcode e equals de maneira correta. 20
Mapeando Entidades com chaves compostas com @EmbeddedId Contornando o problema da classe que representa a PK aceitar somente atributos nativos Criar os relacionamentos na classe que representa a tabela. Ex.: @Entity @Table(name="vacinaanimal") public class VacinaAnimal { @EmbeddedId private VacinaAnimalPK id;... } @ManyToOne @JoinColumn(name = "vacina_id", insertable = false, updatable = false) private Vacina vacina; 21
Mapeando Entidades com chaves compostas com @EmbeddedId No caso do exemplo temos que marcar que: A entidade relacionada não é inserível caso ela ainda não exista no banco insertable = false Não é possível atualizar o relacionamento (trocar uma vacina por outra no relacionamento) updatable=false Outra opção que nos levaria ao mesmo resultado seria utilizar @MapsId( nomeatribudonaclasseembeaddable ) Quais os problemas que podem surgir com isso? 22
Mapeando Entidades com chaves compostas com @EmbeddedId No caso do exemplo temos que marcar que: A entidade relacionada não é inserível caso ela ainda não exista no banco insertable = false Não é possível atualizar o relacionamento (trocar uma vacina por outra no relacionamento) updatable=false Quais os problemas que podem surgir com isso? Ao fazer vacinaanimal.setvacina(...) o id.vacina_id não será trocado automaticamente Modificar o set E se relacionarmos uma vacina a um vacinaanimal, e trocarmos o id da vacina? Vacina.setId(...) Devemos atualizar o vacinaid de vacinaanimal manualmente 23
Eager vs. Lazy Por padrão o hibernate irá carregar as entidades mapeadas por collections (listas, sets,...) de forma lazy. O atributo fech=tipo_do_fetch do relacionamento permite modificar esse comportamento. Carga eager pode aumentar o desempenho evitando o problema do N+1 Deve-se avaliar se a lista sempre é utilizada para valer a pena a carga antecipada dos itens @ManyToMany(fetch=FetchType.EAGER) @JoinTable(name="alergiaanimal", joincolumns={@joincolumn(name="animal_id", referencedcolumnname="animal_id")}, inversejoincolumns={@joincolumn(name="alergia_id", referencedcolumnname="alergia_id")}) private List<Alergia> alergias; 24
Eager em Múltiplas Listas Se tivermos duas listas marcadas como eager em uma única classe teremos problemas Quais? Por que isso ocorre? 25
Eager em Múltiplas Listas Se tivermos duas listas marcadas como eager em uma única classe teremos problemas Quais? Os objetos podem se repetir na lista, ou o JPA pode lançar uma exceção org.hibernate.loader.multiplebagfetchexception: cannot simultaneously fetch multiple bags Por que isso ocorre? O banco de dados gera um produto cartesiano do dados 26
Eager em Múltiplas Listas Possíveis soluções Apenas uma das listas deve ser eager Utilziar sets ao invés de listas Utilizar anotações específicas do Hibernate Exemplo: @Fetch(value = FetchMode.SUBSELECT) 27