-
Notifications
You must be signed in to change notification settings - Fork 0
/
spring-annotations-relationship.txt
295 lines (170 loc) · 9.36 KB
/
spring-annotations-relationship.txt
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
@manyToOne: quem fica com essa anotação é o dono do relacionamento, a outra classe fica com @one to may
@manyToOne: por padrão o nome da chave estrangeira usada pelo jpa será o nome da outra classe + nome da primary key,
Caso o nome da coluna da sua chave estrangeira não siga o padrão ou caso você prefira deixar isso explícito, você ainda pode usar a anotação @JoinColumn especificando o nome da coluna no atributo name.
@oneToMany: parametro mapped by refere-se ao nome do atributo na classe que tem o @manyToOne
quando se usa mapped by esse se torna o lado inverso do relacionamento
O lado múltiplo de uma associação um para muitos é opcional. Você pode implementá-lo se for pretendido ou ignorá-lo se não for necessário ou até mesmo arriscado. Um fabricante poderia ter muitos milhares de produtos. Então não faz sentido buscar todos eles de uma vez. É melhor carregar por meio de uma consulta e usar paginação. É claro que você pode adicionar a coleção de produtos ao seu fabricante se achar que isso o ajuda.
ex:
@Entity
public class Product {
@ManyToOne(optional = false, fetch = FetchType.LAZY)
private Manufacturer manufacturer;
}
@Entity
public class Manufacturer{
private ManufacturerId manufacturerId;
private String name;
private Manufacturer() {
}
}
classe Manufacturer não tem uma List de Product
-------------------------------------------------------
@oneToOne: relacionamento 1:1
Caso o nome da coluna da sua chave estrangeira não siga o padrão ou caso você prefira deixar isso explícito, você ainda pode usar a anotação @JoinColumn especificando o nome da coluna no atributo name.
@oneToOne: quem possui a coluna de chave estrangeira recebe a anotação @JoinColumn.
-------------------------------------------------------
@manyToMany: relacionamento N:N
Usaremos um modelo de alunos, cursos e vários relacionamentos entre eles.
@Entity
class Student {
@Id
Long id;
@ManyToMany
Set<Course> courses;
}
@Entity
class Course {
@Id
Long id;
@ManyToMany
Set<Student> students;
}
O lado proprietário é onde configuramos o relacionamento. Usaremos a classe Student .
Podemos fazer isso com a anotação @JoinTable na classe Student. Fornecemos o nome da tabela de junção ( course_student ), bem como as chaves
estrangeiras com as anotações @JoinColumn . O atributo joinColumn se conectará ao lado proprietário do relacionamento e o inverseJoinColumn
ao outro lado:
classe student:
@ManyToMany
@JoinTable(
name = "course_student",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id"))
Set<Course> courses;
Observe que não é necessário usar @JoinTable ou mesmo @JoinColumn. O JPA gerará os nomes das tabelas e colunas para nós.
No entanto, a estratégia que o JPA usa nem sempre corresponde às convenções de nomenclatura que usamos. Portanto, precisamos da
possibilidade de configurar nomes de tabelas e colunas.
Do lado do destino, só precisamos fornecer o nome do campo que mapeia o relacionamento.
Portanto, definimos o atributo mappedBy da anotação @ManyToMany na classe Course:
@ManyToMany(mappedBy = "courses")
Set<Student> students;
**Lembre-se de que, como um relacionamento muitos-para-muitos não possui um lado proprietário no banco de dados,
poderíamos configurar a tabela de junção na classe Course e referenciá-la na classe Student.
**no exemplo acima geraria uma tabela course_student com as chaves estrangeiras das tabelas
@manyToMany com atributos no relacionamento:
A implementação de um relacionamento simples de muitos para muitos foi bastante direta. O único problema é que não podemos adicionar uma
propriedade a um relacionamento dessa forma porque conectamos as entidades diretamente. Portanto, não tínhamos como adicionar uma
propriedade ao relacionamento em si.
Como mapeamos atributos de banco de dados para campos de classe em JPA, precisamos criar uma nova classe de entidade para o relacionamento.
Obviamente, toda entidade JPA precisa de uma chave primária. Como nossa chave primária é uma chave composta(course_id e student_id),
temos que criar uma nova classe que conterá as diferentes partes da chave:
@Embeddable
class CourseRatingKey implements Serializable {
@Column(name = "student_id")
Long studentId;
@Column(name = "course_id")
Long courseId;
// standard constructors, getters, and setters
// hashcode and equals implementation
}
Observe que uma classe de chave composta deve atender a alguns requisitos principais :
-Temos que marcá-lo com @Embeddable .
-Tem que implementar java.io.Serializable .
-Precisamos fornecer uma implementação dos métodos hashcode() e equals() .
Usando a chave composta criada acima:
@Entity
class CourseRating {
@EmbeddedId
CourseRatingKey id;
@ManyToOne
@MapsId("studentId")
@JoinColumn(name = "student_id")
Student student;
@ManyToOne
@MapsId("courseId")
@JoinColumn(name = "course_id")
Course course;
int rating;
// standard constructors, getters, and setters
}
Usamos @EmbeddedId para marcar a chave primária, que é uma instância da classe CourseRatingKey.
Marcamos os campos do aluno e do curso com @MapsId.
@MapsId significa que vinculamos esses campos a uma parte da chave e eles são as chaves estrangeiras de um relacionamento muitos para um.
Depois disso, podemos configurar as referências inversas nas entidades Aluno e Curso como antes:
class Student {
// ...
@OneToMany(mappedBy = "student")
Set<CourseRating> ratings;
// ...
}
class Course {
// ...
@OneToMany(mappedBy = "course")
Set<CourseRating> ratings;
// ...
}
Configuramos os relacionamentos com as classes Student e Course como @ManyToOne.
Poderíamos fazer isso porque com a nova entidade decompusemos estruturalmente o relacionamento muitos para muitos em dois
relacionamentos muitos para um.
Muitos para muitos com uma nova entidade:
Digamos que queremos permitir que os alunos se inscrevam em cursos. Além disso, precisamos armazenar o momento em que um aluno se
inscreveu em um curso específico. Além disso, queremos armazenar a nota que ela recebeu no curso.
Num mundo ideal, poderíamos resolver isso com a solução acima, onde tínhamos uma entidade com chave composta. Porém, o mundo está longe do
ideal e os alunos nem sempre concluem o curso na primeira tentativa.
Nesse caso, há diversas conexões entre os mesmos pares aluno-curso , ou diversas linhas, com os mesmos pares student_id-course_id.
Não podemos modelá-lo usando nenhuma das soluções anteriores porque todas as chaves primárias devem ser únicas.
Portanto, precisamos usar uma chave primária separada.
Portanto, podemos introduzir uma entidade , que deterá os atributos do registro:
Neste caso, a entidade Registo representa a relação entre as outras duas entidades.
Por ser uma entidade, terá sua própria chave primária.
Na solução anterior, lembre-se que tínhamos uma chave primária composta que criamos a partir das duas chaves estrangeiras.
Agora as duas chaves estrangeiras não farão parte da chave primária (a tabela da relação terá a sua própria chave primária):
@Entity
class CourseRegistration {
@Id
Long id;
@ManyToOne
@JoinColumn(name = "student_id")
Student student;
@ManyToOne
@JoinColumn(name = "course_id")
Course course;
LocalDateTime registeredAt;
int grade;
// additional properties
// standard constructors, getters, and setters
}
Também precisamos configurar os relacionamentos nas classes Aluno e Curso:
class Student {
// ...
@OneToMany(mappedBy = "student")
Set<CourseRegistration> registrations;
// ...
}
class Course {
// ...
@OneToMany(mappedBy = "course")
Set<CourseRegistration> registrations;
// ...
}
-------------------------------------------------------
**as referências(objetos) pertencentes ao proprietário têm precedência e são salvas no banco de dados primeiro.
**é uma boa prática marcar o lado muitos para um como o lado proprietário.
**OBS: se uma classe tem @oneToMany e outra @manyToOne isso se torna um relacionamento bidirecional. Bidirecional significa que se pode acessar os dados dos 2 lados, pra ser unidirecional teria que tirar uma das anotações, escolher Bidirecional ou unidirecional depende do contexto
**Também é possível marcar o lado um para muitos como o lado proprietário e o lado muitos para um como o lado inverso. mas isso não é boa prática podendo levar a insconsistencias, pois o lado 1 seria salvo antes do lado N, ex: salvar uma list em que um desses objetos faz referencia a outro objeto diferente
do objeto do lado 1, nesse caso ele seria salvo no bd com a referencia do objeto do lado 1 mesmo ele tendo referencia pra outro objeto pois o lado 1
como proprietario teria prioridade e seria salvo antes dos objetos do lado N
**É importante em relacionamentos bidirecionais, sempre ligar os dois lados da relação antes de persistir no EntityManager:
Evento e = ...;
Categoria c = ...;
e.setCategoria(c);
c.eventos.add(e);