Związek trzech encji z Hibernate
Relacje między dwoma tabelami bazy danych omówiłem poprzednim razem. Teraz chciałbym rozszerzyć problem do trzech tabel. Jednak nie chodzi mi o dowolne relacje, lecz o specyficzny ich rodzaj.
Za przykład posłuży mi wyimaginowany system pracujący w firmie spedycyjnej. W tym systemie istnieją klienci (tabela customers), zaopatrywani w różne towary (tabela goods) przez różne magazyny (tabela warehouses). Jednak dla każdego klienta do dostarczenia konkretnego towaru wybierany jest tylko jeden konkretny magazyn – naogół jest to najbliższy magazyn składujący dany towar.
Na początek przedstawię, jak takie informacje przechowywane by były w bazie danych (dla uproszczenia ograniczę się do kluczy):
CREATE TABLE "goods"
(
"id" bigint NOT NULL,
CONSTRAINT "pk_goods" PRIMARY KEY ("id")
);
CREATE TABLE "warehouses"
(
"id" bigint NOT NULL,
CONSTRAINT "pk_warehouses" PRIMARY KEY ("id")
);
CREATE TABLE "customers"
(
"id" bigint NOT NULL,
CONSTRAINT "pk_customers" PRIMARY KEY ("id")
);
CREATE TABLE "cust_goods_ware"
(
"id_customers" bigint,
"id_goods" bigint,
"id_warehouses" bigint,
CONSTRAINT "fk_cgw_cust"
FOREIGN KEY ("id_customers")
REFERENCES "customers" ("id") MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT "fk_cgw_goods"
FOREIGN KEY ("id_goods")
REFERENCES "goods" ("id") MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT "fk_cgw_ware"
FOREIGN KEY ("id_warehouses")
REFERENCES "warehouses" ("id") MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE
);
Jak widać, pominąłem relację między towarami a magazynami – zależności między dwiema encjami już omówiłem, więc teraz skupię się wyłącznie na relacji trzech encji.
Zacznę od definicji podstawowych klas encji:
@Entity
@Table(name = "goods")
public class Good {
@Id
@GeneratedValue
@NotNull
protected Long id;
}
@Entity
@Table(name = "warehouses")
public class Warehouse {
@Id
@GeneratedValue
@NotNull
protected Long id;
}
@Entity
@Table(name = "customers")
public class Customer {
@Id
@GeneratedValue
@NotNull
protected Long id;
}
Przypuśćmy, że dla konkretnego klienta będzie potrzebna lista dostawców poszczególnych towarów z możliwością ich zmiany. W klasie Customer dodaję następujące pole:
@CollectionOfElements(
targetElement = Warehouse.class)
@MapKeyManyToMany(
targetEntity = Good.class,
joinColumns = @JoinColumn(
name = "id_goods",
referencedColumnName = "id"))
@JoinTable(name = "cust_goods_ware",
joinColumns = @JoinColumn(
name = "id_customers",
referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(
name = "id_warehouses",
referencedColumnName = "id"))
protected Map<Good, Warehouse> suppliers =
new HashMap<Good, Warehouse>();
Każdy klient (Customer) posiada własną kolekcję (org.hibernate.annotations.CollectionOfElements) magazynów (Warehouse). Kluczem (org.hibernate.annotations.MapKeyManyToMany) dla tej relacji jest towar (Good), włączany do tej relacji poprzez klucz obcy id_goods odwołujący się do pola id. Sama relacja przechowywana jest w tabeli pośredniczącej (javax.persistence.JoinTable) cust_goods_ware. Wewnątrz tej tabeli znajdują się kolumny służące do określenia encji będących w relacji (javax.persistence.JoinColumn) – id_customers, wskazująca pole id encji klienta, oraz id_warehouses, wskazująca pole id encji magazynu.
Innymi słowy, do każdego klienta przyporządkowani zostali dostawcy (czyli magazyny) poszczególnych towarów.
Nie jest to takie trudne, a efekt wart jest odrobiny pracy. Na pewno sposób jest szybszy niż tworzenie własnej implementacji interfejsu java.util.Map.



