2017-01-15 4 views
1

Ich versuche, das gleiche Problem zu implementieren, wie hier vor einigen Jahren erwähnt: Hibernate many to many with a composite key with one field shared on either side of the relationshipHibernate ManyToMany Beziehung mit gemeinsamem Fremdschlüssel

ich ein Menü und eine Item-Klasse und eine unidirektionale Beziehung implementieren mag, dass ein Menü alle spart die Artikel, die es enthält.

Menu and Item both have composite keys out of the merchant_id foreign key and an auto incremental itemId/menuId. (EER-Diagramm Bild)

Da Hibernate das Auto nicht abrufen kann Id erzeugt wird, wenn ich einen zusammengesetzten Schlüssel deklarieren und die Id ist einzigartig in dem System, speichere ich die Entitäten ohne zusätzlichen embeddedId PKClass:

@Entity 
@Table(name="ITEM") 
public class Item extends AbstractTimestampEntity{ 
    @Id 
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    @Column(name="itemId", nullable=false) 
    private long itemId; 
    @ManyToOne 
    @JoinColumn(name="merchantId", nullable=false) 
    private Merchant merchant; 
    @Column(name="name", length=45) 
    private String name; 
    @Column(name="description" , length=200) 
    private String description; 
    @Column(name="price") 
    private double price; 

    public Item(){} // getters & setters 

@Entity 
@Table(name="MENU") 
public class Menu { 
    @Id 
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    @Column(name="menuId", nullable=false) 
    private long menuId; 
    @ManyToOne 
    @JoinColumn(name="merchantId", nullable=false) 
    private Merchant merchant; 
    @Column(name="name", length=45) 
    private String name; 
    @ManyToMany 
    @JoinTable(name="MENU_ITEM", joinColumns = { 
     @JoinColumn(name="menuId", nullable=false, updatable=false)}, 
     //@JoinColumn(name="merchant.merchantId", nullable=false, updatable=false)}, 
     inverseJoinColumns = { @JoinColumn(name="itemId", nullable=false, updatable=false)}) 
     //      @JoinColumn(name="merchantId", nullable=false, updatable=false)}) 
    private List<Item> items = new ArrayList<Item>(); // constructor, getters & setters 

Wie Sie aus dem kommentierten Code sehen können, hier ist der Punkt, meine Frage kommt waren. Wie ordne ich die Entitäten jetzt die beste w ohne meine normalisierte Datenbanktabelle zu ändern? (Sie müssen die gleichen Händler haben in der Datenbank validiert werden)

+0

können Sie zeigen, ' MENU_ITEM' Fremdschlüssel? –

Antwort

0

Ich vermute, Sie so etwas wie dies wünschen kann:

CREATE TABLE `MENU_ITEM` (
    `merchant_id` INT NOT NULL, 
    `menu_id` INT NOT NULL, 
    `item_id` INT NOT NULL, 

    PRIMARY KEY (`merchant_id`, `menu_id`, `item_id`), 

    INDEX `ix_menuitem_item` (`item_id`, `merchant_id`), 
    INDEX `ix_menuitem_menu` (`menu_id`, `merchant_id`), 
    INDEX `ix_menuitem_merchant` (`merchant_id`), 

    CONSTRAINT `fk_menuitem_merchant` 
     FOREIGN KEY (`merchant_id`) 
     REFERENCES `merchant` (`id`), 

    CONSTRAINT `fk_menuitem_menu` 
     FOREIGN KEY (`menu_id`, `merchant_id`) 
     REFERENCES `menu` (`id`, `merchant_id`), 

    CONSTRAINT `fk_menuitem_item` 
     FOREIGN KEY (`item_id`, `merchant_id`) 
     REFERENCES `item` (`id`, `merchant_id`) 
) 

aber leider ist dies unmöglich.

Eine Spalte kann in höchstens 1 Fremdschlüssel, in diesem Fall verwendet werden MENU_ITEM.merchant_idwird 3-mal verwendet (höchstens 2, Entfernen fk_menuitem_merchant).

So können Sie etwas Gleichwertiges wollen:

CREATE TABLE `MENU_ITEM` (
    `merchant_id` INT NOT NULL, 
    `menu_id` INT NOT NULL, 
    `menu_merchant_id` INT NOT NULL, 
    `item_id` INT NOT NULL, 
    `item_merchant_id` INT NOT NULL, 

    PRIMARY KEY (`merchant_id`, `menu_id`, `item_id`), 

    INDEX `ix_menuitem_item` (`item_id`, `merchant_id`), 
    INDEX `ix_menuitem_menu` (`menu_id`, `merchant_id`), 
    INDEX `ix_menuitem_merchant` (`merchant_id`), 

    CONSTRAINT `fk_menuitem_merchant` 
     FOREIGN KEY (`merchant_id`) 
     REFERENCES `merchant` (`id`), 

    CONSTRAINT `fk_menuitem_menu` 
     FOREIGN KEY (`menu_id`, `menu_merchant_id`) 
     REFERENCES `menu` (`id`, `merchant_id`), 

    CONSTRAINT `fk_menuitem_item` 
     FOREIGN KEY (`item_id`, `item_merchant_id`) 
     REFERENCES `item` (`id`, `merchant_id`), 

    CHECK (`merchant_id` = `menu_merchant_id`), 
    CHECK (`merchant_id` = `item_merchant_id`) 
) 

aber leider wieder, MySQL nichtCHECK unterstützt.

Wie Sie sehen können, ist dies kein ORM-Problem.

Also, haben Sie zwei Möglichkeiten:

  1. einige Trigger implementieren CHECK (see here)
  2. lassen die Anwendung tun, um die Kontrolle zu simulieren:

    @Entity 
    public class Menu 
    { 
        protected class ItemList extends AbstractList<Item> 
        { 
         protected ArrayList<Item> list; 
    
         public ItemList() 
         { 
          super(); 
          list = new ArrayList<>(); 
         } 
    
         public ItemList(Collection<? extends Item> c) 
         { 
          super(); 
          list = new ArrayList<>(c.size()); 
          addAll(c); 
         } 
    
         @Override 
         public boolean add(Item item) 
         { 
          if(!Objects.equals(merchant, item.merchant)) 
          { 
           throw new IllegalArgumentException(); 
           // or return false; 
          } 
    
          return list.add(item); 
         } 
    
         @Override 
         public Item get(int index) 
         { 
          return list.get(index); 
         } 
    
         @Override 
         public int size() 
         { 
          return list.size(); 
         } 
        } 
    
        @Id 
        @GeneratedValue(strategy = GenerationType.IDENTITY) 
        @Column(name = "id", nullable = false) 
        protected long id; 
    
        @ManyToOne 
        @JoinColumn(name = "merchant_id", nullable = false) 
        protected Merchant merchant; 
    
        @Column(name = "name", length = 45) 
        protected String name; 
    
        @ManyToMany 
        @JoinTable(name = "MENU_ITEM", 
         joinColumns = @JoinColumn(name = "menu_id"), 
         inverseJoinColumns = @JoinColumn(name = "item_id")) 
        protected List<Item> items = new ItemList(); 
    
        public List<Item> getItems() 
        { 
         return items; 
        } 
    
        public void setItems(List<Item> items) 
        { 
         this.items = new ItemList(items); 
        } 
    } 
    
+0

Danke! Ja, ich dachte darüber nach, die Prüfung nur in der Anwendungsebene durchzuführen. Allerdings war ich mir nicht sicher, ob es einen besseren Weg gibt, der das normalisierte DB-Design nicht verändert. – kaikun

+0

Gern geschehen. Ich hätte gerne einen DB-Weg :( –

Verwandte Themen