JPA 2.0 (Hibernate) 使用 @JoinTable 为 @OneToMany 生成不正确的连接表 PK
Posted
技术标签:
【中文标题】JPA 2.0 (Hibernate) 使用 @JoinTable 为 @OneToMany 生成不正确的连接表 PK【英文标题】:JPA 2.0 (Hibernate) generates incorrect join table PK for @OneToMany with @JoinTable 【发布时间】:2014-06-13 01:04:57 【问题描述】:我使用 JPA 2.0 和 Hibernate 4.3.5 并尝试使用 Hibernate 自动生成我的表。
我在我的联系人实体中创建了以下条目:
/**
* Address
*/
@Valid
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinTable(name = "contact_address", joinColumns = @JoinColumn(name = "contact_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "address_id", referencedColumnName = "id"))
@OrderColumn
private List<Address> addresses;
Hibernate 正在正确创建联系人表,但创建的连接表没有正确的 PK:
CREATE TABLE `contact_address` (
`contact_id` bigint(20) NOT NULL,
`address_id` bigint(20) NOT NULL,
`addresses_order` int(11) NOT NULL,
PRIMARY KEY (`contact_id`),
UNIQUE KEY `UK_mvvtppjfu6d0lcjm83u5youn8` (`address_id`),
CONSTRAINT `FK_4fntyt0q2l6vkfg7t38pg4i94` FOREIGN KEY (`contact_id`) REFERENCES `contact` (`id`),
CONSTRAINT `FK_mvvtppjfu6d0lcjm83u5youn8` FOREIGN KEY (`address_id`) REFERENCES `address` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
这里列出的PK只有contact_id
,不正确。那将不允许我分配多个地址。相反,PK 应该是 contact_id
、address_id
的复合 PK。
是 Hibernate 出了问题,还是我的 JPA 注释有问题?这是一种单向关联。
正如一些人所说,从技术上讲,我不需要 @OneToMany 的连接表,但考虑到我需要在其他实体对象中使用 Address 实体,使用连接对我来说更简洁所有关联的表格。
我已经设法使用 @ManyToMany 关联并在连接列上指定唯一约束来破解解决方案,但我试图了解我的 JPA 是否有问题或者它是否是 Hibernate 错误。
【问题讨论】:
为什么需要 JoinTable 来实现 OneToMany 关系?据我所知,ManyToMany 需要一个 JoinTable。 我没有在我的问题中指定它,但我需要在几个不同的实体中使用 Address 对象。因此,我在这里使用连接表更简洁,即使它只是一个 OneToMany。 我认为这个问题在这里得到了很好的评论***.com/questions/3113885/… \@OneToMany 的意思是,您可以为您的联系人分配多个地址,但不能将同一个地址分配给更多联系人。因此,address_id 而非 address_id + contact_id 的唯一约束。这就是 \@ManyToMany 起作用的原因。 【参考方案1】:如果您需要联系人和地址之间的一对多关系,其中一个联系人有多个地址。如果没有关联表,以下就足够了。
@Valid
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name="contact_id", referencedColumnName="id")
private List<Address> addresses;
【讨论】:
虽然这在技术上确实有效,但它并不能修复我的连接表。 :) 我需要/想要使用 JoinTable,即使它是 OneToMany,因为我需要在其他实体中使用 Address 实体,并且在这种情况下使用连接表在架构上是一种更简洁的解决方案。【参考方案2】:您需要为您的问题添加更多详细信息。例如,您如何注释 Address
实体?
无论如何,如果您正确注释和定义实体,Hibernate 会为您正确生成表格:
@Entity
public class Contact
@Id
@GeneratedValue(generator = "UUID")
@GenericGenerator(
name = "UUID",
strategy = "org.hibernate.id.UUIDGenerator"
)
private String id;
private String fullName;
@OneToMany(cascade = CascadeType.ALL)
@JoinTable(
name = "contact_addresses",
joinColumns = @JoinColumn(
name = "contact_id",
referencedColumnName = "id"
),
inverseJoinColumns = @JoinColumn(
name = "address_id",
referencedColumnName = "id"
)
)
private Set<Address> addresses = new HashSet<>();
public String getId()
return id;
public void setId(String id)
this.id = id;
public String getFullName()
return fullName;
public void setFullName(String brand)
this.fullName = brand;
public Set<Address> getAddresses()
return addresses;
public void setAddresses(Set<Address> contacts)
this.addresses = addresses;
public void addAddress(Address address)
getAddresses().add(address);
address.setContact(this);
@Entity
public class Address
@Id
@GeneratedValue(generator = "UUID")
@GenericGenerator(
name = "UUID",
strategy = "org.hibernate.id.UUIDGenerator"
)
private String id;
private String streetAddress;
@ManyToOne(cascade = CascadeType.ALL)
private Contact contact;
public String getId()
return id;
public void setId(String id)
this.id = id;
public String getStreetAddress()
return streetAddress;
public void setStreetAddress(String name)
this.streetAddress = name;
public Contact getContact()
return contact;
public void setContact(Contact contact)
this.contact = contact;
这是一个示例测试用例:
@Test
public void testContactAddresses()
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("ContactAddresses");
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
Contact contact = new Contact();
contact.setFullName("Steve Ballmer");
Address address1 = new Address();
address1.setStreetAddress("Developers Street");
Address address2 = new Address();
address2.setStreetAddress("Developers Developers Street");
Address address3 = new Address();
address3.setStreetAddress("Developers Developers Developers Street");
contact.addAddress(address1);
contact.addAddress(address2);
contact.addAddress(address3);
entityManager.persist(contact);
transaction.commit();
下面是 Hibernate 执行的 DDL 语句:
Hibernate: drop table Address if exists
Hibernate: drop table Contact if exists
Hibernate: drop table contact_addresses if exists
Hibernate: create table Address (id varchar(255) not null, streetAddress varchar(255), contact_id varchar(255), primary key (id))
Hibernate: create table Contact (id varchar(255) not null, fullName varchar(255), primary key (id))
Hibernate: create table contact_addresses (
contact_id varchar(255) not null,
address_id varchar(255) not null,
primary key (contact_id, address_id)
)
Hibernate: alter table contact_addresses add constraint UK_mxmb2y0iu8624h4rrdamayobp unique (address_id)
Hibernate: alter table Address add constraint FK98iji1i9ycae5a36rman0vd17 foreign key (contact_id) references Contact
Hibernate: alter table contact_addresses add constraint FK6n27pwv86i3cx03jv3i9taidw foreign key (address_id) references Address
Hibernate: alter table contact_addresses add constraint FKmw2sdpyxrxj3obg1x1ltdlwbo foreign key (contact_id) references Contact
【讨论】:
以上是关于JPA 2.0 (Hibernate) 使用 @JoinTable 为 @OneToMany 生成不正确的连接表 PK的主要内容,如果未能解决你的问题,请参考以下文章
JPA 2.0 使用 Hibernate 作为提供者 - 例外:EntityManager 没有持久性提供者
JPA 2.0:在JPA 2.0中使用javax.validation。*包的例外
无法使用 JPA 2.0、Spring 2.5.6、Hibernate 3.6.1 和 Maven 在 HSQLDB 中持久化
spring.jpa.hibernate.ddl-auto=create 在带有 SpringBoot 2.0 的 Hibernate 5 中不起作用