JBoss Seam – relacje pomiędzy encjami
Omówiłem już adnotacje dotyczące encji, pozostawiając jednak temat relacji na później. Tym razem zajmę się właśnie tym zagadnieniem.
Podstawowe relacje, to te występujące pomiędzy dwiema encjami. Są to:
Postaram się wytłumaczyć na prostych przykładach ich działanie.
Jest to najprostrza możliwa zależność dwóch encji. Oznacza, że jeden rekord tabeli A odpowiada maksymalnie jednemu rekordowi tabeli B. Relacja przechowywana jest w jednej z tabel:
CREATE TABLE "A"
(
"id" bigint NOT NULL,
CONSTRAINT "pk_a" PRIMARY KEY ("id")
);
CREATE TABLE "B"
(
"id" bigint NOT NULL,
"id_a" bigint,
CONSTRAINT "pk_b" PRIMARY KEY ("id"),
CONSTRAINT "fk_b_a" FOREIGN KEY ("id_a")
REFERENCES "A" ("id") MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE
);
W mapowaniu obiektowo-relacyjnym takie tabele będą przedstawiane za pomocą dwóch encji (dla uproszczenia użyję pól zamiast właściwości):
@Entity
public class A {
@Id
@GeneratedValue
@NotNull
protected Long id;
}
@Entity
public class B {
@Id
@GeneratedValue
@NotNull
protected Long id;
@OneToOne
@JoinColumn(
name = "id_a",
referencedColumnName = "id")
protected A a;
}
Jak widać na powyższym przykładzie, mając obiekt klasy B można odwołać się bezpośrednio do powiązanego z nim obiektu klasy A. Ponieważ to tabela B przechowuje klucz obcy, ewentualne odwołanie odwrotne w klasie A wyglądałoby w następujący sposób:
@OneToOne(mappedBy = "a") protected B b;
Wykorzystane w poprzedniej relacji tabele mogą zostać również powiązane relacją wiele do wielu. Jednak wówczas kod programu będzie wyglądał w następujący sposób:
@Entity
public class A {
@Id
@GeneratedValue
@NotNull
protected Long id;
@OneToMany(mappedBy = "a")
protected Set<B> b;
}
@Entity
public class B {
@Id
@GeneratedValue
@NotNull
protected Long id;
@ManyToOne
@JoinColumn(
name = "id_a",
referencedColumnName = "id")
protected A a;
}
Tym razem w klasie B zmianie uległa jedna adnotacja (OneToOne na ManyToOne). Natomiast klasa A posiada teraz dowiązanie OneToMany do zbioru obiektów klasy B.
Dla relacji wiele do wielu trzeba już zdefiniować trzy tabele:
CREATE TABLE "A"
(
"id" bigint NOT NULL,
CONSTRAINT "pk_a" PRIMARY KEY ("id")
);
CREATE TABLE "B"
(
"id" bigint NOT NULL,
CONSTRAINT "pk_b" PRIMARY KEY ("id")
);
CREATE TABLE "A_B"
(
"id_a" bigint,
"id_b" bigint,
CONSTRAINT "pk_a_b" PRIMARY KEY ("id_a", "id_b"),
CONSTRAINT "fk_a_b_a" FOREIGN KEY ("id_a")
REFERENCES "A" ("id") MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT "fk_a_b_b" FOREIGN KEY ("id_b")
REFERENCES "B" ("id") MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE
);
Jednak wciąż wystarczą tylko dwie klasy encji:
@Entity
public class A {
@Id
@GeneratedValue
@NotNull
protected Long id;
@ManyToMany(mappedBy = "a")
protected Set<B> b;
}
@Entity
public class B {
@Id
@GeneratedValue
@NotNull
protected Long id;
@ManyToMany
@JoinTable(
name="A_B",
joinColumns=
@JoinColumn(
name="id_b",
referencedColumnName="id"),
inverseJoinColumns=
@JoinColumn(
name="id_a",
referencedColumnName="id"))
protected Set<A> a;
}
Definicje klas niewiele się zmieniły. Tym razem w obu encjach istnieje odwołanie do zbioru obiektów. Dodatkowo wprowadzona została adnotacja opisująca tabelę A_B, która realizuje relację w bazie danych.
Podsumowanie
Opisałem dzisiaj wszystkie możliwe relacje pomiędzy dwiema encjami. Ich mapowanie jest w znacznym stopniu uproszczone przez stosowanie adnotacji. Jednak w realnych projektach może też wystąpić konieczność modelowania relacji bardziej złożonych. Ale o tym następnym razem.



