JPA、Hibernate映射@OneToOne关系
@OneToOne注释分两种映射关联:
-
单向
@OneToMany
关联 -
双向
@OneToMany
关联
单向@OneToOne
@Entity
@Table
public class Phone extends AbstractEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Column
private String number;
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "details_id")
private PhoneDetails details;
//Getters and setters are omitted for brevity
}
@Entity
@Table
public class PhoneDetails extends AbstractEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Column
private String provider;
@Column
private String technology;
//Getters and setters are omitted for brevity
}
PhoneDetails phoneDetails = new PhoneDetails("provider", "technology");
Phone phone = new Phone("number", phoneDetails);
entityManager.persist(phone);
entityManager.clear();
entityManager.find(PhoneDetails.class, 1);
Hibernate将执行以下SQL语句:
insert into phone_details (id, provider, technology) values (null, ?, ?)
insert into phone (id, details_id, number) values (null, ?, ?)
select phonedetai0_.id as id1_6_0_, phonedetai0_.provider as provider2_6_0_, phonedetai0_.technology as technolo3_6_0_ from phone_details phonedetai0_ where phonedetai0_.id=?
数据库表关系:
双向@OneToOne
@Entity
@Table
public class Book extends AbstractEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Column
private String name;
@OneToOne(cascade = CascadeType.ALL, mappedBy = "book", orphanRemoval = true, fetch = FetchType.LAZY)
private BookDetails details;
//Getters and setters are omitted for brevity
}
@Table
@Entity
public class BookDetails extends AbstractEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Column
private String author;
@Column
private String press;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "book_id")
private Book book;
//Getters and setters are omitted for brevity
}
Book book = new Book("name");
BookDetails bookDetails = new BookDetails("author", "press");
book.addDetails(bookDetails);
entityManager.persist(book);
entityManager.clear();
entityManager.find(Book.class, 1);
Hibernate将执行以下SQL语句:
insert into book (id, name) values (null, ?)
insert into book_details (id, author, book_id, press) values (null, ?, ?, ?)
select book0_.id as id1_0_0_, book0_.name as name2_0_0_ from book book0_ where book0_.id=?
select bookdetail0_.id as id1_1_0_, bookdetail0_.author as author2_1_0_, bookdetail0_.book_id as book_id4_1_0_, bookdetail0_.press as press3_1_0_ from book_details bookdetail0_ where bookdetail0_.book_id=?
数据库表关系:
使用双向@OneToOne
关联时,Hibernate在获取子对象时会强制执行唯一约束。如果同一个父对象有多个子对象,则Hibernate将抛出org.hibernate.exception.ConstraintViolationException
。
尽管使用注释FetchType.LAZY
延迟获取子对象,但是Hibernate不执行,因为它无法知道关联是否存在null
。
但是,如果您确实需要使用双向关联,并且要确保始终会延迟地获取它,那么您需要启用字节码增强功能,并同时使用 @LazyToOne
注释。
双向@OneToOne延时加载
@Table
@Entity
public class Content extends AbstractEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Column
private String title;
@OneToOne(mappedBy = "content", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
private ContentDetails details;
//Getters and setters are omitted for brevity
public void addDetails(ContentDetails details) {
details.setContent(this);
this.details = details;
}
public void removeDetails() {
if (details != null) {
details.setContent(null);
this.details = null;
}
}
}
@Table
@Entity
public class ContentDetails extends AbstractEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Column
private String text;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "content_id")
private Content content;
//Getters and setters are omitted for brevity
}
pom.xml增加hibernate-enhance-maven-plugin
插件,启用字节码增强功能
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<version>5.4.12.Final</version>
<executions>
<execution>
<configuration>
<failOnError>true</failOnError>
<enableLazyInitialization>true</enableLazyInitialization>
<enableDirtyTracking>true</enableDirtyTracking>
<enableAssociationManagement>true</enableAssociationManagement>
</configuration>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
Content content = new Content();
content.setTitle("title");
ContentDetails details = new ContentDetails("text");
content.addDetails(details);
entityManager.persist(content);
entityManager.clear();
entityManager.find(Content.class, 1);
Hibernate将执行以下SQL语句:
insert into content (id, title) values (null, ?)
insert into content_details (id, content_id, text) values (null, ?, ?)
select content0_.id as id1_3_0_, content0_.title as title2_3_0_ from content content0_ where content0_.id=?
数据库表关系:
从上述几种情况看出,我们应该尽量采用单向@OneToOne设置关联关系,减少配置的难度及解决延时加载的问题。