Spring JPA:一对一关系中共享 PK 的问题

Jos*_*ara 4 spring hibernate jpa one-to-one flyway

我正在尝试与共享 PK 建立一对一的关系,但是在尝试了很多事情之后我陷入了困境......

我会尽量提供所有可能的信息:

我正在使用的技术:

  • 春季启动 2.1.5
  • 春季JPA
  • 飞道

数据源配置:

spring.datasource.url = jdbc:mysql://localhost:3306/customers?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true&useSSL=false
spring.datasource.username = admin
spring.datasource.password = root

spring.jpa.database-platform = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql = true
Run Code Online (Sandbox Code Playgroud)

数据库模型:

@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name = "customer", schema = "customers")
public class Customer {

    @Id
    @GeneratedValue(generator = "uuid2")
    @GenericGenerator(name = "uuid2", strategy = "uuid2")
    @Column(name = "customer_id", columnDefinition = "BINARY(16)")
    private UUID customerId;

    @OneToOne(mappedBy = "customer", cascade = CascadeType.ALL)
    private Address address;
}

@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name = "address", schema = "customers")
public class Address {

    @Id
    @Column(name = "address_id", columnDefinition = "BINARY(16)")
    private UUID addressId;

    @OneToOne(fetch = FetchType.LAZY)
    @MapsId
    private Customer customer;
}
Run Code Online (Sandbox Code Playgroud)

Flyway 迁移文件:

CREATE SCHEMA IF NOT EXISTS CUSTOMERS;

CREATE TABLE CUSTOMERS.CUSTOMER
(
    customer_id BINARY(16) NOT NULL PRIMARY KEY,
) ENGINE = InnoDB;

CREATE TABLE CUSTOMERS.ADDRESS
(
    address_id BINARY(16) NOT NULL PRIMARY KEY,
) ENGINE = InnoDB;

ALTER table CUSTOMERS.ADDRESS
ADD CONSTRAINT fk_customer_address
FOREIGN KEY (address_id)
REFERENCES CUSTOMERS.CUSTOMER (customer_id);
Run Code Online (Sandbox Code Playgroud)

集成测试:

@RunWith(SpringRunner.class)
@SpringBootTest
public class CustomerRepositoryIntegrationTest {

    @Autowired
    private CustomerRepository cut;

    @Test
        public void testSaveAndDeleteCustomer() {
            Address address = new Address();
            Customer customer = new Customer();
            customer.setAddress(address);

            cut.save(customer);

            Customer retrievedCustomer = cut.findAll().get(0);
            assertEquals(customer, retrievedCustomer);
        }
}
Run Code Online (Sandbox Code Playgroud)

错误:

ERROR] testSaveAndDeleteCustomer(com.foxhound.customers.repositories.CustomerRepositoryIntegrationTest)  Time elapsed: 0.034 s  <<< ERROR!
org.springframework.orm.jpa.JpaSystemException: attempted to assign id from null one-to-one property [com.foxhound.customers.models.Address.customer]; nested exception is org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.foxhound.customers.models.Address.customer]
    at com.foxhound.customers.repositories.CustomerRepositoryIntegrationTest.testSaveAndDeleteCustomer(CustomerRepositoryIntegrationTest.java:47)
Caused by: org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.foxhound.customers.models.Address.customer]
    at com.foxhound.customers.repositories.CustomerRepositoryIntegrationTest.testSaveAndDeleteCustomer(CustomerRepositoryIntegrationTest.java:47)
Run Code Online (Sandbox Code Playgroud)

预先感谢您的帮助。

干杯!

K.N*_*las 5

您不了解双向映射的工作原理,因此您需要仔细考虑。编码

@OneToOne(fetch = FetchType.LAZY)
@MapsId
private Customer customer;
Run Code Online (Sandbox Code Playgroud)

inAddress使映射成为单向的。通常为了保存它,您需要设置customer字段并保存它。

address.setCustomer(customer);        
addressRepo.save(address);
Run Code Online (Sandbox Code Playgroud)

但是,您已经定义了双向映射并提供了级联注释。

@OneToOne(mappedBy = "customer", cascade = CascadeType.ALL)
private Address address;
Run Code Online (Sandbox Code Playgroud)

级联注释使您不必执行两个持久性操作(在您的代码中),但这并不意味着您不必在地址中设置客户字段。此外,正如您所注意到的,为了使级联操作起作用,您需要设置客户的地址字段。

customer.setAddress(address);
Run Code Online (Sandbox Code Playgroud)

因此,为了使代码正常工作,您需要更改它以在地址中设置客户。

Address address = new Address();
Customer customer = new Customer();
customer.setAddress(address);
address.setCustomer(customer);
customerRepo.save(customer);
Run Code Online (Sandbox Code Playgroud)

使用双向映射,您必须管理关系的双方或以单向方式使用它进行持久化,并以双向方式使用它进行检索。如果您添加级联注释,那么您还必须管理关系的双方以进行持久化。

  • 您好 JoseLara,您可以通过在 @MapsId 注释下添加 @JoinColumn(name = "customer_id") 来重命名客户字段 (2认同)