JPA ManyToMany Join Table has all attributes as PK

I use Hibernate 3.3.1 and follow this approximate table structure in the simulation, but I am unable to create a join table with additional attributes.

This is a many-to-many relationship between the Order and Product tables. Connection table - Order Detail table. I followed the approach mentioned here .

Now i have entities

 @Entity @Table(name = "Orders") public class Order { @OneToMany(mappedBy="order") private List<OrderDetail> orderItems; } 

and

 @Entity @Table(name="PRODUCTS") public class Product { @OneToMany(mappedBy="product") private List<OrderDetail> orderItems; } 

and

 @Entity @IdClass(OrderDetail.class) @Table(name = "ORDER_DETAIL") public class OrderDetail implements Serializable { @Id @Column(name="ORDER_ID") private Long orderId; @Id @Column(name="PRODUCT_ID") private Long productId; @Column(name = "PRICE") private double price; @Column(name = "LAST_UPDATED_TIME") private Date lastUpdatedTime; @ManyToOne @JoinColumn(name = "ORDER_ID") private Order order; @ManyToOne @JoinColumn(name = "PRODUCT_ID") private Product product; } 

and

 public class OrderDetailId implements Serializable { private Long orderId; private Long productId; } 

I used Apache Derby to run the test, but I was having problems with the created table structure.

 CREATE TABLE ORDER_DETAIL ( PRODUCT_ID BIGINT NOT NULL, ORDER_ID BIGINT NOT NULL, LAST_UPDATED_TIME TIMESTAMP NOT NULL, PRICE DOUBLE NOT NULL ); CREATE INDEX SQL120323142938020 ON ORDER_DETAIL (PRODUCT_ID ASC); CREATE UNIQUE INDEX SQL120323142937810 ON ORDER_DETAIL (PRODUCT_ID ASC, ORDER_ID ASC, LAST_UPDATED_TIME ASC, PRICE ASC); ALTER TABLE ORDER_DETAIL ADD CONSTRAINT SQL120323142937810 PRIMARY KEY (PRODUCT_ID, ORDER_ID, LAST_UPDATED_TIME, PRICE); ALTER TABLE ORDER_DETAIL ADD CONSTRAINT FK4A94AA82CC6D989A FOREIGN KEY (PRODUCT_ID) REFERENCES PRODUCTS (PROD_ID); 

It seems like he created all my columns as a primary key. Why is this so?

+3
source share
3 answers

You are using your entity class as an argument to IdClass. This is not true. The class id should be used. In addition, separate fields for id in the union are not needed.

Go for something like the code below. I can’t guarantee that it works on such an old version of Hibernate, but it probably never works. Worth a try anyway. This will not hurt to upgrade to at least version 3.5.X (or rather, even more recent) if you want to use the JPA 2.0 features. Constructors / peers, etc. Removed to save space.

 @Entity @Table(name = "Orders") public class Order { @Id Long id; @OneToMany(mappedBy="order") private List<OrderDetail> orderItems; } @Entity @Table(name="PRODUCTS") public class Product { @Id Long id; @OneToMany(mappedBy="product") private List<OrderDetail> orderItems; } @Entity @IdClass(OrderDetailId.class) @Table(name = "ORDER_DETAIL") public class OrderDetail implements Serializable { @Id @ManyToOne @JoinColumn(name = "ORDER_ID") private Order order; @Id @ManyToOne @JoinColumn(name = "PRODUCT_ID") private Product product; @Column(name = "PRICE") private double price; //Maybe you also want to use @TemporalType here @Column(name = "LAST_UPDATED_TIME") private Date lastUpdatedTime; } public class OrderDetailId implements Serializable { private Long order; private Long product; } 

UPDATE 08/15/2017 In JPA 2.1 and later, you do not need to add a class for the composite identifier, and you can do this as follows:

 @Entity @Table(name = "ORDER_DETAIL") public class OrderDetail implements Serializable { @Id @ManyToOne @JoinColumn(name = "ORDER_ID") private Order order; @Id @ManyToOne @JoinColumn(name = "PRODUCT_ID") private Product product; @Column(name = "PRICE") private double price; //Maybe you also want to use @TemporalType here @Column(name = "LAST_UPDATED_TIME") private Date lastUpdatedTime; } 
+7
source

The code below generates tables as desired, I tested it in MySQL (just creating a table, not CRUD):

 @Entity @Table(name = "orders") public class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @OneToMany(mappedBy = "orderDetailId.order") private List<OrderDetail> orderItems; //get set ….. } @Entity @Table(name="products") public class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @OneToMany(mappedBy = "orderDetailId.product") private List<OrderDetail> orderItems; //get set …… } @Entity @Table(name = "order_detail") public class OrderDetail { @Id private OrderDetailId orderDetailId; private double price; @Temporal(TemporalType.TIMESTAMP) private Date lastUpdatedTime; //get set …. } @Embeddable public class OrderDetailId implements Serializable{ private Order order; private Product product; @ManyToOne(fetch=FetchType.LAZY) @Access(AccessType.PROPERTY) public Order getOrder() { return order; } public void setOrder(Order order) { this.order = order; } @ManyToOne(fetch=FetchType.LAZY) @Access(AccessType.PROPERTY) public Product getProduct() { return product; } public void setProduct(Product product) { this.product = product; } //hash code equals override } 

Hibernate DEBUG details as below

 DEBUG: org.hibernate.tool.hbm2ddl.SchemaUpdate - create table order_detail (lastUpdatedTime datetime, price double precision not null, product_id bigint, order_id bigint, primary key (order_id, product_id)) ENGINE=InnoDB DEBUG: org.hibernate.tool.hbm2ddl.SchemaUpdate - create table orders (id bigint not null auto_increment, primary key (id)) ENGINE=InnoDB DEBUG: org.hibernate.tool.hbm2ddl.SchemaUpdate - create table products (id bigint not null auto_increment, primary key (id)) ENGINE=InnoDB DEBUG: org.hibernate.tool.hbm2ddl.SchemaUpdate - alter table order_detail add index FK23AE5A622128CF91 (order_id), add constraint FK23AE5A622128CF91 foreign key (order_id) references orders (id) DEBUG: org.hibernate.tool.hbm2ddl.SchemaUpdate - alter table order_detail add index FK23AE5A62EB201631 (product_id), add constraint FK23AE5A62EB201631 foreign key (product_id) references products (id) 
+5
source

Many-to-many connectivity is logically determined using the @ManyToMany annotation. You must also describe the association table and join conditions using the @JoinTable annotation. If the association is bidirectional, one side must be the owner, and one side must be the back end (i.e., it will be ignored when updating the relationship values ​​in the association table). I gave an example between employee and employer tables. This way you can change your code.

 @Entity public class Employer implements Serializable { @ManyToMany( targetEntity=org.hibernate.test.metadata.manytomany.Employee.class, cascade={CascadeType.PERSIST, CascadeType.MERGE} ) @JoinTable( name="EMPLOYER_EMPLOYEE", joinColumns=@JoinColumn (name="EMPER_ID"), inverseJoinColumns=@JoinColumn (name="EMPEE_ID") ) public Collection getEmployees() { return employees; } ... } @Entity public class Employee implements Serializable { @ManyToMany( cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "employees", targetEntity = Employer.class ) public Collection getEmployers() { return employers; } } 
0
source

All Articles