使用@MapsId 在双向@OneToOne 上级联
Cascade on bidirectional @OneToOne with @MapsId
我致力于 spring 启动 2.1.1.RELEASE
、休眠 5.3.7.FINAL
规则是,该用户不能有 phone(phone
在 user
中可以为空)但是,phone 不能没有用户存在(user
在 phone
) 中不为空。
实体:
@Entity
public class Phone {
@Id
private Long id;
@OneToOne
@MapsId
@JoinColumn(name = "id")
private User user;
public Long getId() {
return id;
}
public void setId(final Long id) {
this.id = id;
}
public User getUser() {
return user;
}
public void setUser(final User user) {
this.user = user;
}
}
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private Phone phone;
public Long getId() {
return id;
}
public void setId(final Long id) {
this.id = id;
}
public Phone getPhone() {
return phone;
}
public void setPhone(final Phone phone) {
this.phone = phone;
}
}
控制器:
@RestController
@RequestMapping
public class UserController {
private final UserService userService;
public UserController(final UserService userService) {
this.userService = userService;
}
@GetMapping("/demo")
public void createUserAndAddPhone() {
final User user = new User();
userService.save(user);
final Phone phone = new Phone();
phone.setUser(user);
user.setPhone(phone);
userService.update(user);
}
}
存储库:
public interface UserRepository extends PagingAndSortingRepository<User, Long> {
}
服务:
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(final UserRepository userRepository) {
this.userRepository = userRepository;
}
@Transactional
public void save(final User user) {
userRepository.save(user);
}
@Transactional
public void update(final User user) {
userRepository.save(user);
}
}
表:
CREATE TABLE `phone` (
`id` bigint(20) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
应用程序 yml:
spring:
datasource:
url: jdbc:mysql://localhost:3308/demo?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false&allowPublicKeyRetrieval=true
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
jpa:
database-platform: org.hibernate.dialect.MySQL5Dialect
hibernate:
ddl-auto: validate
pom xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
调用 GET http://localhost:8080/demo
给我一个错误:
org.hibernate.id.IdentifierGenerationException: attempted to assign id
from null one-to-one property [com.example.demo.Phone.user]
当我注释掉 userService.save(user);
时,它起作用并生成:
insert into `user`
values ( )
-- Generated identifier: 13, using strategy: org.hibernate.id.ForeignGenerator
insert into `phone` (`id`) values (?)
-- Binding parameter [1] as [BIGINT] - [13]
但是如果 user
被持久化然后更新,它就不起作用(引发上述异常)
啊,到底是hibernate的bug(HHH-12436)。
它可以通过以下用例在纯休眠应用程序中重现:
Session session = sessionFactory.openSession();
Transaction tr1 = session.beginTransaction();
User user = new User();
session.persist(user);
tr1.commit();
Transaction tr2 = session.beginTransaction();
Phone newPhone = new Phone();
user.setPhone(newPhone);
newPhone.setUser(user);
session.merge(user);
tr2.commit();
session.close();
从上面可以看出link在hibernate 5.4分支中已经修复
P.S。我能够在最新的 5.3 版本中重现该问题 (5.3.18.Final
)
我致力于 spring 启动 2.1.1.RELEASE
、休眠 5.3.7.FINAL
规则是,该用户不能有 phone(phone
在 user
中可以为空)但是,phone 不能没有用户存在(user
在 phone
) 中不为空。
实体:
@Entity
public class Phone {
@Id
private Long id;
@OneToOne
@MapsId
@JoinColumn(name = "id")
private User user;
public Long getId() {
return id;
}
public void setId(final Long id) {
this.id = id;
}
public User getUser() {
return user;
}
public void setUser(final User user) {
this.user = user;
}
}
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private Phone phone;
public Long getId() {
return id;
}
public void setId(final Long id) {
this.id = id;
}
public Phone getPhone() {
return phone;
}
public void setPhone(final Phone phone) {
this.phone = phone;
}
}
控制器:
@RestController
@RequestMapping
public class UserController {
private final UserService userService;
public UserController(final UserService userService) {
this.userService = userService;
}
@GetMapping("/demo")
public void createUserAndAddPhone() {
final User user = new User();
userService.save(user);
final Phone phone = new Phone();
phone.setUser(user);
user.setPhone(phone);
userService.update(user);
}
}
存储库:
public interface UserRepository extends PagingAndSortingRepository<User, Long> {
}
服务:
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(final UserRepository userRepository) {
this.userRepository = userRepository;
}
@Transactional
public void save(final User user) {
userRepository.save(user);
}
@Transactional
public void update(final User user) {
userRepository.save(user);
}
}
表:
CREATE TABLE `phone` (
`id` bigint(20) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
应用程序 yml:
spring:
datasource:
url: jdbc:mysql://localhost:3308/demo?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false&allowPublicKeyRetrieval=true
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
jpa:
database-platform: org.hibernate.dialect.MySQL5Dialect
hibernate:
ddl-auto: validate
pom xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
调用 GET http://localhost:8080/demo
给我一个错误:
org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.example.demo.Phone.user]
当我注释掉 userService.save(user);
时,它起作用并生成:
insert into `user`
values ( )
-- Generated identifier: 13, using strategy: org.hibernate.id.ForeignGenerator
insert into `phone` (`id`) values (?)
-- Binding parameter [1] as [BIGINT] - [13]
但是如果 user
被持久化然后更新,它就不起作用(引发上述异常)
啊,到底是hibernate的bug(HHH-12436)。
它可以通过以下用例在纯休眠应用程序中重现:
Session session = sessionFactory.openSession();
Transaction tr1 = session.beginTransaction();
User user = new User();
session.persist(user);
tr1.commit();
Transaction tr2 = session.beginTransaction();
Phone newPhone = new Phone();
user.setPhone(newPhone);
newPhone.setUser(user);
session.merge(user);
tr2.commit();
session.close();
从上面可以看出link在hibernate 5.4分支中已经修复
P.S。我能够在最新的 5.3 版本中重现该问题 (5.3.18.Final
)