Escolar Documentos
Profissional Documentos
Cultura Documentos
@Entity - javax.persistence.Entity;
@Entity
public class Category {
...
public Category() { ... }
public Category(String name) { ... }
...
}
@Id - javax.persistence.Id
Marca o atributo com identificador. Pode ser usada tanto baseada em um campo como
em uma propriedade. Em um objeto de domínio pode ter somente uma anotação @Id.
@Entity @Entity
public class Category { public class Category {
@Id ...
public Long id; protected Long id;
public String name; ...
public Date modificationDate;
public Category() {} @Id
} public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
...
}
@Transient - javax.persistence.Transient
Define um campo como transitório, isto é, caso necessário você pode impedir uma
propriedade da entidade de ser persistida, marcando o getter com a anotação
@Transient.
@Entity
public class Category {
...
@Transient
protected Long activeUserCount;
1
transient public String generatedName
...
public Long getActiveUserCount() {
return activeUserCount;
}
public void setActiveUserCount(Long activeUserCount) {
this.activeUserCount = activeUserCount;
}
...
}
@IdClass - javax.persistence.IdClass;
Permite o uso de mais de uma anotação @Id de uma maneira lógica. Esta anotação é
utilizada em caso de entidades com campos compostos, isto é, entidade que possuem
mais de um campo como chave primária.
2
@Entity
@IdClass(CategoryPK.class) |#5
public class Category {
public Category() {}
@Id |#6
protected String name; |#6
@Id |#6
protected Date createDate; |#6
...
}
@EmbeddedId - javax.persistence.EmbeddedId
Usar a anotação @EmbeddedId é como mover a IdClass para a sua entidade e suar o
campos de identidade aninhado para armazenar dados da entidade. O obejto
@Embedded não precisa ser serializable. A anotação @Id e @IdClass não tem
permissão para se usada em conjunto com @Embedded.
@Embeddable |#1
public class CategoryPK {
String name;
Date createDate;
public CategoryPK() {}
public boolean equals(Object other) { |#2
if (other instanceof CategoryPK) {
final CategoryPK otherCategoryPK = (CategoryPK)other;
return (otherCategory.name.equals(name) &&
otherCategoryPK.createDate.equals(createDate));
}
return false;
}
public int hashCode() { |#2
return super.hashCode();
}
}
@Entity
public class Category {
public Category() {}
@EmbeddedId |#3
protected CategoryPK categoryPK;
...
}
@Embeddable - javax.persistence.Embeddable
Esta anotação é usada para designar objetos persistentes que não precisam de uma
identidade própria, um exemplo seria um objeto Address usado dentro do objeto User
como uma alternativa elegante para listar endereço, cidade, estado e etc.
@Embeddable |#1
public class Address {
protected String streetLine1;
protected String streetLine2;
protected String city;
3
protected String state;
protected String zipCode;
protected String country;
...
}
@Entity
public class User {
@Id
protected Long id; |#2
protected String username;
protected String firstName;
protected String lastName;
@Embedded
protected Address address; |#3
protected String email;
protected String phone;
...
}
Relações de Entidades
Uma relação significa que uma entidade possui uma referência de objeto à outra. Cada
tipo de relação é expresso em JPA por meio de uma anotação.
@OneToOne
É usada para marcar relações um-para-um uni e bidirecionais. Esta anotação pode ser
usada tanto para campo quanto para propriedade.
@Entity
public class User {
@Id
protected String userId;
protected String email;
@OneToOne
protected BillingInfo billingInfo;
}
@Entity
public class BillingInfo {
@Id
protected Long billingId;
protected String creditCardType;
protected String creditCardNumber;
protected String nameOnCreditCard;
protected Date creditCardExpiration;
protected String bankAccountNumber;
protected String bankName;
protected String routingNumber;
}
4
Bidirecional um-para-um
@Entity
public class BillingInfo {
@Id
protected Long billingId;
protected String creditCardType;
...
protected String routingNumber;
@OneToOne(mappedBy="billingInfo", optional="false");
protected User user;
}
@OneToMany e @ManyToOne
@Entity
public class Item {
@Id
protected Long itemId;
protected String title;
protected String description;
protected Date postdate;
...
@OneToMany(mappedBy="item")
protected Set<Bid> bids;
...
}
@Entity
public class Bid {
@Id
protected Long bidId;
protected Double amount;
protected Date timestamp;
...
@ManyToOne
protected Item item;
...
}
@ManyToMany
@Entity
public class Category {
5
@Id
protected Long categoryId;
protected String name;
...
@ManyToMany
protected Set<Item> items;
...
}
@Entity
public class Item {
@Id
protected Long itemId;
protected String title;
...
@ManyToMany(mappedBy="items")
protected Set<Category> categories;
...
}
6
Mapeamento Objeto-Relacional (ORM)
O Problema de Impedância
Mapeando as entidades
@Entity
@Table(name="USERS")
@SecondaryTable(name="USER_PICTURES",
pkJoinColumns=@PrimaryKeyJoinColumn(name="USER_ID"))
public class User implements Serializable {
@Id
@Column(name="USER_ID", nullable=false)
protected Long userId;
@Column(name="USER_NAME", nullable=false)
protected String username;
@Column(name="LAST_NAME", nullable=false)
protected String lastName;
@Enumerated(EnumType.ORDINAL)
@Column(name="USER_TYPE", nullable=false)
protected UserType userType;
7
@Column(name="PICTURE", table="USER_PICTURES")
@Lob
@Basic(fetch=FetchType.LAZY)
protected byte[] picture;
@Column(name="CREATION_DATE", nullable=false)
@Temporal(TemporalType.DATE)
protected Date creationDate;
@Embedded
protected Address address;
public User() {}
}
@Embeddable
public class Address implements Serializable {
@Column(name="STREET", nullable=false)
protected String street;
@Column(name="CITY", nullable=false)
protected String city;
@Column(name="STATE", nullable=false)
protected String state;
@Column(name="ZIP_CODE", nullable=false)
protected String zipCode;
@Column(name="COUNTRY", nullable=false)
protected String country;
}
@Table especifica a tabela contendo as colunas e torna disponível para o ORM. Todo
dado de persistência para entidade é mapeado para a tabela especifica pelo parâmetro
da anotação NAME.
@Target(TYPE)
@Retention(RUNTIME)
public @interface Table {
String name() default "";
String catalog() default "";
String schema() default "";
UniqueConstraint[] uniqueConstraints() default {};
}
A anotação @table é opcional por si mesma. Se ela for omitida, a entidade é assumida
para ser mapeada para uma tabela no esquema padrão com o mesmo nome como uma
classe de entidade. Se o parâmetro name for omitido, o nome da tabela é assumido
para ser o mesmo da entidade.
8
1.1.2. Mapeando as colunas
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface Column {
String name() default "";
boolean unique() default false;
boolean nullable() default true;
boolean insertable() default true;
boolean updatable() default true;
String columnDefinition() default "";
String table() default "";
int length() default 255;
int precision() default 0;
int scale() default 0;
}
Na listagem anterior, o campo ‘tipo de usuário’ tem um tipo de UserType, que é uma
enumeração (enumeration) Java definida assim:
public enum UserType {SELLER, BIDDER, CSR, ADMIN};
Significando que um tipo de dado definido como UserType pode ter apenas os quatros
valores. Como uma série, cada elemento da enumeração é associado com um índice
chamado de ordinal. Por exemplo, o valor UserType.SELLER tem um ordinal 0
(zero), o valor UserType.BIDDER tem um ordinal 1 (um), e assim por diante.
Apersistência Java suporta duas opções por meio de anotação @Enumerated:
@Enumerated(EnumType.ORDINAL) @Enumerated(EnumType.STRING)
... ...
protected UserType userType; protected UserType userType;
Isto siguinifica se o campo for marcado Neste caso um valor UserType.ADMIN
para o UserType.SELLER, o valor 0 seria salvo dentro do banco de dados com
(zero) será armazenado na base de dados. “ADMIN”.
9
Por padrão, um campo ou propriedade enumerada é salva como um ordinal. Esse caso
ocorreria se a anotação @Enumerated fosse omitida ou se nenhum parâmetro de
anotação fosse especificado.
@Lob
@Basic(fetch=FetchType.LAZY)
protected byte[] picture;
@Temporal(TemporalType.DATE)
protected Date creationDate;
Alguma vez um dado de entidade deve vir de duas tabelas diferentes. A anotação
@SecondaryTable nos permite extrair dados da entidade de mais de uam tabela, é
definido como segue:
@Target({TYPE}) @Retention(RUNTIME)
public @interface SecondaryTable {
String name();
String catalog() default "";
10
String schema() default "";
PrimaryKeyJoinColumn[] pkJoinColumns() default {};
UniqueConstraint[] uniqueConstraints() default {};
}
@Entity
@Table(name="USERS")
@SecondaryTable(name="USER_PICTURES",
pkJoinColumns=@PrimaryKeyJoinColumn(nam
e="USER_ID"))
public class User implements
Serializable {
..}
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="USER_ID")
protected Long userId;
11
A anotação @SequenceGerator cria um gerador de seguencia chamado
USER_SEQUENCI_GENERATOR como referencia à seqüência Oracle.
O atributo allocantionSize especifica especifica em quanto a seqüência é
incrementada a cada vez que um valor é gerado. Os valores padrão para initialValue e
allocationSize são 0 e 50, respectivamente.
Vale ressaltar que qualquer gerador é compartilhado por todas as entidades no módulo
de persistência e cada gerador deve ser unicamente nomeado. Finalmente,
reimplementaremos a chave gerada para a coluna USER_ID, como segue:
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE,
generator="USER_SEQUENCE_GENERATOR")
@Column(name="USER_ID")
protected Long userId;
O primeiro passo é criar uma tabela para usar na geração de valore seguindo um
formato geral como o seguinte criado para o Oracle:
@TableGenerator (name="USER_TABLE_GENERATOR",
table="SEQUENCE_GENERATOR_TABLE",
pkColumnName="SEQUENCE_NAME",
valueColumnName="SEQUENCE_VALUE",
pkColumnValue="USER_SEQUENCE")
@Id
@GeneratedValue(strategy=GenerationType.TABLE,
generator="USER_TABLE_GENERATOR")
@Column(name="USER_ID")
protected Long userId;
12
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="USER_ID")
protected Long userId;
@Table(name="USERS")
...
public class User implements Serializable {
@Id
@Column(name="USER_ID", nullable=false)
protected Long userId;
...
@Embedded
protected Address address;
...
}
@Embeddable
public class Address implements Serializable {
@Column(name="STREET", nullable=false)
protected String street;
...
@Column(name="ZIP_CODE", nullable=false)
protected String zipCode;
...
}
Note que diferente da entidade User, o objeto embutido Address está perdendo a
anotação @Table. Isto que o EJB3 não permite que objetos embutidos sejam
mapeados para uma tabela diferente da entidade incluída. O mapeamento da anotação
@Column aplicado aos campos do objeto Address, realmente, se refere às colunas na
tabela USERS.
Uns dos recursos mais úteis das classes embutidas é que elas podem ser
compartilhadas entre entidades.
@Entity
@Table(name="USERS")
public class User {
@Id
@Column(name="USER_ID")
protected String userId;
...
@OneToOne
13
@JoinColumn(name="USER_BILLING_ID",
referencedColumnName="BILLING_ID", updatable=false)
protected BillingInfo billingInfo;
}
@Entity
@Table(name="BILLING_INFO")
public class BillingInfo {
@Id
@Column(name="BILLING_ID")
protected Long billingId;
...
}
@Entity
public class BillingInfo {
@OneToOne(mappedBy="billingInfo")
protected User user;
..
}
@Entity
@Table(name="USERS")
public class User {
@Id
@Column(name="USER_ID")
protected Long userId;
...
@OneToOne
@PrimaryKeyJoinColumn(name="USER_ID",
referencedColumnName="BILLING_USER_ID")
protected BillingInfo billingInfo;
14
}
@Entity
@Table(name="BILLING_INFO")
public class BillingInfo {
@Id
@Column(name="BILLING_USER_ID")
protected Long userId;
...
}
@Entity
@Table(name="ITEMS")
public class Item {
@Id
@Column(name="ITEM_ID")
protected Long itemId;
...
@OneToMany(mappedBy="item")
protected Set<Bid> bids;
...
}
@Entity
@Table(name="BIDS")
public class Bid {
@Id
@Column(name="BID_ID")
protected Long bidId;
...
@ManyToOne
@JoinColumn(name="BID_ITEM_ID", referencedColumnName="ITEM_ID")
protected Item item;
...
}
Visto que múltiplas instâncias BIDS fariam referência ao mesmo record na tabela
ITENS, a tabela BIDS deterá uma referência à chave estrangeira para a chave
primária da tabela ITENS. Na @ManyToOne, o elemento name especifica a chave
estrangeira, BID_ITEM_ID, e o elemento referencedColumnName especifica a chave
15
primária ITEM_ID. Da perspectiva da entidade Item, isso significa que o provedor de
persistência descobriria quais instâncias Bid colocar nos Bids carregando as chaves
BID_ITEM_ID que cominam na tabela BIDS.
@Entity
@Table(name="CATEGORIES")
public class Category implements Serializable {
@Id
@Column(name="CATEGORY_ID")
protected Long categoryId;
...
@ManyToOne
@JoinColumn(name="PARENT_ID",referencedColumnName="CATEGORY_ID"
)
Category parentCategory; ...
16
@Entity
@Table(name="CATEGORIES")
public class Category implements Serializable {
@Id
@Column(name="CATEGORY_ID")
protected Long categoryId;
@ManyToMany
@JoinTable(name="CATEGORIES_ITEMS",
joinColumns=
@JoinColumn(name="CI_CATEGORY_ID",
referencedColumnName="CATEGORY_ID"),
inverseJoinColumns=
@JoinColumn(name="CI_ITEM_ID",
referencedColumnName="ITEM_ID"))
protected Set<Item> items;
...
}
@Entity
@Table(name="ITEMS")
public class Item implements Serializable {
@Id
@Column(name="ITEM_ID")
protected Long itemId;
...
@ManyToMany(mappedBy="items")
protected Set<Category> categories;
...
}
Mapeando Herança
17
1.1.12. Estratégia da tabela simples
Esta estratégia, todas as classes da hierarquia são mapeados para uma tabela simples.
Isto significa que a tabela simples conterá um super set de todos os dados armazenado
na hierarquia. Objetos diferentes na hierarquia OO são identificados usando uma
coluna especial chamada coluna DISCRIMINADORA. A coluna discriminadora no
exemplo é USER_TYPE.
@Entity
@Table(name="USERS")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="USER_TYPE",
discriminatorType=DiscriminatorType.STRING, length=1)
public abstract class User ...
@Entity
@DiscriminatorValue(value="S")
public class Seller extends User ...
@Entity
@DiscriminatorValue(value="B")
public class Bidder extends User
18
1.1.13. Estratégia das tabelas juntas
A estratégia de herança das tabelas juntas usa as relações um-para-um para modelar a
herança OO. Em efeito, a estratégia das tabelas juntas envolve a criação de tabelas
separadas para cada entidade na hierarquia OO e relaciona descendentes na hierarquia
com as ralações um-para-um. Na hierarquia, as tabelas filhas contém colunas
específicas para cada subtipo da entidade, como exemplo, as tabelas USERS e
SELLER são relacionadas por meio de chave estrangeira USER_ID na tabela
SELLER apontando para a chave primária da tabela USERS. Uma relação similar
existe entre as tabelas BIDDER e USERS. A coluna discriminadora na tabela USERS
ainda é usada, primeiramente, como uma forma de diferenciar facilmente tipos de
dados na hierarquia.
@Entity
@Table(name="USERS")
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn(name="USER_TYPE",
discriminatorType=STRING, length=1)
public abstract class User ...
@Entity
@Table(name="SELLERS")
@DiscriminatorValue(value="S")
@PrimaryKeyJoinColumn(name="USER_ID")
public class Seller extends User ...
@Entity
@Table(name="BIDDERS")
@DiscriminatorValue(value="B")
@PrimaryKeyJoinColumn(name="USER_ID")
public class Seller extends User ...
19
1.1.14. Estratégia de Tabela por Classe
@Entity
@Table(name="USERS")
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class User {
...
@Entity
@Table(name="SELLERS")
public class Seller extends User {
...
@Entity
@Table(name="BIDDERS")
public class Bidder extends User {
20
desse tipo de mapeamento é que ele não tem bom suporte para relações ou questões
polimórficas porque cada subclasse é mapeada para sua própria tabela.
Além dessas estratégias de herança, o EJB3 JPA permite um entidade herdar de uma
classe não-entidade. Tal classe é anotada com @MappedSuperClass. Como um objeto
embutido, uma superclasse mapeada não tem uma tabela associada.
21
Manipulando entidades com o EntityManager
Você viu como os objetos de domínio e relações são mapeadas para o banco de dados.
Embora as anotações ORM exploradas no capítulo 8 indiquem como uma entidade é
persistida, elas mesmas não são persistentes. Isto é realizado pelas aplicações
utilizando a interface EntityManager, que executa operações CRUD (Creat, Read,
Update, Delete) nos objetos de domínio.
Apresentando o EntityManager
22
public FlushModeType getFlushMode(); Recupera o modo flush atual.
public void refresh(Object entity); Atualiza (reinicia) a entidade do banco de
dados
public Query createQuery(String Cria uma consulta dinâmica utilizando
jpqlString); uma instrução JPQL.
public Query createNamedQuery(String Cria uma instância de consulta com base
name); em uma consulta nomeada na instância da
entidade.
Cria uma consulta dinâmica utilizando
public Query createNativeQuery(String uma instrução SQL nativa.
sqlString);
public Query createNativeQuery(String
sqlString, Class result Class);
public Query createNativeQuery(String
sqlString, String resultSetMapping);
public void close(); Finaliza uma aplicação EntityManager
gerenciada.
public boolean isOpen(); Verifica se o EntityManager está aberto.
public EntityTransaction Recupera um objeto de transação que
getTransaction(); pode ser utilizado para iniciar uma
transação manualmente ou finaliza-la.
public void joinTransaction(); Pede para um EntityManager associar-se
a uma transação JTA existente.
Entender o ciclo de vida é fácil, uma vez que você entendeu um conceito óbvio: o
EntityManager não sabe nada sobre um POJO a não ser a maneira como ele é
anotado. Isso é exatamente o oposto de POJOs anotados para serem beans de sessão
ou MDBs, que são carregados e gerenciados pelo container assim que a aplicação
inicia.
Uma entidade que o EntityManager está observando é considerada agregada ou
gerenciada. Por outro lado, quando um EntityManager pára de gerenciar uma
entidade, a entidade é chamada de separada. Uma entidade que nunca foi gerenciada
em nenhum momento é chamada de temporária ou nova.
23