Tutorial JPA Chave Primária Composta
EXISTE DOIS NOVOS POSTS SOBRE ESSE ASSUNTO:
CHAVE COMPOSTA SIMPLES: http://uaihebert.com/?p=1622&page=8
CHAVE COMPOSTA COMPLEXA: http://uaihebert.com/?p=1622&page=9
CHAVE COMPOSTA SIMPLES: http://uaihebert.com/?p=1622&page=8
CHAVE COMPOSTA COMPLEXA: http://uaihebert.com/?p=1622&page=9
Bom dia.
Vamos falar hoje sobre Chave Primária Composta (Composite Key)?
Imagine que apenas o ID não seria suficiente para definir a chave primária da sua classe. Como fazer com que o JPA entenda esse mapeamento?
Vou utilizar como exemplo uma classe Car (Carro). Imagine um sistema onde para se identificar um carro, é necessário o número do chassi. Vamos supor que um novo requisito chegou e além do chassi será necessário o código de identificação do motor.
Iremos continuar exatamente do último post sobre JPA (JPA SequenceGenerator). Você poderá ver os outros posts sobre JPA caso precise de alguma ajuda para montar o ambiente necessário para rodar o código desse post: JPA TableGenerator – Chave Primária Simples, Auto Create Schema Script com: Ant, Hibernate 3 e JPA 2,Tutorial Hibernate 3 com JPA 2.
Será necessário criar uma classe para fazer o trabalho da chave composta. Vamos ver como ficará o código da classe CarPK:
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
| package com; import java.io.Serializable; public class CarPK implements Serializable { private String chassisSerialNumber; private String engineSerialNumber; public CarPK(){ // Your class must have a no-arq constructor } @Override public boolean equals(Object obj) { if (obj instanceof CarPK){ CarPK carPk = (CarPK) obj; if (!carPk.getChassisSerialNumber().equals(chassisSerialNumber)){ return false ; } if (!carPk.getEngineSerialNumber().equals(engineSerialNumber)){ return false ; } return true ; } return false ; } @Override public int hashCode() { return chassisSerialNumber.hashCode() + engineSerialNumber.hashCode(); } public String getChassisSerialNumber() { return chassisSerialNumber; } public void setChassisSerialNumber(String chassisSerialNumber) { this .chassisSerialNumber = chassisSerialNumber; } public String getEngineSerialNumber() { return engineSerialNumber; } public void setEngineSerialNumber(String engineSerialNumber) { this .engineSerialNumber = engineSerialNumber; } } |
Existem algumas normas que sua classe de chave composta deve seguir:
- Deve ter um construtor sem argumentos
- Deve implementar a interface java.io.Serializable
- Deve sobrescrever os métodos equals e hashCode
Agora vamos criar nossa classe Car:
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
38
39
40
41
42
43
44
45
| package com; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.IdClass; import javax.persistence.Table; @Entity @Table (name= "CAR" ) @IdClass (value=CarPK. class ) public class Car { @Id private String chassisSerialNumber; @Id private String engineSerialNumber; @Column private String name; // Yes, some people like to give name to theirs cars. public String getChassisSerialNumber() { return chassisSerialNumber; } public void setChassisSerialNumber(String chassisSerialNumber) { this .chassisSerialNumber = chassisSerialNumber; } public String getEngineSerialNumber() { return engineSerialNumber; } public void setEngineSerialNumber(String engineSerialNumber) { this .engineSerialNumber = engineSerialNumber; } public String getName() { return name; } public void setName(String name) { this .name = name; } } |
Repare que em nossa classe Car, apenas adicionamos a anotação @Id sem nos preocupar em definir qualquer outro tipo de anotação. Uma observação é que no atributo “name” existe a anotação @Column mas ela não é obrigatória.
Veja o código da classe Main que irá inserir um objeto da classe Car (carro) em nosso banco de dados:
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
| package com; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; public class Main { public static void main(String[] args) { EntityManagerFactory emf = Persistence.createEntityManagerFactory( "Hello" ); EntityManager em = emf.createEntityManager(); try { em.getTransaction().begin(); Car car = new Car(); car.setChassisSerialNumber( "9BW DA05X6 1 T050136" ); car.setEngineSerialNumber( "ABC123" ); car.setName( "Thunder" ); em.persist(car); em.getTransaction().commit(); } catch (Exception e) { em.getTransaction().rollback(); e.printStackTrace(); } finally { emf.close(); } System.out.println( "It is over" ); } } |
Vamos executar a classe Main e ver o resultado.
E como consultar uma entidade que tem a chave primária composta? Vamos alterar nossa classe Main para executar essa pesquisa:
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
| package com; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; public class Main { public static void main(String[] args) { EntityManagerFactory emf = Persistence.createEntityManagerFactory( "Hello" ); EntityManager em = emf.createEntityManager(); try { em.getTransaction().begin(); CarPK carPK = new CarPK(); carPK.setChassisSerialNumber( "9BW DA05X6 1 T050136" ); carPK.setEngineSerialNumber( "ABC123" ); Car car = em.find(Car. class , carPK); System.out.println(car.getName()); em.getTransaction().commit(); } catch (Exception e) { em.getTransaction().rollback(); e.printStackTrace(); } finally { emf.close(); } System.out.println( "It is over" ); } } |
E após executar a classe:
Para consultar uma entidade que tem sua chave primaria (primary-key) composta é necessário se criar uma instância da dessa chave e passá-la como parâmetro na consulta.
Vou mostrar outro modo de se mapear uma chave primária composta. Repare que atualmente nós temos os mesmo campos dentro da classe CarPK e Car. Ambos contêm o código do chassi e o código do motor. Podemos fazer uma pequena alteração no código para solucionar isso.
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
| package com; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Embeddable; @Embeddable public class CarPK implements Serializable { @Column private String chassisSerialNumber; @Column private String engineSerialNumber; public CarPK(){ // Your class must have a no-arq constructor } @Override public boolean equals(Object obj) { if (!(obj instanceof CarPK)){ CarPK carPk = (CarPK) obj; if (!carPk.getChassisSerialNumber().equals(chassisSerialNumber)){ return false ; } if (!carPk.getEngineSerialNumber().equals(engineSerialNumber)){ return false ; } return true ; } return false ; } @Override public int hashCode() { return chassisSerialNumber.hashCode() + engineSerialNumber.hashCode(); } public String getChassisSerialNumber() { return chassisSerialNumber; } public void setChassisSerialNumber(String chassisSerialNumber) { this .chassisSerialNumber = chassisSerialNumber; } public String getEngineSerialNumber() { return engineSerialNumber; } public void setEngineSerialNumber(String engineSerialNumber) { this .engineSerialNumber = engineSerialNumber; } } |
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
| package com; import javax.persistence.Column; import javax.persistence.EmbeddedId; import javax.persistence.Entity; import javax.persistence.Table; @Entity @Table (name = "CAR" ) public class Car { @EmbeddedId private CarPK carPK; @Column private String name; // Yes, some people like to give name to theirs cars. public String getName() { return name; } public void setName(String name) { this .name = name; } public CarPK getCarPK() { return carPK; } public void setCarPK(CarPK carPK) { this .carPK = carPK; } } |
Pequenas alterações foram feitas.
- Na classe CarPK agora existe a anotação @Column. Esses serão a partir de agora os atributos mapeados no banco de dados. Foi adicionada também a anotação @Embeddeable informando que essa classe pode ser incorporada em outra classe.
- Na classe Car foram retirados os atributos e substituídos por um atributo da classe CarPK. Foi retirado também a anotação @IdClass. Agora colocamos uma outra anotação chamada @EmbeddedId que informa que ao JPA que os campos a serem utilizados virão de dentro da classe CarPK.
Caso você queria, execute a classe Main novamente e verá que a consulta será realizada normalmente.
Atenção: Ao utilizar do recurso de chave primária composta não será possível utilizar uma “sequence” para gerar o ID automaticamente. Você terá que gerar o ID por código.
Atenção: Ao utilizar do recurso de chave primária composta não será possível utilizar uma “sequence” para gerar o ID automaticamente. Você terá que gerar o ID por código.
Alguma dúvida? Sugestão? Basta colocar abaixo.
Até mais.
Nenhum comentário:
Postar um comentário