@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=?

数据库表关系:

image-20200503224816939

GitHub

双向@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=?

数据库表关系:

image-20200503225755108

使用双向@OneToOne关联时,Hibernate在获取子对象时会强制执行唯一约束。如果同一个父对象有多个子对象,则Hibernate将抛出org.hibernate.exception.ConstraintViolationException

尽管使用注释FetchType.LAZY延迟获取子对象,但是Hibernate不执行,因为它无法知道关联是否存在null

但是,如果您确实需要使用双向关联,并且要确保始终会延迟地获取它,那么您需要启用字节码增强功能,并同时使用 @LazyToOne注释。

GitHub

双向@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=?    

数据库表关系:

image-20200503231300804

GitHub

从上述几种情况看出,我们应该尽量采用单向@OneToOne设置关联关系,减少配置的难度及解决延时加载的问题。