Java 如何在JPA中实现复杂的多对多关系?
这里是db模式Java 如何在JPA中实现复杂的多对多关系?,java,jpa,Java,Jpa,这里是db模式 CREATE TABLE Products ( id INT NOT NULL AUTO_INCREMENT, category_id INT NOT NULL, description VARCHAR(100), price DECIMAL(10, 2) NOT NULL, PRIMARY KEY (id), FOREIGN KEY (category_id) REFERENCES Categori
CREATE TABLE Products
(
id INT NOT NULL AUTO_INCREMENT,
category_id INT NOT NULL,
description VARCHAR(100),
price DECIMAL(10, 2) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (category_id) REFERENCES Categories(id)
) ENGINE = INNODB;
CREATE TABLE Orders
(
id INT NOT NULL AUTO_INCREMENT,
customer_id INT NOT NULL,
status VARCHAR(20) NOT NULL,
date_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
FOREIGN KEY (customer_id) REFERENCES Customers(id)
) ENGINE = INNODB;
CREATE TABLE OrderDetails
(
product_id INT NOT NULL,
order_id INT NOT NULL,
quantity INT NOT NULL,
subtotal DECIMAL(10, 2) NOT NULL,
PRIMARY KEY (product_id, order_id),
FOREIGN KEY (product_id) REFERENCES Products(id),
FOREIGN KEY (order_id) REFERENCES Orders(id)
) ENGINE = INNODB;
模型
@Embeddable
public class OrderDetailPK
{
private Product product;
private Order order;
public OrderDetailPK() {}
public OrderDetailPK(Product product, Order order)
{
this.product = product;
this.order = order;
}
}
public class OrderDetail {
@EmbeddedId
private OrderDetailPK id;
@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn(name="product_id", insertable=false, updatable=false)
private Product product;
@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn(name="order_id", insertable=false, updatable=false)
private Order order;
private int quantity;
private double subtotal;
public OrderDetail() {}
public OrderDetail(OrderDetailPK id, int quantity, double subtotal)
{
this.product = id.getProduct();
this.order = id.getOrder();
this.quantity = quantity;
this.subtotal = subtotal;
}
// getters, setters
}
public class Product {
@Id
private int id;
private String description;
private double price;
@ManyToOne
@JoinColumn(name="category_id")
private Category category;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "Products")
private List<OrderDetail> orderDetail;
}
public class Order {
@Id
private int id;
@ManyToOne
@JoinColumn(name="customer_id")
private Customer customer;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "Orders")
private List<OrderDetail> orderDetail;
}
有人能告诉我问题出在哪里吗?谢谢首先,
OrderDetailPK
必须实现Serializable
第二,请指定要使用的ID,因为您已将列product\u ID
和order\u ID
指定为insertable=false,updateable=false
(只读)
因此,您需要尝试以下方法:
@EmbeddedId
@AttributeOverrides({
@AttributeOverride(name = "product_id",column = @Column(name = "product_id")),
@AttributeOverride(name = "listingId",column= @Column(name = "order_id"))
})
private OrderDetailPK id;
您可以在此处找到更多信息:
从
EmbeddedId
javadoc:
不支持在嵌入式id类中定义的关系映射
所以你不能这样做。我不认为JPA1指定了一种标准的实现方法(在JPA2中有@MapsId
,但我从未尝试过),但这是我通常做的,大多数实现(我认为至少Hibernate、EclipseLink和OpenJPA)都支持它:
使用基元类型声明主键类:
@Embeddable
public class OrderDetailPK implements Serializable
{
private int product;
private int order;
public OrderDetailPK() {}
...
}
使用@IdClass
注释实体,并使用相同的名称声明字段,但声明所需的类型:
@Entity
@IdClass(OrderDetailPK.class)
public class OrderDetail {
@Id
@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn(name="product_id", insertable=false, updatable=false)
private Product product;
@Id
@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn(name="order_id", insertable=false, updatable=false)
private Order order;
...
}
(我一直在实体中的字段上保留@Id
,但我没有重新检查它们是否是强制性的)当我之前这样做时(如中所述),我在可嵌入Id原语中创建字段(对应于所指实体的Id字段),然后在实体中使用。我相信这是满足所有要求的最简单(我敢说是正确的):实体中的字段是关系,ID类中的字段是基本字段,每个列只映射一次(@MapsId
字段不是真正的映射,而是某种别名)
将其应用到您的案例中,ID类如下所示:
@Embeddable
public class OrderDetailPK {
private final int productId;
private final int orderId;
public OrderDetailPK(int productId, int orderId) {
this.productId = productId;
this.orderId = orderId;
}
}
public class OrderDetail {
@EmbeddedId
private OrderDetailPK id;
@ManyToOne(cascade = CascadeType.ALL)
@MapsId("productId")
private Product product;
@ManyToOne(cascade = CascadeType.ALL)
@MapsId("orderId")
private Order order;
private int quantity;
private double subtotal;
public OrderDetail(Product product, Order order, int quantity, double subtotal) {
this.id = new OrderDetailPK(product.getId(), order.getId());
this.product = product;
this.order = order;
this.quantity = quantity;
this.subtotal = subtotal;
}
protected OrderDetail() {}
}
实体类如下所示:
@Embeddable
public class OrderDetailPK {
private final int productId;
private final int orderId;
public OrderDetailPK(int productId, int orderId) {
this.productId = productId;
this.orderId = orderId;
}
}
public class OrderDetail {
@EmbeddedId
private OrderDetailPK id;
@ManyToOne(cascade = CascadeType.ALL)
@MapsId("productId")
private Product product;
@ManyToOne(cascade = CascadeType.ALL)
@MapsId("orderId")
private Order order;
private int quantity;
private double subtotal;
public OrderDetail(Product product, Order order, int quantity, double subtotal) {
this.id = new OrderDetailPK(product.getId(), order.getId());
this.product = product;
this.order = order;
this.quantity = quantity;
this.subtotal = subtotal;
}
protected OrderDetail() {}
}
为什么不像其他实体一样,为OrderDetail使用一个技术性的非复合主键呢。事情会简单得多(而且效率高)。@JBNizet:在某些方面更简单,在其他方面则不然。为什么你认为它会更有效?因为一个数据库索引一个数值比一个索引两个数值更有效。但主要的一点是简单。在应用程序的每个级别使用单个值来标识订单详细信息,将比使用两个值简单得多。仅供参考,我遇到了一个新错误
OrderDetail.id“不能作为主键。它的类型不受支持
,您需要像我一样删除OrderDetail.id
)