Marzec
24
2009

Związek trzech encji z Hibernate

Słowa kluczowe: , , , | Kategorie: Seam Framework
No Gravatar

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.

Napisz Komentarz

*