Spring JPA: проблема с общим ПК в отношениях один-к-одному - PullRequest
2 голосов
/ 31 мая 2019

Я пытаюсь создать отношения один-к-одному с общим ПК, но я испытываю трудности после многих попыток ...

Я постараюсь предоставить всю возможную информацию:

Технологии, которые я использую:

  • Пружинный ботинок 2.1.5
  • Пружина JPA
  • Flyway

Конфигурация источника данных:

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

Модели баз данных:

@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;
}

Файлы миграции 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);

Интеграционный тест:

@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);
        }
}

Ошибка:

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)

Заранее спасибо за помощь.

Ура!

1 Ответ

4 голосов
/ 31 мая 2019

Вы не понимаете, как работает двунаправленное отображение, поэтому вам нужно продумать это.Код

@OneToOne(fetch = FetchType.LAZY)
@MapsId
private Customer customer;

в Address делает отображение однонаправленным.Обычно, чтобы сохранить это, вам нужно установить поле customer и сохранить его.

address.setCustomer(customer);        
addressRepo.save(address);

Однако вы определили двунаправленное отображение и предоставили каскадную аннотацию.

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

Каскадная аннотация освобождает вас от необходимости выполнять две персистентные операции (в вашем коде), но это не означает, что вам не нужно устанавливать поле клиента в адресе.Кроме того, как вы заметили, для того, чтобы каскадная операция работала, вам нужно установить адресное поле заказчика.

customer.setAddress(address);

Итак, чтобы код работал правильно, вам нужно изменить его, чтобы установитьклиент по адресу.

Address address = new Address();
Customer customer = new Customer();
customer.setAddress(address);
address.setCustomer(customer);
customerRepo.save(customer);

При двунаправленном отображении вы должны управлять обеими сторонами отношений или использовать его однонаправленным образом для сохранения и двунаправленным способом для извлечения.Если вы добавите каскадную аннотацию, то вам придется также управлять обеими сторонами отношений для сохранения.

...