Вы находитесь на странице: 1из 76

JPA Mini Livro

Primeiros passos e conceitos detalhados

Autor: Hebert Coelho de Oliveira

Contedo
JPA Mini Livro......................................................................................................................... 1 Primeiros passos e conceitos detalhados ............................................................................ 1 Captulo 1: Introduo .............................................................................................................. 4 Captulo 2: Quais os motivos que levaram criao do JPA ....................................................... 5 Captulo 3: O que o JPA? O que so as implementaes do JPA? ............................................ 7 Captulo 4: Para que serve o persistence.xml? E suas configuraes? ........................................ 8 Captulo 5: Definies de uma Entity. O que so anotaes Lgicas e Fsicas? ...................... 12 Captulo 6: Gerando Id: Definio, Id por Identity ou Sequence ............................................... 15 Identity ............................................................................................................................... 15 Sequence .......................................................................................................................... 16 Captulo 7: Gerando Id: TableGenerator e Auto ....................................................................... 18 TableGenerator ................................................................................................................ 18 Auto ................................................................................................................................... 19 Captulo 8: Utilizando Chave Composta Simples ...................................................................... 20 @IdClass........................................................................................................................... 20 @Embeddable .................................................................................................................. 23 Captulo 9: Utilizando Chave Composta Complexa................................................................... 25 Captulo 10: Modos para obter um EntityManager .................................................................. 30 Captulo 11: Mapeando duas ou mais tabelas em uma entitade .............................................. 31 Captulo 12: Mapeando Heranas MappedSuperclass .......................................................... 32 Captulo 13: Mapeando Heranas Single Table ..................................................................... 34 Captulo 14: Mapeando Heranas Joined .............................................................................. 36 Captulo 15: Mapeando Heranas Table Per Concrete Class .................................................. 39 Captulo 16: Prs e Contras dos mapeamentos das heranas .................................................. 42 Captulo 17: Embedded Objects .............................................................................................. 44 Captulo 18: ElementCollection Como mapear uma lista de valores em uma classe .............. 46 Captulo 19: OneToOne (Um para Um) Unidirecional e Bidirecional ........................................ 48 Unidirecional ..................................................................................................................... 48 Bidirecional ....................................................................................................................... 49 No existe auto relacionamento ................................................................................... 50 Captulo 20: OneToMany (um para muitos) e ManyToOne (muitos para um) Unidirecional e Bidirecional ............................................................................................................................. 51 No existe auto relacionamento ................................................................................... 52

Captulo 21: ManyToMany (muitos para muitos) Unidirecional e Bidirecional ......................... 53 No existe auto relacionamento ................................................................................... 55 Captulo 22: ManyToMany com campos adicionais ................................................................. 56 Captulo 23: Como funciona o Cascade? Para que serve o OrphanRemoval? Como tratar a org.hibernate.TransientObjectException ................................................................................. 60 OrphanRemoval ............................................................................................................... 67 Captulo 24: Como excluir corretamente uma entidade com relacionamentos. Capturar entidades relacionadas no momento da excluso de um registro no banco de dados ............. 70 Captulo 25: Criando um EntityManagerFactory por aplicao................................................. 72 Captulo 26: Entendendo o Lazy/Eager Load............................................................................ 73 Captulo 27: Tratando o erro: cannot simultaneously fetch multiple bags ............................ 75

Captulo 1: Introduo
Vamos ver sobre JPA: o que JPA, para que serve o persistence.xml, criar corretamente uma entidade, como realizar desde mapeamentos simples at os mapeamentos complexos de chaves primrias, criar relacionamentos entre as entidades, modos para persistncia automtica e outros.

Captulo 2: Quais os motivos que levaram criao do JPA


Um dos grandes problemas da Orientao a Objetos como mapear seus objetos para refletir o banco de dados. possvel ter uma classe com o nome de Carro mas seus dados estarem salvos em uma tabela chamada TB_CARRO. O nome da tabela seria apenas o comeo dos problemas, se sua classe Carro tem o campo nome mas na tabela est como STR_NAME_CAR? O framework mais bsico do Java para acessar o banco de dados o JDBC. Infelizmente, com o JDBC, necessrio um trabalho braal para transformar o resultado que vem do banco de dados em uma classe. Para obter objetos do tipo carros (no cdigo abaixo est como Car) vindo do banco de dados utilizando JDBC seria necessrio ter o cdigo abaixo:
1 import java.sql.*; 2 import java.util.LinkedList; 3 import java.util.List; 4 5 public class MainWithJDBC { 6 public static void main(String[] args) throws Exception { 7 Class.forName("org.hsqldb.jdbcDriver"); 8 9 Connection connection = // get a valid connection 10 11 Statement statement = connection.createStatement(); 12 13 ResultSet rs = statement.executeQuery("SELECT \"Id\", \"Name\" FROM \"Car\""); 14 15 List<Car> cars = new LinkedList<Car>(); 16 17 while(rs.next()){ 18 Car car = new Car(); 19 car.setId(rs.getInt("Id")); 20 car.setName(rs.getString("Name")); 21 cars.add(car); 22 } 23 24 for (Car car : cars) { 25 System.out.println("Car id: " + car.getId() + " Car Name: " + car.getName()); 26 } 27 28 connection.close(); 29 } 30 }

No cdigo acima possvel ver como trabalhoso transformar os valores retornados pela consulta em objetos. Imagine uma classe com 30 campos Para piorar imagine uma classe com 30 campos e relacionado com outra classe que tambm tenha 30 campos? Por exemplo, um Carro pode ter uma Lista de Pessoas e cada objeto da classe Pessoa teria 30 campos. Outra desvantagem de uma aplicao que utiliza o JDBC puro a sua portabilidade. A sintaxe de cada query pode variar entre bancos de dados. Para se limitar o nmero de linhas de uma consulta na Oracle se utiliza a palavra ROWNUM, j no SQL Server utilizado a palavra TOP. A portabilidade de uma aplicao que utiliza JDBC fica comprometida quando as queries nativas de cada banco so utilizadas. Existem solues para esse tipo de problema, por exemplo, ter cada consulta utilizada salva em um arquivo .sql fora da aplicao. Para cada banco diferente que a aplicao rodasse um arquivo .sql diferente seria utilizado. possvel encontrar outras dificuldades ao longo do caminho: como atualizar os relacionamentos de modo automtico, no deixar registros rfos na tabela ou como utilizar hierarquia de um modo mais simples.

Captulo 3: O que o JPA? O que so as implementaes do JPA?


O JPA veio para solucionar todos os problemas listados na pgina anterior. A proposta do JPA que seja possvel trabalhar diretamente com as classes e no ter que utilizar as consultas nativas de cada banco de dados; o JPA ir fazer esse trabalho pelo desenvolvedor. O JPA nada mais do que um conjunto de especificaes (muitos textos, normas e interfaces do Java) de como uma implementao deve se comportar. Existem diversas implementaes no mercado que seguem as especificaes do JPA. Podemos citar o Hibernate, OpenJPA, EclipseLink e o recente Batoo. As implementaes tem a liberdade de adicionar anotaes e cdigos a mais que desejarem, mas tem que implementar o bsico que o JPA requeira. A primeira soluo para o problema de portabilidade que o JPA apresenta o modo de mapear os dados para dentro de cada classe. Como veremos mais a frente, possvel mapear os dados para cada coluna da classe mesmo que o nome do atributo seja diferente do nome da coluna no banco de dados. O JPA criou uma linguagem de consulta chamada JPQL para realizar as consultas no banco de dados. A vantagem do JPQL que a mesma consulta pode ser executada em todos os bancos de dados.

1 SELECT id, name, color, age, doors FROM Car

A consulta acima pode ser executada em JPQL conforme abaixo:


1 SELECT c FROM Car c

Note que o retorno da consulta c, ou seja, um objeto do tipo carro e no os campos/valores existentes na tabela. O prprio JPA montar os objetos automaticamente.

Caso voc queira ver como realizar diversos modos de query com JPA, acesse: http://uaihebert.com/?p=1137.

O JPA ficar responsvel por traduzir cada JPQL para a sintaxe correta; o desenvolvedor no precisar tomar conhecimento de qual a sintaxe requerida para cada banco.

Captulo 4: Para que serve o persistence.xml? E suas configuraes?


O arquivo do persistence.xml o arquivo responsvel por diversas configuraes do sistema. Nele possvel definir configuraes especficas da aplicao e do banco de dados, como tambm passar configuraes especficas de cada implementao do JPA. O cdigo do persistence.xml apresentado aqui consta as configuraes relacionadas ao EclipseLink, mas os conceitos e os cdigos presentes nas classes Java deste post funcionam com qualquer implementao. O arquivo persistence.xml deve estar localizado na pasta META-INF no mesmo diretrio das classes. Abaixo um exemplo de onde esse arquivo deve estar localizado no Eclipse:

Essa imagem valida para o Eclipse. Para o Netbeans necessrio verificar qual o correto lugar na documentao. No momento no o tenho instalado para fornecer essa informao. Caso voc tenha a mensagem de erro: Could not find any META-INF/persistence.xml file in the classpath voc pode verificar o seguinte:

Abra o arquivo WAR gerado e veja se o arquivo persistence.xml se encontra dentro de /WEB-INF/classes/META-INF/; Se no estiver seu erro est na criao do WAR. Caso o empacotamento seja automtico voc precisa colocar o arquivo no lugar que a IDE espera que ele esteja. Se seu empacotamento for manual (ant, maven) verifique o script de gerao do arquivo. Se seu projeto for um projeto EAR verifique se o arquivo se encontra na raiz do jar EJB dentro da pasta META-INF; Se no estiver seu erro est na criao do WAR. Caso o empacotamento seja automtico voc precisa colocar o arquivo no lugar que a IDE espera que ele esteja. Se seu empacotamento for manual (ant, maven) verifique o script de gerao do arquivo.

Caso a aplicao seja JSE (desktop) verifique se no est na raiz das classes dentro da pasta META-INF no JAR. Por default o JPA ir procurar na raiz do JAR pelo endereo METAINF/persistence.xml.

Verifique se o arquivo est escrito todo em letras minsculas persistence.xml. O arquivo tem que ter o nome todo com letras minsculas.

Caso o arquivo no seja localizado o JPA ir exibir a mensagem de erro citada acima. Como regra geral, o melhor lugar para se colocar o persistence.xml como mostrado na imagem acima. Veja abaixo, um exemplo de persistence.xml:
1 <?xml version="1.0" encoding="UTF-8"?> 2 3 <persistence version="2.0" 4 xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> 6 7 <persistence-unit name="MyPU" transaction-type="RESOURCE_LOCAL"> 8 <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> 9 10 <class>page20.Person</class> 11 <class>page20.Cellular</class> 12 <class>page20.Call</class> 13 <class>page20.Dog</class> 14 15 <exclude-unlisted-classes>true</exclude-unlisted-classes> 16 17 <properties> 18 <property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver" /> 19 <property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:mem:myDataBase" /> 20 <property name="javax.persistence.jdbc.user" value="sa" /> 21 <property name="javax.persistence.jdbc.password" value="" /> 22 <property name="eclipselink.ddl-generation" value="create-tables" /> 23 <property name="eclipselink.logging.level" value="FINEST" /> 24 </properties> 25 </persistence-unit> 26 27 <persistence-unit name="PostgresPU" transaction-type="RESOURCE_LOCAL"> 28 <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> 29 30 <class>page26.Car</class> 31 <class>page26.Dog</class> 32 <class>page26.Person</class> 33 34 <exclude-unlisted-classes>true</exclude-unlisted-classes> 35 36 <properties> 37 <property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost/JpaRelationships" />

38 <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" /> 39 <property name="javax.persistence.jdbc.user" value="postgres" /> 40 <property name="javax.persistence.jdbc.password" value="postgres" /> 41 <!-- <property name="eclipselink.ddl-generation" value="drop-and-createtables" /> --> 42 <property name="eclipselink.ddl-generation" value="create-tables" /> 43 <!-- <property name="eclipselink.logging.level" value="FINEST" /> --> 44 </properties> 45 </persistence-unit> 46 </persistence>

Vamos analisar detalhadamente cada linha citada acima:

<persistence-unit name=MyPU =>Com essa configurao definimos o nome do Persistence Unit. O Persistence Unit pode ser definido como o universo JPA da sua aplicao. Ele contm dados de todas as classes, relacionamentos, chaves e outras informaes relacionados ao banco de dados. possvel tambm adicionar mais de um PersistenceUnit no mesmo arquivo conforme demonstrado acima. transaction-type=RESOURCE_LOCAL => Define qual o tipo de transao. Atualmente dois valores so permitidos: RESOURCE_LOCAL e JTA. Em aplicaes desktop utilizado o RESOURCE_LOCAL e para aplicaes web pode se utilizar ambos, dependendo da

arquitetura do sistema. <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> => Define qual ser o provider da implementao JPA utilizada. O provider na verdade qual implementao ser utilizada. Para o Hibernate utilizado org.hibernate.ejb.HibernatePersistence e para o OpenJPA utilizado org.apache.openjpa.persistence.PersistenceProviderImpl. <class></class> => Serve para declarar as classes. A necessidade dessa tag estar presente varia de acordo com a implementao. Por exemplo, no JSE ao utilizar o Hibernate no obrigado listar todas as classes presentes no sistema, j com EclipseLink e OpenJPA necessrio declarar as classes. <exclude-unlisted-classes>true</exclude-unlisted-classes> => Essa configurao define que qualquer entidade, que no se encontra listada no arquivo persistence.xml, no deve ser mapeada dentro do Persistence Unit (prximo captulo explicar o que uma entidade). Essa configurao muito til quando so necessrios dois ou mais Persistence Units no mesmo projeto onde uma entidade pode apenas existir em um Persistence Unit e no em outro. possvel deixar um cdigo comentado utilizando os valores <! > <properties> => possvel passar parmetros especficos a serem utilizados pela implementao. Valores como driver, senha e usurio normal de encontrar em todas as implementaes; geralmente esses valores so escritos em um persistence.xml quando temos uma aplicao que utiliza RESOURCE_LOCAL. Para transaes JTA so utilizados datasources que contm as configuraes necessrias. Para configurar um datasource necessrio usar uma das duas anotaes a seguire: <jta-data-source></jta-data-source> <nonjta-data-source></non-jta-data-source>. Alguns servidores, como o JBoss, tem como

10

requisito que mesmo um Persistence Unit de RESOURCE_LOCAL seja mapeado com as tags de datasource. Duas configuraes valem apena destacar:

Configurao eclipselink.ddl-generation hibernate.hbm2ddl.auto

Implementao EclipseLink Hibernate

Serve Para Ativar a criao/atualizao automtica as tabelas ou at mesmo validar se o esquema do banco de dados valido com relao ao mapeamento realizado com o JPA nas classes.

openjpa.jdbc.SynchronizeMappings eclipselink.logging.level org.hibernate.SQL.level org.hibernate.type.level openjpa.Log

OpenJPA EclipseLink

Hibernate OpenJPA

Configuraes que definem o nvel de contedo a ser impresso pelo log.

Voc ir encontrar na internet os valores permitidos para cada configurao de cada implementao.

11

Captulo 5: Definies de uma Entity. O que so anotaes Lgicas e Fsicas?


Para que o JPA possa mapear corretamente cada tabela do banco de dados para dentro de uma classe Java foi criado o conceito de Entidade (ou Entity em ingls). Uma Entity deve refletir exatamente a estrutura da tabela no banco de dados para que o JPA possa tomar conta do restante do trabalho. Para que uma classe Java possa ser considerada uma Entity ela seguir os seguintes requisitos:

Ter a anotao @Entity Ter um construtor pblico sem parmetros Ter um campo anotado como @Id

Veja abaixo uma classe que segue todos os requisitos necessrios:


1 import javax.persistence.Entity; 2 import javax.persistence.Id; 3 4 @Entity 5 public class Car { 6 7 public Car(){ 8 9 } 10 11 public Car(int id){ 12 this.id = id; 13 } 14 15 // Just to show that there is no need to have get/set when we talk about JPA Id 16 @Id 17 private int id; 18 19 private String name; 20 21 public String getName() { 22 return name; 23 } 24 25 public void setName(String name) { 26 this.name = name; 27 } 28 }

12

Sobre o cdigo acima:


A classe anotada com @Entity logo acima de seu nome Existe um atributo que definimos como o id da nossa classe apenas anotando com @Id.Sim, toda Entity tem que ter um campo que sirva como ID . Em geral esse campo um nmero sequencial, mas pode ser uma String e outros tipos de valores Um detalhe curioso que no existe get/set para o ID. Na viso do JPA um ID imutvel ento no existe a necessidade de alterao do ID obrigatria presena de um construtor que no aceite de parmetros. Outros construtores podem ser adicionados.

De acordo com o cdigo exibido acima so exibidas apenas anotaes para definir uma Entity; o JPA ir procurar por uma tabela chamada CAR no banco de dados e por campos chamado ID e NAME. Por padro o JPA utiliza o nome da classe e o nome de seus atributos para encontrar a tabela e sua estrutura no banco. Segundo o livro Pro JPA 2 podemos definir as anotaes do JPA de dois modos, anotaes fsicas e anotaes lgicas. As anotaes fsicas so aquelas que determinam o relacionamento entre a classe e o banco de dados. As anotaes lgicas definem a modelagem da classe com relao ao sistema. Veja o cdigo abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import java.util.List; import javax.persistence.*; @Entity @Table(name = "TB_PERSON_02837") public class Person { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; @Basic(fetch = FetchType.LAZY) @Column(name = "PERSON_NAME", length = 100, unique = true, nullable = false) private String name; @OneToMany private List<Car> cars; public String getName() { return name; } public void setName(String name) { this.name = name; }

23 24 25 26 27 }

13

No cdigo acima possvel encontrar as anotaes: @Entity, @Id e @OneToMany (estudaremos mais adiante sobre essa anotao); essas anotaes so consideradas como Anotaes Lgicas. Note que essas anotaes no definem nada relacionado ao banco de dados. Eles definem apenas configuraes para o comportamento da classe como Entity. No cdigo acima tambm possvel ver as anotaes @Table, @Column e @Basic. Essas anotaes definem informaes relacionadas ao banco de dados. Note que possvel definir o nome da tabela, nome da coluna e diversas outras configuraes relacionados ao banco de dados fsico. Esse tipo de anotao conhecido por Anotaes Fsicas, pois esto diretamente ligadas ao banco de dados. No sero abordadas nesse post as possveis opes para cada anotao, mas bastante fcil de encontrar na internet a utilizao para opo. Por exemplo: a anotao@Column(name = PERSON_NAME, length = 100, unique = true, nullable = false). As opes das anotaes fsicas so mais fceis de entender pois parecem bastante com as configuraes de um banco de dados.

14

Captulo 6: Gerando Id: Definio, Id por Identity ou Sequence


Como foi dito na pgina 5, toda Entity obrigada a ter um ID. O JPA vem com a opo de gerao de ID de um modo automtico e prtico. Atualmente existem trs modos para gerao de um ID:

Identity Sequence TableGenerator

necessrio entender que cada banco adota um modo de gerao automtica de id. Atualmente o Oracle e o Postgres trabalham com Sequence, o Sql Server e o MySQL trabalham com Identity. No possvel forar o banco de dados a trabalhar com uma gerao de ids automtica que ele no suporta. Os tipos permitidos para ids simples so: byte/Byte, int/Integer, short/Short, long/Long, char/Character, String, BigInteger, java.util.Date e java.sql.Date.

Identity
Esse o tipo de gerao automtica mais simples que existe. Basta anotar o atributo id como abaixo:
1 import javax.persistence.Entity; 2 import javax.persistence.GeneratedValue; 3 import javax.persistence.GenerationType; 4 import javax.persistence.Id; 5 6 @Entity 7 public class Person { 8 9 @Id 10 @GeneratedValue(strategy = GenerationType.IDENTITY) 11 private int id; 12 13 14 15 16 17 18 19 private String name; public String getName() { return name; } public void setName(String name) {

15

20 21 22 }

this.name = name; }

Quem controla qual ser o prximo ID o prprio banco de dados, sem atuao do JPA. necessrio que primeiro a entidade seja persistida, e aps a realizao o commit, uma consulta ao banco ser realizada para descobrir qual ser o ID da entidade em questo. Pode inclusive haver uma pequena perca de desempenho, mas nada alarmante. Esse tipo de esquema de gerao no permite que blocos de Ids sejam alocados para facilitar no desempenho. A alocao de ids blocos ser explicada a seguir.

Sequence
O tipo de gerao de id por Sequence funciona conforme abaixo:

1 import javax.persistence.Entity; 2 import javax.persistence.GeneratedValue; 3 import javax.persistence.GenerationType; 4 import javax.persistence.Id; 5 import javax.persistence.SequenceGenerator; 6 7 @Entity 8 @SequenceGenerator(name = Car.CAR_SEQUENCE_NAME, sequenceName = Car.CAR_SEQUENCE_NAME, initialValue = 10, allocationSize = 53) 9 public class Car { 10 11 public static final String CAR_SEQUENCE_NAME = "CAR_SEQUENCE_ID"; 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 } @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = CAR_SEQUENCE_NAME) private int id; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; }

Sobre o cdigo acima:

16

A anotao @SequenceGenerator define que existir uma Sequence no banco com o nome descrito nela (atributo sequenceName). Essa Sequence pode ser utilizada por mais classes mas no aconselhvel. Caso a Sequence da classe Car tambm fosse utilizada pela classe House o valor dos Ids no seriam sequenciais. Ao cadastrar o primeiro carro, o id seria um. Ao cadastrar a primeira casa, o id seria dois. O valor name = Car.CAR_SEQUENCE_NAME define por qual nome a Sequence ser conhecida dentro da aplicao. Em qualquer classe que for utilizada no necessrio declar-la novamente, basta utilizar o mesmo nome. Por isso que foi criado um atributo esttico com esse valor.

O valor sequenceName = Car.CAR_SEQUENCE_NAME aponta diretamente para a Sequence no banco de dados. O valor initialValue = 10 define qual ser o valor do primeiro ID. preciso ter bastante cuidado ao se utilizar essa configurao; aps inserir o primeiro valor e reiniciar a aplicao, o JPA tentar novamente inserir um objeto com o mesmo initialValue. O valor allocationSize = 53 define qual o tamanho ids que ficaro alocados na memria do JPA. Funciona do seguinte modo: ao iniciar a aplicao o JPA ir alocar em sua memria a quantidade de ids determinada nesse valor e ir distribuir para cada nova entidade que for salva no banco. Nesse caso, os ids de iriam de do initialValue at + 53. E uma vez que esgote os 53 ids em memria o JPA iria buscar mais 53 ids e guardar na memria novamente. Essa alocao de ids em blocos permite um melhor desempenho pois o JPA no precisar ir ao banco para buscar qual foi o ID gerado para aquele objeto. A anotao @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = CAR_SEQUENCE_NAME) define o tipo de gerao como Sequence e aponta para o nome do gerador que definido na anotao @SequenceGenerator.

17

Captulo 7: Gerando Id: TableGenerator e Auto


TableGenerator
A opo TableGenerator funciona do seguinte modo:

Uma tabela utilizada para armazenar o valor de cada chave. Essa tabela contm uma coluna com o nome da tabela e outra coluna com o valor atual da chave. Essa a nica soluo (at o atual momento) que permite a portabilidade da aplicao entre banco de dados, sem a necessidade de alterao da gerao de ID. Suponha que uma aplicao que utilize Sequence para gerar ID no banco de dados do Postgres e a mesma aplicao ir rodar em um banco SQL Server; essa aplicao ter que gerar um artefato diferente para cada banco de dados. Utilizando o TableGenerator a aplicao poder ser utilizada em qualquer banco independente de qual o tipo de gerao de ID.

Veja o cdigo abaixo de como utilizar o TableGenerator:

1 import javax.persistence.*; 2 3 @Entity 4 public class Person { 5 6 @Id 7 @TableGenerator(name="TABLE_GENERATOR", table="ID_TABLE", pkColumnName="ID_TABLE_NAME", pkColumnValue="PERSON_ID", valueColumnName="ID_TABLE_VALUE") 8 @GeneratedValue(strategy = GenerationType.TABLE, generator="TABLE_GENERATOR") 9 private int id; 10 11 private String name; 12 13 public String getName() { 14 return name; 15 } 16 17 public void setName(String name) { 18 this.name = name; 19 } 20 }

O cdigo acima vai utilizar a seguinte tabela no banco de dados ( possvel que o JPA crie a tabela automaticamente, veja a configurao na pgina sobre o persistence.xml):

18

Sobre a anotao @TableGenerator possvel destacar:


name => o id do TableGenerator dentro da aplicao. table => Nome da tabela que ir conter as chaves de cada tabela. pkColumnName =>Nome da coluna que ir conter o nome de id. No cdigo acima a coluna com o nome id_table_name ser gerada. valueColumnName => Nome da coluna que ir conter os valores de cada id. pkColumnValue => Nome da tabela que ser salvo. O valor default o nome da classe + id. Em nosso caso, PERSON_ID que o mesmo descrito no cdigo acima. initialValue, allocationSize => Apesar de no estarem presentes no exemplo acima, possvel utilizar essas configuraes igual ao demonstrado na gerao por Sequence, veja na pgina anterior.

possvel declarar o TableGenerator em outras entidades sem afetar o sequencial de uma entidade como visto com Sequence (veja na outra pgina).

A melhor prtica para essa abordagem criar o table generator dentro do arquivo orm.xml, esse arquivo utilizado para sobrescrever as configuraes do JPA e est fora do escopo desse post.

Auto
O modo Auto (de automtico) permite que o JPA decida qual estratgia utilizar. Esse o valor padro e para utiliz-lo basta fazer:
1 @Id 2 @GeneratedValue(strategy = GenerationType.AUTO) // or just @GeneratedValue 3 private int id;

O JPA ir escolher o padro de gerao do ID a ser seguido. Os padres podem ser qualquer um dos 3 tipos j citados.

19

Captulo 8: Utilizando Chave Composta Simples


Uma chave simples quando tempos apenas um campo como ID. Um id simples funciona como abaixo:

Uma chave composta necessria quando precisamos de mais de um atributo para ser o identificador da entidade. Existem chaves compostas que so simples ou complexas. So chamadas de chaves compostas simples quando apenas os tipos do Java so utilizados como id (String, int, ). Na prxima pgina se encontra modos de mapear chave compostas complexas. Existem dois modos de utilizar uma chave composta simples, utilizando a anotao @IdClass ou @EmbeddedId.

@IdClass
Veja o cdigo abaixo:

1 import javax.persistence.*; 2 3 @Entity 4 @IdClass(CarId.class) 5 public class Car { 6 7 @Id 8 private int serial; 9 10 @Id 11 private String brand; 12 13 private String name; 14 15 public String getName() {

20

16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 }

return name; } public void setName(String name) { this.name = name; } public int getSerial() { return serial; } public void setSerial(int serial) { this.serial = serial; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; }

Sobre cdigo acima:


@IdClass(CarId.class) => Essa anotao indica que a classe CarId contm os campos considerados com Id. Todos os campos que so marcados com Id devem estar descritos na classe CarId. possvel tambm utilizar @GeneratedValue na chave composta neste tipo de chave composta. No exemplo acima possvel adicionar @GeneratedValue ao atributo serial.

Veja abaixo o cdigo da classe CarId:

1 import java.io.Serializable; 2 3 public class CarId implements Serializable{ 4 5 private static final long serialVersionUID = 343L; 6 7 private int serial; 8 private String brand; 9 10 // must have a default construcot 11 public CarId() { 12 13 } 14 15 public CarId(int serial, String brand) { 16 this.serial = serial;

21

17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 }

this.brand = brand; } public int getSerial() { return serial; } public String getBrand() { return brand; } // Must have a hashCode method @Override public int hashCode() { return serial + brand.hashCode(); } // Must have an equals method @Override public boolean equals(Object obj) { if (obj instanceof CarId) { CarId carId = (CarId) obj; return carId.serial == this.serial && carId.brand.equals(this.brand); } return false; }

A classe CarId contm os campos listados na entidade Car que foram marcados como @Id. Para que uma classe possa ser utilizada como id, ela deve:

Ter um construtor pblico sem argumentos Implementar a interface Serializable Sobrescrever os mtodos hashCode/equals

E para buscar um carro no banco de dados utilizando chave composta, basta fazer como abaixo:

1 EntityManager em = // get valid entity manager 2 3 CarId carId = new CarId(33, "Ford"); 4 5 Car persistedCar = em.find(Car.class, carId); 6 7 System.out.println(persistedCar.getName() + " - " + persistedCar.getSerial());

22

@Embeddable
O outro modo de mapear uma chave composta :
1 import javax.persistence.*; 2 3 @Entity 4 public class Car { 5 6 @EmbeddedId 7 private CarId carId; 8 9 private String name; 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 } public String getName() { return name; } public void setName(String name) { this.name = name; } public CarId getCarId() { return carId; } public void setCarId(CarId carId) { this.carId = carId; }

Sobre o cdigo acima:


A classe de ID foi colocada dentro da classe Car. A anotao @EmbeddedId utilizada para identificar a classe de ID No existe mais a necessidade de utilizar a anotao @Id dentro da classe.

A classe id ficar como abaixo:

1 import java.io.Serializable; 2 3 import javax.persistence.Embeddable; 4 5 @Embeddable 6 public class CarId implements Serializable{ 7 8 private static final long serialVersionUID = 343L; 9 10 private int serial; 11 private String brand;

23

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 }

// must have a default construcot public CarId() { } public CarId(int serial, String brand) { this.serial = serial; this.brand = brand; } public int getSerial() { return serial; } public String getBrand() { return brand; } // Must have a hashCode method @Override public int hashCode() { return serial + brand.hashCode(); } // Must have an equals method @Override public boolean equals(Object obj) { if (obj instanceof CarId) { CarId carId = (CarId) obj; return carId.serial == this.serial && carId.brand.equals(this.brand); } return false; }

Sobre o cdigo acima:


A anotao @Embeddable utilizada para habilitar a classe para ser chave composta. Os campos dentro da classe j sero considerados como ids.

Para que uma classe possa ser utilizada como id, ela deve:

Ter um construtor pblico sem argumentos Implementar a interface Serializable Sobrescrever os mtodos hashCode/equals

Para realizar consultas possvel utilizar o mesmo mtodo de consulta que no exemplo do @IdClass.

24

Captulo 9: Utilizando Chave Composta Complexa


Podemos definir uma chave composta complexa quando o id de uma classe composto de outras entidades. Imagine por exemplo a entidade DogHouse (casa de cachorro) onde ela utiliza como id o id do prprio cachorro. Veja o cdigo abaixo:
1 import javax.persistence.*; 2 3 @Entity 4 public class Dog { 5 @Id 6 private int id; 7 8 private String name; 9 10 public int getId() { 11 return id; 12 } 13 14 public void setId(int id) { 15 this.id = id; 16 } 17 18 public String getName() { 19 return name; 20 } 21 22 public void setName(String name) { 23 this.name = name; 24 } 25 }

1 import javax.persistence.*; 2 3 @Entity 4 public class DogHouse { 5 6 @Id 7 @OneToOne 8 @JoinColumn(name = "DOG_ID") 9 private Dog dog; 10 11 12 13 14 15 private String brand; public Dog getDog() { return dog; }

25

16 17 18 19 20 21 22 23 24 25 26 27 28 }

public void setDog(Dog dog) { this.dog = dog; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; }

Sobre o cdigo acima:


A anotao @Id foi utilizada na entidade Dog para informar ao JPA que o id da entidade DogHouse devem ser o mesmo. Junto com a anotao do @Id encontra-se a anotao @OneToOne para indicar que alm do id em comum, existe um relacionamento entre as classes. Mais informaes sobre a anotao @OneToOne ainda nesse post.

Imagine um caso onde seja necessrio acessar o id da classe DogHouse sem precisar passar pela classe Dog (dogHouse.getDog().getId()). O prprio JPA j providencia um modo de fazer sem a necessidade de utilizar da Lei de Demeter: http://uaihebert.com/?p=62

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

import javax.persistence.*; @Entity public class DogHouseB { @Id private int dogId; @MapsId @OneToOne @JoinColumn(name = "DOG_ID") private Dog dog; private String brand; public Dog getDog() { return dog; } public void setDog(Dog dog) { this.dog = dog; } public String getBrand() {

26

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 }

return brand; } public void setBrand(String brand) { this.brand = brand; } public int getDogId() { return dogId; } public void setDogId(int dogId) { this.dogId = dogId; }

Sobre o cdigo acima:


Existe um campo int anotado com @Id e no mais a classe Dog. A entidade Dog foi anotada com @MapsId. Essa anotao indica ao JPA que ser utilizado o id da entidade Dog como id da classe DogHouse e que esse id desse ser refletido no atributo mapeado com @Id. O JPA no necessita que o atributo dogId esteja no banco de dados. O que acontece que em tempo de execuo ele ir entender que o dogId igual a dog.getId().

Para finalizar esse assunto, como seria uma entidade com id composto com mais de uma entidade? Veja o cdigo abaixo:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

import javax.persistence.*; @Entity @IdClass(DogHouseId.class) public class DogHouse { @Id @OneToOne @JoinColumn(name = "DOG_ID") private Dog dog; @Id @OneToOne @JoinColumn(name = "PERSON_ID") private Person person; private String brand; // get and set }

27

Sobre o cdigo acima:


Veja que as duas entidades (Dog e Person) foram marcadas com a anotao @Id. utilizada a anotao @IdClass para determinar uma classe de chave composta.

1 import java.io.Serializable; 2 3 public class DogHouseId implements Serializable{ 4 5 private static final long serialVersionUID = 1L; 6 7 private int person; 8 private int dog; 9 10 public int getPerson() { 11 return person; 12 } 13 14 public void setPerson(int person) { 15 this.person = person; 16 } 17 18 public int getDog() { 19 return dog; 20 } 21 22 public void setDog(int dog) { 23 this.dog = dog; 24 } 25 26 @Override 27 public int hashCode() { 28 return person + dog; 29 } 30 31 @Override 32 public boolean equals(Object obj) { 33 if(obj instanceof DogHouseId){ 34 DogHouseId dogHouseId = (DogHouseId) obj; 35 return dogHouseId.dog == dog && dogHouseId.person == person; 36 } 37 38 return false; 39 } 40 }

Sobre o cdigo acima:


Veja que a classe contm dois atributos inteiros apontando para as entidades do relacionamento. Note que o nome dos atributos so exatamente iguais aos nomes dos atributos. Se o atributo na entidade DogHouse fosse Person dogHousePerson, o nome dentro da classe de id teria que ser dogHousePerson ao invs de person.

28

Para que uma classe possa ser utilizada como id, ela deve:

Ter um construtor pblico sem argumentos Implementar a interface Serializable Sobrescrever os mtodos hashCode/equals

29

Captulo 10: Modos para obter um EntityManager


Existem dois modos de obter um EntityManager. Um por injeo e outro atravs de um Factory. O modo de injeo o mais simples, quem ir injetar os dados o prprio servidor. Abaixo segue um modo de como injetar um EntityManager:
1 @PersistenceContext(unitName = "PERSISTENCE_UNIT_MAPPED_IN_THE_PERSISTENCE_XML") 2 private EntityManager entityManager;

S foi preciso utilizar a anotao @PersistenceContext(unitName = PERSISTENCE_UNIT_MAPPED_IN_THE_PERSISTENCE_XML) . Entenda que para que a injeo funcione necessrio que sua aplicao rode em um ambiente JEE, ou seja, em um servidor de aplicativos como JBoss, Glassfish Para que um Entity Manager seja injetado corretamente o arquivo persistence.xml deve estar no local correto, e deve-se ter (se necessrio) um datasource corretamente configurado. A injeo de EntityManager, atualmente, s funciona em servidores que suportam EJB. Tomcat e outros no iro realizar a injeo. Para aplicaes desktop ou quando se deseja controlar a transao manualmente mesmo em servidores JEE basta fazer conforme abaixo:
1 EntityManagerFactory emf = Persistence.createEntityManagerFactory("PERSISTENCE_UNIT_MAPPED_IN_THE_PERSISTENCE_XML") ; 2 EntityManager entityManager = emf.createEntityManager(); 3 4 5 6 7 8 9 entityManager.getTransaction().begin(); // do something entityManager.getTransaction().commit(); entityManager.close();

Note que primeiro necessrio criar uma instncia do EntityManagerFactory. Ele ir apontar para o PersistenceUnit mapeado no persistence.xml. Atravs do EntityManagerFactory possvel criar uma nova instncia do EntityManager.

30

Captulo 11: Mapeando duas ou mais tabelas em uma entitade


Uma classe pode conter informaes espalhadas por duas ou mais tabelas. Para mapear uma Entity que tem dados em duas tabelas diferentes utilize as anotaes abaixo:

1 import javax.persistence.*; 2 3 @Entity 4 @Table(name="DOG") 5 @SecondaryTables({ 6 @SecondaryTable(name="DOG_SECONDARY_A", pkJoinColumns={@PrimaryKeyJoinColumn(name="DOG_ID")}), 7 @SecondaryTable(name="DOG_SECONDARY_B", pkJoinColumns={@PrimaryKeyJoinColumn(name="DOG_ID")}) 8 }) 9 public class Dog { 10 @Id 11 @GeneratedValue(strategy = GenerationType.AUTO) 12 private int id; 13 14 private String name; 15 private int age; 16 private double weight; 17 18 // get and set 19 20 }

Sobre o cdigo acima:

A anotao @SecondaryTable utilizada para indicar em qual tabela os dados secundrios sero encontrados. Caso os dados estejam em apenas uma tabela secundria, apenas essa anotao ser suficiente. A anotao @SecondaryTables utilizada para agrupar vrias tabelas em uma classe. Ela agrupa vrias anotaes @SecondaryTable.

31

Captulo 12: Mapeando Heranas MappedSuperclass


Caso seja necessrio compartilhar atributos/mtodos entre classes de uma herana, mas a super classe que ter os dados no uma entidade, necessrio que essa classe tenha o conceito de uma MappedSuperclass. Veja abaixo como ficaria o cdigo de uma MappedSuperclass:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

import javax.persistence.MappedSuperclass; @MappedSuperclass public abstract class DogFather { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }

1 @Entity 2 @Table(name = "DOG") 3 public class Dog extends DogFather { 4 5 @Id 6 @GeneratedValue(strategy = GenerationType.AUTO) 7 private int id; 8 9 private String color; 10 11 public int getId() { 12 return id; 13 } 14 15 public void setId(int id) { 16 this.id = id; 17 } 18 19 public String getColor() { 20 return color; 21 } 22

32

23 24 25 26 }

public void setColor(String color) { this.color = color; }

Sobre o cdigo acima:

Na classe DogFather foi definida a anotao @MappedSuperclass. Com essa anotao toda a classe que herdar da classe DogFather herdar todos seus atributos. Esses atributos sero refletidos no banco de dados.

A MappedSuperclass pode ser tanto abstrata quanto concreta. A classe Dog contm o gerador de ID, apenas Dog uma Entity. DogFather no uma entidade.

Existem algumas dicas e normas para utilizao do MappedSuperclass:


Uma MappedSuperclass no pode ser anotada com @Entity\@Table. Ela no uma classe que ser persistida. Seus atributos/mtodos sero refletidos nas classes filhas. boa prtica sempre defini-la como abstrata. Ela uma classe que no ser consultada diretamente por JPQL ou Queries. No podem ser persistidas, elas no so Entities.

Quando utilizar? Se a classe no tiver a necessidade de ser acessada diretamente nas consultas ao banco de dados, pode-se usar a MappedSuperclass. Caso essa classe venha ter seu acesso direto por pesquisas, aconselhvel utilizar herana de Entity (veja nas prximas pginas).

33

Captulo 13: Mapeando Heranas Single Table


No JPA possvel encontrar diversas abordagens quanto herana. Em uma linguagem a Orientao a Objetos como o Java comum encontrar heranas entre as classes onde todos os dados devem ser persistidos. A estratgia Single Table utilizar apenas uma tabela para armazenar todas as classes da herana. Veja abaixo como utilizar:

1 2 3 4 5 6 7 8 9 10

import javax.persistence.*; @Entity @Table(name = "DOG") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "DOG_CLASS_NAME") public abstract class Dog { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String name; // get and set

11 12 13 14 15 }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

import javax.persistence.Entity; @Entity @DiscriminatorValue("SMALL_DOG") public class SmallDog extends Dog { private String littleBark; public String getLittleBark() { return littleBark; } public void setLittleBark(String littleBark) { this.littleBark = littleBark; } }

1 2 3 4 5

import javax.persistence.*; @Entity @DiscriminatorValue("HUGE_DOG") public class HugeDog extends Dog {

34

6 7 8 9 10 11 12 13 14 15 }

private int hugePooWeight; public int getHugePooWeight() { return hugePooWeight; } public void setHugePooWeight(int hugePooWeight) { this.hugePooWeight = hugePooWeight; }

Sobre o cdigo acima:

@Inheritance(strategy = InheritanceType.SINGLE_TABLE) => essa anotao deve ser utilizada na classe de hierarquia mais alta (classe pai), tambm conhecida por root. Essa anotao define qual ser o padro de hierarquia a ser utilizado, sendo que seu valor default SINGLE_TABLE. @DiscriminatorColumn(name = DOG_CLASS_NAME) => define qual o nome da coluna que ir conter a descrio de qual tabela a linha da tabela no banco de dados ir pertencer. Veja a imagem abaixo para ver como ficar a estrutura da tabela. @DiscriminatorValue => Define qual o valor a ser salvo na coluna descrita na anotao @DiscriminatorColumn. Veja a imagem abaixo para ver como ficar a estrutura da tabela. Note que o ID definido apenas na classe que est mais acima da hierarquia. No permitido reescrever o id de uma classe na hierarquia.

possvel utilizar o campo de descrio da classe com um inteiro tambm:


@DiscriminatorColumn(name = DOG_CLASS_NAME, discriminatorType = DiscriminatorType.INTEGER) => basta definir o tipo da coluna como Inteiro. @DiscriminatorValue(1) => O valor a ser salvo deve ser alterado na Entity tambm. O nmero ser salvo ao invs do texto.

35

Captulo 14: Mapeando Heranas Joined


Ao utilizar o tipo de mapeamento por Joined definido que cada entidade ter seus dados em uma tabela especfica. Ao invs de existir apenas uma tabela com todos os dados, ser utilizada uma tabela por classe para armazenar os dados. Veja abaixo como funciona o modelo de herana por Joined:

1 2 3 4 5 6 7

import javax.persistence.*; @Entity @Table(name = "DOG") @Inheritance(strategy = InheritanceType.JOINED) @DiscriminatorColumn(name = "DOG_CLASS_NAME") public abstract class Dog { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String name; // get and set

8 9 10 11 12 13 14 15 16 }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

import javax.persistence.*; @Entity @DiscriminatorValue("HUGE_DOG") public class HugeDog extends Dog { private int hugePooWeight; public int getHugePooWeight() { return hugePooWeight; } public void setHugePooWeight(int hugePooWeight) { this.hugePooWeight = hugePooWeight; } }

1 2 3 4

import javax.persistence.*; @Entity @DiscriminatorValue("SMALL_DOG")

36

5 public class SmallDog extends Dog { 6 private String littleBark; 7 8 public String getLittleBark() { 9 return littleBark; 10 } 11 12 public void setLittleBark(String littleBark) { 13 this.littleBark = littleBark; 14 } 15 }

Sobre o cdigo acima:


A anotao @Inheritance(strategy = InheritanceType.JOINED) est utilizando o valor JOINED. Veja abaixo como ficaram as tabelas criadas no banco de dados:

Tabela da classe Dog

Tabela da classe HugeDog

Tabela da classe SmallDog

37

Note nas imagens como foram persistidos os dados. Cada entidade teve sua informao distribuda em tabelas distintas; para essa estratgia o JPA utilizar uma tabela para cada classe sendo a classe concreta ou abstrata. Na tabela Dog que contm os dados em comum de todas as classes; na tabela Dog existe um campo para descrever para qual entidade pertence cada linha.

38

Captulo 15: Mapeando Heranas Table Per Concrete Class


A estratgia Table Per Concrete Class cria uma tabela por entidade concreta. Caso uma entidade abstrata seja encontrada em uma hierarquia a ser persistida, essas informaes sero salvas nas classes concretas abaixo dela. Veja o cdigo abaixo:

1 2 3 4 5 6 7 8 9 10 12 13 14 15 16

import javax.persistence.*; @Entity @Table(name = "DOG") @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) public abstract class Dog { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String name; // get and set }

1 2 3 4 5 6 7 8 9 10 11 12 13 14

import javax.persistence.Entity; @Entity public class HugeDog extends Dog { private int hugePooWeight; public int getHugePooWeight() { return hugePooWeight; } public void setHugePooWeight(int hugePooWeight) { this.hugePooWeight = hugePooWeight; } }

1 2 3 4 5

import javax.persistence.Entity; @Entity public class SmallDog extends Dog { private String littleBark;

39

6 7 8 9 10 11 12 13 14 }

public String getLittleBark() { return littleBark; } public void setLittleBark(String littleBark) { this.littleBark = littleBark; }

Sobre o cdigo acima:


@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) => indicado que uma tabela por entidade concreta ser utilizado. No existe mais a necessidade da anotao: @DiscriminatorColumn(name =

DOG_CLASS_NAME). Cada classe concreta ter todos os seus dados, com isso no existir mais uma tabela com dados que no pertenam a ela.

No existe mais a necessidade da anotao: @DiscriminatorValue pelo mesmo motivo explicado acima. Abaixo as imagens de como ficaram as estruturas das tabelas:

Tabela HugeDog

Tabela SmallDog

40

Note que os dados da classe abstrata Dog foram salvas dentro da tabela HugeDog e SmallDog.

41

Captulo 16: Prs e Contras dos mapeamentos das heranas


Infelizmente no existe o modo mais correto a seguir, cada abordagem tem sua vantagem e desvantagem. necessrio fazer uma anlise por qual o melhor modelo a ser utilizado para cada situao:

Abordagem

Prs

Contras No pode ter campos no nulos. Imagine um caso onde a classe SmallDog tivesse o atributo no null hairColor no banco de dados. Ao persistir HugeDog

que no tem esse atributo, A estrutura da tabela no banco uma mensagem de erro de dados fica mais fcil de avisando que hairColor analisar. Apenas uma tabela necessria Os atributos das entidades podero ser encontrados em uma nica tabela. Todos os dados persistidos sero encontrados em uma nica tabela. SINGLE_TABLE Em geral tem um bom desempenho. O insert mais custoso. Um insert dever ser feito para cada entidade utilizada na hierarquia. Se existe uma herana C extends B extends A, ao persistir um C, trs inserts sero realizados um em cada entidade mapeada. estava null seria enviada pelo banco de dados.

JOINED

Cada entidade uma tabela contendo seus dados.

42

Quanto maior for a hierarquia, maior ser o Ir seguir o conceito OO aplicado no modelo de dados. nmero de JOINs realizado em cada consulta.

Quando a consulta realizada Colunas sero repetidas. As em apenas uma entidade seu retorno mais rpido. Em uma tabela constam apenas os dados da classe. colunas referentes aos atributos das classes abstratas sero repetidas nas tabelas das classes filhas. Quando uma consulta realizada para trazer mais de uma entidade da herana, TABLE_PER_CLASS essa pesquisa ter um custo maior. Ser utilizado UNION ou uma consulta por tabela.

43

Captulo 17: Embedded Objects


Embedded Objects um modo de organizar entidades que tem diferentes dados mesma tabela. Para deixar mais simples, imagine uma tabela person (pessoa) onde se encontram os dados referentes pessoa (nome, idade) e ao seu endereo (rua, nmero da casa, bairro, cidade). Veja abaixo a estrutura da tabela:

Como possvel ver, existem dados relacionados para as classes pessoa e casa. Veja como ficar a entidade Person (pessoa) e Address (endereo) ao utilizar o conceito de Embedded Objects:

1 import javax.persistence.*; 2 3 @Embeddable 4 public class Address { 5 @Column(name = "house_address") 6 private String address; 7 8 @Column(name = "house_color") 9 private String color; 10 11 @Column(name = "house_number") 13 private int number; 14 15 16 17 18 19 20 21 22 23 24 } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } // get and set

44

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

import javax.persistence.*; @Entity @Table(name = "person") public class Person { @Id private int id; private String name; private int age; @Embedded private Address address; public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } // get and set }

Sobre o cdigo acima:

A anotao @Embeddable (class Address) indica que essa classe ser utilizada dentro de uma entidade, note que Address no uma entidade. apenas uma classe java que ir refletir dados do banco de dados de um modo organizado. Foi utilizada a anotao @Column dentro da classe ADDRESS para indicar qual o nome da coluna dentro da tabela person. A anotao @Embedded (da classe Person) indica que o JPA deve mapear os campos que esto dentro da classe Address como se os dados pertencessem classe Person. A classe Address pode ser utilizada em outras entidades. Existem modos inclusive de sobrescrever o valor definido na anotao @Column em tempo de execuo.

45

Captulo 18: ElementCollection Como mapear uma lista de valores em uma classe
Em diversas modelagens se torna necessrio o mapeamento de uma lista de valores que no so entidades para dentro de uma classe; por exemplo: pessoa tem e-mails, cachorro tem apelidos, etc. Veja o cdigo abaixo para realizar esse mapeamento:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

import java.util.List; import java.util.Set; import javax.persistence.*; @Entity @Table(name = "person") public class Person { @Id @GeneratedValue private int id; private String name; @ElementCollection @CollectionTable(name = "person_has_emails") private Set<String> emails; @ElementCollection(targetClass = CarBrands.class) @Enumerated(EnumType.STRING) private List<CarBrands> brands; // get and set }

1 public enum CarBrands { 2 FORD, FIAT, SUZUKI 3 }

Sobre o cdigo acima:

46

Note que foi mapeado um Set<String> e um List<CarBrand>. O ElementCollection no est direcionado para uma Entidade, mas para atributos simples (um String e um Enum). A anotao @ElementCollection utilizada para marcar um atributo como um item que poder se repetir diversas vezes. A anotao @Enumerated(EnumType.STRING) foi utilizada no ElementCollection de enum. Ela define como o Enum ser salvo no banco de dados, se por STRING ou ORDINAL (veja mais informaes: http://uaihebert.com/?p=43). @CollectionTable(name = person_has_emails) => Indica qual ser a tabela que ter a informao. Quando essa anotao no encontrada o JPA ir definir um valor default. No caso do cdigo acima, para o Enum de List<CarBrand> brands foi utilizada a tabela: person_brands. O nome padro se d juntando o nome da entidade atual mais o nome do atributo.

47

Captulo 19: OneToOne (Um para Um) Unidirecional e Bidirecional


muito fcil de encontrar entidades que tenham algum relacionamento. Uma pessoa tem cachorros, cachorros tm pulgas, pulgas tm hum deixa para l.

Unidirecional
O relacionamento um para um o mais simples. Vamos imagina a situao onde uma pessoa (Person) tem um celular (Cellular), e apenas a entidade Person ter acesso ao Cellular. Veja a imagem abaixo:

Veja como ficar a classe Person:

import javax.persistence.*;

2 3 @Entity 4 public class Person { 5 6 @Id 7 @GeneratedValue 8 private int id; 9 10 private String name; 11 12 @OneToOne 13 @JoinColumn(name="cellular_id") 14 private Cellular cellular; 15 16 // get and set 17 }

1 2

import javax.persistence.*;

48

3 @Entity 4 public class Cellular { 5 6 @Id 7 @GeneratedValue 8 private int id; 9 10 private int number; 11 12 // get and set 13 }

Sobre o cdigo acima:

Em um relacionamento Unidirecional, apenas um lado conhece o outro. Note que a classe Person conhece Cellular, mas a classe Cellular no conhece Person. possvel fazer person.getCellular() mas no possvel fazer o inverso cellular.getPerson(). Na entidade Person foi utilizado a anotao @OneToOne. Essa anotao indica ao JPA que existe um relacionamento entre essas entidades.

Todo o relacionamento necessita que umas das entidades seja a dona desse relacionamento. Ser dona do relacionamento nada mais do ter a chave estrangeira na tabela do banco de dados. No exemplo acima possvel ver na classe Person a utilizao da anotao @JoinColumn. Essa anotao indica que a chave estrangeira ficar dentro da tabela person, fazendo com que a entidade Person seja a dona do relacionamento.

Bidirecional
Para deixar esse relacionamento bidirecional necessrio alterar apenas a classe Cellular. Veja abaixo com ela ficou:

1 import javax.persistence.*; 2 3 @Entity 4 public class Cellular { 5 6 @Id 7 @GeneratedValue 8 private int id; 9 10 private int number; 11 12 @OneToOne(mappedBy="cellular")

49

13 14 15 16 }

private Person person; // get and set

Sobre o cdigo acima:


Veja que utilizada a mesma anotao @OneToOne da entidade Person. Foi utilizado o atributo mappedBy na anotao @OneToOne. Esse atributo indica que a entidade Person a dona do relacionamento; a chave estrangeira deve ficar na tabela Person e no na tabela Cellular.

E necessrio que o desenvolvedor tenha sempre em mente que, para o correto funcionamento da aplicao, em um relacionamento recomendado que s exista um dono do relacionamento. Se a anotao @OneToOne da entidade Cellular estivesse sem o mappedBy, o JPA trataria a classe Cellular como dona do relacionamento tambm. No aconselhvel deixar sem o mappedBy nos dois lados do relacionamento ou colocar mappedBy nos dois lados do relacionamento. O mappedBy deve apontar para o nome do atributo e no para o nome da classe.

No existe auto relacionamento


Para que um relacionamento OneToOne bidirecional funcione corretamente necessrio fazer como abaixo:

1 2

person.setCellular(cellular); cellular.setPerson(person);

O JPA usa o princpio do Java de referncia, uma classe precisa fazer referncia para outra. O JPA no criar um relacionamento de modo automtico para que o relacionamento exista dos dois lados, necessrio fazer o processo acima.

50

Captulo 20: OneToMany (um para muitos) e ManyToOne (muitos para um) Unidirecional e Bidirecional
O relacionamento OneToMany utilizado quando uma entidade tem uma lista de outra entidade, um Cellular tem muitas Calls (ligaes), mas uma Call (ligao) s pode ter um Cellular. O relacionamento OneToMany representado por uma lista como atibuto, pois est relacionado a mais de um registro. Vamos comear pelo lado ManyToOne:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import javax.persistence.*; @Entity public class Call { @Id @GeneratedValue private int id; @ManyToOne @JoinColumn(name = "cellular_id") private Cellular cellular; private long duration; // get and set }

Sobre o cdigo acima:


Foi utilizada a anotao @ManyToOne. Note que j foi utilizada a anotao @JoinColumn para definir quem ser o dono do relacionamento. O lado do relacionamento que tiver a anotao @ManyToOne ser sempre dominante. No existe a opo de utilizar mappedBy dentro da anotao @ManyToOne

Para realizar um relacionamento bidirecional necessrio alterar a entidade Cellular (que foi criada na pgina anterior). Veja como ficar a entidade:

import javax.persistence.*;

51

2 3 4

@Entity public class Cellular { @Id @GeneratedValue private int id; @OneToOne(mappedBy = "cellular") private Person person; @OneToMany(mappedBy = "cellular") private List<Call> calls; private int number; // get and set

5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 }

Sobre o cdigo acima:


Foi utilizada a anotao @OneToMany. Essa anotao deve ser colocada sobre uma coleo. Foi utilizado o mappedBy para indicar que esse relacionamento no o lado dominante.

Todo o relacionamento necessita que umas das entidades seja a dona desse relacionamento. Ser dona do relacionamento nada mais do ter a chave estrangeira na tabela do banco de dados. No exemplo acima possvel ver na classe Call a utilizao da anotao @JoinColumn. Essa anotao indica que a chave estrangeira ficar dentro da tabela Call. O atributo mappedBy deve apontar para o nome do atributo e no para o nome da classe.

No existe auto relacionamento


Para que um relacionamento OneToMany/ManyToOne bidirecional funcione corretamente necessrio fazer como abaixo:

1 2

call.setCellular(cellular); cellular.setCalls(calls);

O JPA usa o princpio do Java de referncia. Ele no criar um relacionamento de modo automtico. Para que o relacionamento exista dos dois lados necessrio fazer o processo acima.

52

Captulo 21: ManyToMany (muitos para muitos) Unidirecional e Bidirecional


Um exemplo para um relacionamento ManyToMany poderia que uma pessoa pode ter vrios cachorros e um cachorro pode ter vrias pessoas (imagine um cachorro que more numa casa com 15 pessoas). Para a abordagem ManyToMany necessrio utilizar uma tabela adicional para realizar a juno desse relacionamento. Teramos uma tabela person (pessoa), uma tabela dog (cachorro) e uma tabela de relacionamento chamada person_dog. Tabela person_dog teria apenas o id da pessoa, e o id cachorro indicando qual pessoa tem qual cachorro. Veja nas imagens abaixo de como ficar o banco de dados: Tabela person

Tabela dog

Tabela person_dog

53

Note que na tabela person_dog contm apenas ids. Veja como ficar a entidade Person:

1 2 3 4 5 6 7 8 9 10 11 12

import java.util.List; import javax.persistence.*; @Entity public class Person { @Id @GeneratedValue private int id; private String name;

13 14 @ManyToMany 15 @JoinTable(name = "person_dog", joinColumns = @JoinColumn(name = "person_id"), inverseJoinColumns = @JoinColumn(name = "dog_id")) 16 private List<Dog> dogs; 17 18 @OneToOne 19 @JoinColumn(name = "cellular_id") 20 private Cellular cellular; 21 22 // get and set 23 }

Sobre o cdigo acima:


A anotao @ManyToMany utilizada para esse tipo de relacionamento. A anotao @JoinTable foi utilizada para definir qual tabela seria utilizada para realizar armazenas as entidades relacionadas. name define o nome da tabela. joinColumns define qual ser o nome da coluna na tabela da entidade que a dona do relacionamento. inverseJoinColumns define qual ser o nome da coluna na tabela da entidade no dona do relacionamento.

A entidade Person tem o relacionamento com Dog unidirecional. Veja como ficar a classe Dog caso o relacionamento seja bidirecional:

import java.util.List;

54

2 3 import javax.persistence.*; 4 5 @Entity 6 public class Dog { 7 8 @Id 9 @GeneratedValue 10 private int id; 11 12 private String name; 13 14 15 16 17 18 } @ManyToMany(mappedBy="dogs") private List<Person> persons; // get and set

possvel encontrar a notao @ManyToMany utilizando a opo mappedBy para determinar que a dona do relacionamento a entidade Person. Todo o relacionamento necessita que umas das entidades seja a dona de sse relacionamento. No caso do relacionamento ManyToMany a classe dona do relacionamento a classe que no estiver utilizando a opo mappedBy na anotao @ManyToMany. Caso nenhuma classe esteja marcada com mappedBy o JPA ir tratar cada classe marcada com ManyToMany como dona do relacionamento. O mappedBy deve apontar para o nome do atributo e no para o nome da classe.

No existe auto relacionamento


Para que um relacionamento ManyToMany/ManyToMany bidirecional funcione corretamente necessrio fazer como abaixo:

1 2

person.setDog(dogs); dog.setPersons(persons);

O JPA usa o princpio do Java de referncia. Ele no criar um relacionamento de modo automtico. Para que o relacionamento exista dos dois lados necessrio fazer o processo acima.

55

Captulo 22: ManyToMany com campos adicionais


Imagine que voc tem a entidade Person em um relacionamento ManyToMany com Dog; e que toda vez que uma pessoa adotasse um cachorro a data de adoo fosse salva. Esse valor tem que estar ligado ao relacionamento, e no nas entidades cachorro e pessoa. para esse tipo de situao que existe a chamada Classe Associativa ou Entidade Associativa. Com as classes associativas possvel armazenar informaes extras ao criar um relacionamento de ManyToMany. A imagem abaixo explica melhor a necessidade dessa classe:

Para realizar esse mapeamento para entidades, basta fazer como abaixo:

1 2 3 4 5 6 7

import java.util.List; import javax.persistence.*; @Entity public class Person {

56

8 9 10 11 12 13 14 15 16 17 18 }

@Id @GeneratedValue private int id; private String name; @OneToMany(mappedBy = "person") private List<PersonDog> dogs; // get and set

1 2 3

import java.util.List; import javax.persistence.*;

4 5 @Entity 6 public class Dog { 7 8 @Id 9 @GeneratedValue 10 private int id; 11 12 private String name; 13 14 @OneToMany(mappedBy = "dog") 15 private List<PersonDog> persons; 16 17 // get and set 18 }

Foi utilizado um cdigo simples com a anotao @OneToMany em conjunto com mappedBy. Note que no existe um relacionamento @ManyToMany diretamente entre as entidades, mas existe uma classe chamada PersonDog que faz a unio entre as duas entidades. Abaixo o cdigo da classe PersonDog:

1 2 3 4 5 6 7 8 9

import java.util.Date; import javax.persistence.*; @Entity @IdClass(PersonDogId.class) public class PersonDog { @Id

57

10 11 12 13 14 15 16 17 18 19 20 21 22 23 }

@ManyToOne @JoinColumn(name="person_id") private Person person; @Id @ManyToOne @JoinColumn(name="dog_id") private Dog dog; @Temporal(TemporalType.DATE) private Date adoptionDate; // get and set

Existe nessa classe um relacionamento para cachorro e pessoa, e possvel encontrar um campo para armazenar a data da adoo. Existe uma classe que ter o ID entre o relacionamento PersonDogId:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

import java.io.Serializable; public class PersonDogId implements Serializable { private static final long serialVersionUID = 1L; private int person; private int dog; public int getPerson() { return person; } public void setPerson(int person) { this.person = person; } public int getDog() { return dog; } public void setDog(int dog) { this.dog = dog; } @Override public int hashCode() { return person + dog; } @Override public boolean equals(Object obj) { if(obj instanceof PersonDogId){ PersonDogId personDogId = (PersonDogId) obj; return personDogId.dog == dog && personDogId.person == person; }

58

38 39 40 }

return false; }

Um detalhe interessante que os atributos presentes na classe PersonDogId tem o mesmo nome dos atributos Person e Dog encontrados na entidade PersonDog. O nome deve ser exatamente igual ao atributo, como o JPA consegue localizar corretamente as informaes. Para mais informaes sobre chave composta simples e chave composta complexa ver captulos 08 e 09.

59

Captulo 23: Como funciona o Cascade? Para que serve o OrphanRemoval? Como tratar a org.hibernate.TransientObjectException
bastante comum que duas ou mais entidades sofram alteraes em uma transao. Ao alterar dados de uma pessoa, pode-se alterar nome, endereo, idade, cor do carro; apenas para as alteraes citadas trs classes poderiam ser afetadas Person, Car e Address. Em geral so alteraes simples e que poderiam se resumir ao cdigo abaixo

1 2 3

car.setColor(Color.RED); car.setOwner(newPerson); car.setSoundSystem(newSound);

Mas se o cdigo abaixo fosse executado a famosa exceo org.hibernate.TransientObjectException apareceria:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

EntityManager entityManager = // get a valid entity manager Car car = new Car(); car.setName("Black Thunder"); Address address = new Address(); address.setName("Street A"); entityManager.getTransaction().begin(); Person person = entityManager.find(Person.class, 33); person.setCar(car); person.setAddress(address); entityManager.getTransaction().commit(); entityManager.close();

Para o EclipseLink gera a seguinte mensagem de erro: Caused by: java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST.

60

Mas o que significa dizer que um objeto transiente? Ou que esse relacionamento no est marcado com cascade PERSIST? O JPA funciona como um rastreador de toda entidade que for envolvida em uma transao. Uma entidade envolvido em uma transao uma entidade que ser salva, alterada, apagada. O JPA precisa saber quem aquela entidade, de onde veio e para onde vai. Ao abrir uma transao toda entidade que carregada do banco de dados est attached. O attached nada mais quer dizer que aquela entidade est dentro da transao, sendo monitorado pelo JPA. Uma entidade estar attached enquanto a transao estiver aberta (regra para aplicaes J2SE ou aplicaes que no utilizem EJB Stateful com Persistence Scope Extended); para ser considerada attached a entidade precisa ter vindo do banco de dados (por query, ou mtodo find do entity manager, ), ou receber algum comando dentro de transao (merge, refresh). Observe o cdigo abaixo:

1 2 3 4

entityManager.getTransaction().begin(); Car myCar = entityManager.find(Car.class, 33); myCar.setColor(Color.RED); entityManager. getTransaction().commit();

A transao aberta, uma alterao realizada na entidade e que a transao recebe o commit. No foi necessrio realizar um update diretamente na entidade, bastou simplesmente realizar o commit da transao para que a alterao fosse salva no banco de dados. A alterao no carro foi salva no banco de dados por que a entidade est attached, qualquer alterao sofrida por uma entidade attached ser refletida no banco de dados aps o commit da transao ou o comando flush(). No cdigo acima a entidade myCar foi localizada no banco de dados dentro de uma transao, por isso ela est attached aquele Persistence Context. Podemos definir Persistence Context como um local em que o JPA toma conta de todos os objetos, uma grande sacola. Veja a imagem abaixo que ir explicar melhor essa situao:

61

62

Note na imagem acima que aps o carro ser localizado no banco de dados ele foi adicionado ao Persistence Context. Toda alterao realizada na entidade ser monitorada pelo JPA. Aps a transao ser finalizada (ou comando flush), o prprio JPA ir refletir essa alterao no banco de dados. A exceo acontece quando relacionamos uma entidade com a outra. Veja o cdigo e a imagem abaixo que ir explicar melhor a situao:

1 2 3 4 5 6

entityManager.getTransaction().begin(); Person newPerson = new Person(); newPerson.setName("Mary"); Car myCar = entityManager.find(Car.class, 33); myCar.setOwner(newPerson); entityManager. getTransaction().commit();

A entidade Car agora est relacionada com a entidade Person. O problema que a pessoa se encontra fora do Persistence Context, note que a entidade Person no foi localizada no banco de dados ou attached transao. Ao realizar o commit o JPA no consegue reconhecer que a pessoa uma entidade nova, que ainda no existe no banco. Mesmo que o objeto pessoa fosse um objeto j salvo, ao vir de fora da transao (de um ManagedBean do JSF ou de uma Action do Struts por exemplo), ele ainda assim no seria reconhecido nesse Persistence Context. Um objeto fora do Persistence Context chamado de detached. Essa situao de uma entidade detached pode ocorrer em qualquer tipo de ao do JPA: INSERT, UPDATE, DELETE

63

Para ajudar o desenvolvedor nessas situaes o JPA criou a opo Cascade. Essa opo pode ser definidas anotaes @OneToOne, @OneToMany e @ManyToMany. Existe um enum que contm todas as opes de cascade possvel: javax.persistence.CascadeType. O cascade tem as opes abaixo:

CascadeType.DETACH CascadeType.MERGE CascadeType.PERSIST CascadeType.REFRESH CascadeType.REMOVE CascadeType.ALL

O Cascade tem por conceito de propagar a ao configurada no relacionamento. Veja o cdigo abaixo:

1 import javax.persistence.*; 2 3 @Entity 4 public class Car { 5 6 @Id 7 @GeneratedValue 8 private int id; 9 10 private String name; 11 12 13 14 15 16 } @OneToOne(cascade = CascadeType.PERSIST) private Person person; // get and set

No cdigo acima foi definido que o relacionamento @OneToOne com Person sofreria a ao Cascade.PERSIST toda vez que o comando entityManager.persist(car) for executado; toda vez que um carro for persistido o JPA realizar o persist tambm no relacionamento com a entidade Person. A vantagem do Cascade a propagao automaticamente da ao configurada nos relacionamentos da entidade. Veja na tabela abaixo todas as opes de Cascade possveis at hoje:

64

Tipo

Ao Quando uma entidade for retirada do Persistence Context (o que provoca que ela esteja detached) essa ao ser refletida nos relacionamentos. Quando uma entidade tiver alguma informao alterada (update) essa ao ser refletida nos relacionamentos. Quando uma entidade for nova e inserida no banco de dados essa

Disparado por

CascadeType.DETACH

Persistence Context finalizado ou por comando especfico: entityManager.detach(), entityManager.clear(). Quando a entidade for alterada e a transao finalizada ou por comando especfico: entityManager.merge(). Quando uma transao finalizada ou por comando especfico: entityManager.persist().

CascadeType.MERGE

CascadeType.PERSIST

ao ser refletida nos relacionamentos.

Quando uma entidade tiver seus dados sincronizados com o banco de dados essa ao ser refletida Por comando especfico: CascadeType.REFRESH nos relacionamentos. Quando uma entidade for removida (apagada) do banco de CascadeType.REMOVE dados essa ao ser refletida nos Por comando especfico: relacionamentos. entityManager.remove(). Quando qualquer ao citada acima for invocada pelo JPA ou CascadeType.ALL por comando, essa ao ser refletida no objeto. Por qualquer ao ou comando listado acima. entityManager.refresh().

Uma vez que o cascade fosse definido, o cdigo abaixo seria executado sem mensagem de erro:

1 2 3 4 5 6

entityManager.getTransaction().begin(); Person newPerson = new Person(); newPerson.setName("Mary"); Car myCar = entityManager.find(Car.class, 33); myCar.setOwner(newPerson); entityManager. getTransaction().commit();

Observaes sobre o Cascade:

65

preciso ter bastante cuidado ao anotar um relacionamento com CascadeType.ALL. Uma vez que se a entidade fosse removida o seu relacionamento tambm seria removido do banco de dados. No exemplo acima, caso a anotao CascadeType.ALL fosse encontrado na entidade Car, ao seu excluir um carro a pessoa tambm seria excluda do banco de dados.

CascadeType.ALL (ou opes nicas) pode causar lentido em todas as aes realizadas na entidade. Caso a entidade tenha muitas listas um merge() poderia causar todas as listas a receberem merge(). car.setOwner(personFromDB) => se a pessoa atribuda ao relacionamento for uma pessoa que j exista no banco mas est detached, o Cascade no ir ajudar nesse caso. Ao executar o comando entityManager.persist(car) o JPA tentar executar o persist nos objeto do relacionamento marcado com CascadeType.PERSIST (entityManager.persist(person)). Uma vez que a pessoa j existe no banco o JPA tentar inserir novamente e uma mensagem de erro ser exibida. Seria necessrio um objeto atualizado do banco de dados e o melhor modo de se fazer utilizando o mtodo getReferenceOnly(): http://uaihebert.com/?p=1137.

Para que o Cascade seja disparado pelo JPA preciso sempre executar a ao na entidade que contm a opo do cascade configurada. Veja o cdigo abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import javax.persistence.*; @Entity public class Car { @Id @GeneratedValue private int id; private String name; @OneToOne(cascade = CascadeType.PERSIST) private Person person; // get and set }

1 2 3 4 5 6 7 8 9 10 11 11 12 13 14

import javax.persistence.*; @Entity public class Person { @Id private int id; private String name; @OneToOne(mappedBy="person") private Car car; // get and set }

66

Analisando o cdigo acima o modo correto de persistir a entidade e acionar o cascade :


1 entityManager.persist(car);

Desse modo o JPA ir analisar a classe carro e procurar por relacionamentos onde o cascade deve ser aplicado. Caso o comando abaixo fosse executado o erro de objeto transiente seria exibido:
1 entityManager.persist(person);

Lembre-se: o cascade s acionado pelo JPA quando a ao for executada pela entidade em que o Cascade foi definido. No caso acima, apenas a classe Car definiu Cascade para Person, apenas a classe Car ir disparar a funo de Cascade.

OrphanRemoval
Outra opo parecida ao CascadeType.REMOVE o OrphanRemoval. A opo OrphanRemoval conceitualmente aplicada em casos onde uma entidade usada apenas em outra. Imagine a situao onde a entidade Address s vai existir dentro de um Person:

Caso uma pessoa seja salva no banco de dados uma entidade endereo tambm ser criada. Conceitualmente a entidade Address s existe quando uma Person criada; ao excluir uma pessoa seu endereo deve ser removido tambm. possvel perceber que bastaria adicionar o CascadeType.REMOVE no relacionamento que o endereo seria removido juntamente com a pessoa, mas ainda existe outra diferena.

67

Para casos como Person e Address que foi criado o a opo OrphanRemoval. O OrphanRemoval tem quase a mesma funo do CascadeType.REMOVE, mas conceitualmente deve ser aplicado nos casos de Composio. Veja o exemplo abaixo:

1 2 3 4 5 6 7 8 9 10 11 12 13

import javax.persistence.*; @Entity public class Address { @Id @GeneratedValue private int id; private String name; // get and set }

1 2 3 4 5 6 7

import javax.persistence.*; @Entity public class Person { @Id private int id; private String name; @OneToMany(orphanRemoval=true) private Address address; // get and set

8 9 10 11 12 13 14 15 }

Imagine que a entidade Address ser utilizada com Person apenas e nenhuma outra entidade utiliza essa classe. Conceitualmente essa o local ideal para se aplicar o OrphanRemoval. Outro fator que difere o OrphanRemoval do CascadeType.REMOVE que ao colocar o relacionamento para null a entidade ser removida do banco de dados. Veja o cdigo abaixo:

68

person.setAddress(null);

Quando a entidade Person fosse atualizada no banco de dados a entidade Address seria excluda do banco de dados. Essa opo est disponvel apenas os relacionamentos: @OneToOne e @OneToMany.

69

Captulo 24: Como excluir corretamente uma entidade com relacionamentos. Capturar entidades relacionadas no momento da excluso de um registro no banco de dados
Ao excluir uma entidade do banco de dados o um erro de constraint pode aparecer, algo parecido com java.sql.SQLIntegrityConstraintViolationException. Esse erro pode acontecer caso a entidade Person esteja relacionada a uma entidade Car, por exemplo, e o CascadeType.REMOVE no estiver configurado (ver pgina anterior sobre cascade). Imagine que a pessoa de id 33 est relacionada com o carro de id 44 no banco de dados. O cdigo abaixo causaria um erro de violao de chave:

entityManager.remove(person);

possvel resolver esse erro de algumas maneiras:


CascadeType.REMOVE => Uma vez que a entidade for apagada o JPA ficar responsvel de eliminar essa dependncia. Na pgina anterior desse post mostra como fazer isso. OrphanRemoval => Uma soluo que pode ser aplicada, mas com algumas restries de relacionamentos. Na pgina anterior desse post mostra como fazer isso. Colocar o relacionamento para null antes de realizar a excluso. Veja abaixo como fazer:

1 2

person.setCar(null); entityManager.remove(person);

Existe um grande problema em localizar qual o relacionamento est pendente. O que pode ser feito capturar, na mensagem de erro qual a classe com padro. Essa soluo no

70

uma soluo precisa pois ser necessrio capturar a mensagem de erro (em String) e localizar o nome da tabela. Infelizmente essa mensagem pode variar de implementao do JPA, JDBC driver e a linguagem em que foi desenvolvido.

71

Captulo 25: Criando um EntityManagerFactory por aplicao


Geralmente utilizado um EntityManagerFactory por aplicao quando se controla a transao programaticamente, e no quando o servidor controla a transao. Essa soluo muito utilizada pois carregar um EntityManagerFactory na memria tem um custo relativamente alto, pois o JPA ir analisar todo o banco, validar Entities e demais informaes. Realizar a opo de criar um novo EntityManagerFactory a cada transao fica invivel. Abaixo segue um cdigo que poderia ser utilizado para centralizar a criao de EntityManager na aplicao:

import javax.persistence.*;

2 3 public abstract class ConnectionFactory { 4 private ConnectionFactory() { 5 } 6 7 private static EntityManagerFactory entityManagerFactory; 8 9 public static EntityManager getEntityManager(){ 10 if (entityManagerFactory == null){ 11 entityManagerFactory = Persistence.createEntityManagerFactory("MyPersistenceUnit"); 12 } 13 14 return entityManagerFactory.createEntityManager(); 15 } 16 }

72

Captulo 26: Entendendo o Lazy/Eager Load


Uma entidade pode ter um atributo que ocupe muitos bytes (um arquivo grande por exemplo) ou ento ter uma lista enorme de atributos. Uma pessoa pode ter diversos carros, ou uma imagem atribuda ao seu perfil de 150MB. Veja a classe abaixo:

1 import javax.persistence.*; 2 3 @Entity 4 public class Car { 5 6 @Id 7 @GeneratedValue 8 private int id; 9 10 private String name; 11 12 @ManyToOne 13 private Person person; 14 15 16 } // get and set

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

import java.util.List; import javax.persistence.*; @Entity public class Person { @Id private int id; private String name; @OneToMany(mappedBy = "person", fetch = FetchType.LAZY) private List<Car> cars; @Lob @Basic(fetch = FetchType.LAZY) private Byte[] hugePicture;

73

20 21 }

// get and set

Sobre o cdigo acima:


Note que a coleo cars foi marcada com a opo FetchType.LAZY. Note que a imagem Byte[] hugePicture foi tambm marcada como Lazy.

O Lazy indica que o relacionamento/atributo no ser carregado do banco de dados de modo natural. Ao fazer entityManager.find(Person.class, 33) a lista cars e a imagem hugePicture no sero carregados. Desse modo a quantidade de retorno de dados vinda do banco de dados ser menor. A vantagem dessa abordagem que a query fica mais leve, e o trfego de dados pela rede fica menor. Esses dados podem ser acessados aps um get ser realizado na propriedade. Ao fazer person.getHugePicture() uma nova consulta ser realizada no banco de dados para trazer essa informao. Por padro um atributo/campo simples ser sempre EAGER. Para colocar um atributo/campo simples como Lazy basta utilizar a anotao @Basic como mostrado acima. Os relacionamentos entre entidades tem um comportamento padro:

Relacionamentos que terminam em One sero EAGER por padro: @OneToOne e @ManyToOne. Relacionamentos que terminam em Many sero LAZY por padro: @OneToMany e @ManyToMany.

Para alterar algum relacionamento padro, basta fazer como exibido no cdigo acima. Ao utilizar o Lazy por default o erro chamado Lazy Initialization Exception poder acontecer. Esse erro acontece quando o atributo Lazy acessado sem nenhuma conexo ativa. Veja esse post para mais detalhes sobre esse problema e como resolv-lo : http://uaihebert.com/?p=1367 O problema de utilizar EAGER em todas as listas/atributos da entidade que o custo da consulta no banco de dados pode aumentar muito. Caso todas as entidades Person tenham uma lista de 40.000 aes no log, imagina o custo para trazer 100.000 pessoas do banco de dados? necessrio ter bastante cautela ao escolher qual o tipo de fetch que ser utilizado no atributo/relacionamento.

74

Captulo 27: Tratando o erro: cannot simultaneously fetch multiple bags


Esse erro acontece quando o JPA busca uma entidade com mais de uma coleo no banco de dados de modo EAGER. comum encontrar cdigos nas entidades onde um desenvolvedor para evitar o erro de LazyInitializationException (http://uaihebert.com/?p=1367) atribui a propriedade EAGER para todas as listas (Collection, List, ); ao consultar uma entidade no banco de dados, todos as listas j viriam carregadas. Veja o cdigo abaixo:
1 import java.util.List; 2 3 import javax.persistence.*; 4 5 @Entity 6 public class Person { 7 8 @Id 9 private int id; 10 11 private String name; 12 13 @OneToMany(mappedBy = "person", fetch = FetchType.EAGER, cascade = CascadeType.ALL) 14 private List<Car> cars; 15 16 17 18 19 20 } @OneToMany(fetch = FetchType.EAGER) private List<Dog> dogs; // get and set

Ao executar uma consulta para buscar uma pessoa da entidade acima a seguinte mensagem de erro aparecer : javax.persistence.PersistenceException: org.hibernate.HibernateException: cannot simultaneously fetch multiple bags. Uma lista (List, Collection) tambm pode ser conhecida por bag. Esse erro acontece pois o Hibernate tenta igualar o nmero de resultados vindo em uma consulta. Caso o SQL gerado retorne como resultado 2 linhas para a lista de dogs, e uma para car, o Hibernate ir repetir esse resultado para igualar as linhas da consulta no resultado. Veja a imagem abaixo para entender melhor o que acontece caso o seguinte cdigo fosse executado entityManager.find(Person.class, 33):

75

O problema que ao fazer isso, resultados repetidos apareceriam e quebraria o resultado correto da query. O resultado destacado em vermelho mostra o cdigo que o Hibernate acaba por entender como repetido. Existem 4 solues (at o dia de hoje) que poderiam resolver essa situao:

Utilizar java.util.Set ao invs das outras colees => Com essa simples alterao esse erro poder ser evitado. Utilizar EclipseLink = > uma soluo bastante radical, mas para os usurios de JPA que utilizam apenas as anotaes do JPA a mudana ser de baixo impacto. Utilizar FetchType.LAZY ao invs de EAGER => Essa soluo parcial, pois caso uma consulta seja feita e seja utiliza join fetch nas colees, o problema volta a acontecer. Uma consulta que poderia gerar esse erro : select p from Person p join fetch p.dogs d join fetch p.cars c. Ao optar por essa abordagem o erro de LazyInitializationException (http://uaihebert.com/?p=1367) poder acontecer. Utilizar @LazyCollection ou @IndexColumn do prprio Hibernate na coleo => necessrio conhecer bem a anotao @IndexColumn e suas implicaes quando utilizada, pois seu comportamento varia se colocado em uma ponta do relacionamento ou na outra (no vem ao caso desse post explicar essa anotao).

76

Вам также может понравиться