2013-02-28 109 views
3

這裏的DB模式如何在JPA中實現複雜的多對多關係?

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; 
} 

而對於一些原因,我不斷收到錯誤

Concrete type "class models.OrderDetail" with application identity does not declare any primary key fields. 

任何人都可以指出我哪裏出了問題?謝謝

+0

你爲什麼不乾脆用一個技術性,非複合主OrderDetail的關鍵,就像其他實體一樣。事情會非常簡單(和高效)。 – 2013-02-28 08:27:52

+0

@JBNizet:在某些方面更簡單,而不是其他方面。你爲什麼認爲它會更有效率? – 2013-02-28 08:42:06

+1

因爲數據庫上的數據庫索引比兩個索引更有效。但重點是簡單。在應用程序的每個級別使用單個值來識別訂單詳細信息將比使用兩個簡單得多。 – 2013-02-28 09:03:46

回答

0

首先OrderDetailPK必須執行Serializable

對於第二個請指定您將使用哪個ID,因爲您已指定product_idorder_id列爲insertable=false, updatable=false(只讀)。

所以你需要嘗試像下面這樣:

@EmbeddedId 
@AttributeOverrides({ 
     @AttributeOverride(name = "product_id",column = @Column(name = "product_id")), 
     @AttributeOverride(name = "listingId",column= @Column(name = "order_id")) 
}) 
private OrderDetailPK id; 

的更多信息,你可以在這裏找到:

http://docs.oracle.com/javaee/6/api/javax/persistence/EmbeddedId.html

http://docs.oracle.com/javaee/6/api/javax/persistence/AttributeOverride.html

0

EmbeddedId的javadoc:

不支持在嵌入式id類中定義的關係映射。

所以你不能這樣做。我不認爲JPA 1指定了一個標準的方式來實現這個(在JPA 2中有@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在實體的領域,但我沒有重新檢查,如果他們是強制性的)

+0

僅供參考,我得到一個新的'OrderDetail.id'錯誤不能成爲主鍵,它是一個不受支持的類型' – 2013-02-28 09:12:45

+0

你需要刪除'OrderDetail.id',就像我做的那樣:) – 2013-02-28 09:30:30

2

當我這樣做之前(詳細this question and answer),我做了可嵌入的ID原語中的字段(對應的ID字段所涉及的實體),然後在實體中使用@MapsId。我相信這是滿足所有要求的最簡單的(並且我敢說是正確的):實體中的字段是關係,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() {} 
}