How to implement a complex many to many relationship in JPA?

前端 未结 3 673
独厮守ぢ
独厮守ぢ 2020-12-30 17:00

Here the db schema

CREATE TABLE Products
(
    id          INT NOT NULL AUTO_INCREMENT,
    category_id  INT NOT NULL,
    description VARCHAR(100),
    pric         


        
相关标签:
3条回答
  • 2020-12-30 17:23

    From the EmbeddedId javadoc:

    Relationship mappings defined within an embedded id class are not supported.

    So you cannot do it this way. I don't think JPA 1 specifies a standard way to implement this (in JPA 2 there is @MapsId but I never tried), but this is what I usually do and most implementations (I think at least Hibernate, EclipseLink and OpenJPA) support it:

    Declare your primary key class using primitive types:

    @Embeddable
    public class OrderDetailPK implements Serializable
    {
        private int product;
        private int order;
    
        public OrderDetailPK() {}
    
        ...
    }
    

    Annotate your entity with @IdClass and declare the fields using the same name but the desired types:

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

    (I have always kept the @Id on the fields in the entity but I didn't recheck if they are mandatory)

    0 讨论(0)
  • 2020-12-30 17:26

    When i did this before (as detailed in this question and answer), i made the fields in the embeddable ID primitives (corresponding to the ID fields of the entities referred to), and then used @MapsId in the entity. I believe this is the simplest (and dare i say correct) of meeting all the requirements: that the fields in the entity are relationships, that the fields in the ID class are primitive, that every column is mapped exactly once (the @MapsId fields not really being mappings, but sort of aliases).

    Applying that to your case, the ID class looks like:

    @Embeddable
    public class OrderDetailPK {
        private final int productId;
        private final int orderId;
    
        public OrderDetailPK(int productId, int orderId) {
            this.productId = productId;
            this.orderId = orderId;
        }
    }
    

    And the entity class looks like:

    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() {}
    }
    
    0 讨论(0)
  • 2020-12-30 17:35

    First of all OrderDetailPK has to implement Serializable.

    For second please specify which ID's you are going to use, because you has specified columns product_id and order_id as insertable=false, updatable=false (read-only).

    So you need to try something like the following:

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

    More information you may find here:

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

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

    0 讨论(0)
提交回复
热议问题