我尝试解决几年前在这里提到的相同问题: Hibernate many to many with a composite key with one field shared on either side of the relationship
我有一个 Menu 和一个 Item 类,我想实现一个单向关系,菜单保存它包含的所有项目。
因为当我声明复合键时 Hibernate 无法检索自动生成的 Id,并且该 Id 在系统中是唯一的,所以我保存实体时没有额外的 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
正如您从注释代码中看到的那样,这就是我的问题所在。如何在不修改规范化数据库表的情况下最好地映射实体? (他们需要有相同的商家才能在数据库中进行验证)
最佳答案
我想你可能想要这样的东西:
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`)
)
但是,不幸的是,这不可能。
一列可以在最多1外键中使用,在这种情况下MENU_ITEM.merchant_id
被使用3次(最多2次, 删除 fk_menuitem_merchant
).
因此,您可能需要等效的东西:
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`)
)
但是,不幸的是,MySQL 不支持 CHECK
。
如您所见,这不是 ORM 问题。
所以,你有两个选择:
- 实现一些触发器来模拟
CHECK
( see here ) 让应用程序进行检查:
@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); } }
关于java - 具有公共(public)外键的 Hibernate ManyToMany 关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41660132/