Как смоделировать отношения один-к-одному с Spring Data JDBC? - PullRequest
0 голосов
/ 04 декабря 2018

Я хочу смоделировать отношения один-к-одному, используя Spring Data JDBC и PostgreSQL, но у меня возникают проблемы с правильной настройкой корневых агрегатов.

Существует следующий сценарий: Изображение , SQL
Каждый механизм уникален, car имеет уникальный столбец engine_id, который является внешним ключом engine.idТо же самое касается truck.Поэтому легковые и грузовые автомобили должны быть корневыми агрегатами, поэтому, когда легковой или грузовой автомобиль удаляется, также должна быть удалена указанная строка из таблицы двигателей.

Из моего понимания Агрегаты JDBC данных Spring

Если несколько агрегатов ссылаются на одну и ту же сущность, эта сущность не может быть частью этих агрегатов, ссылающихся на нее с тех пор, какон может быть частью только одного агрегата.

Таким образом, вопросы:

  • Возможен ли обходной путь из-за объяснения выше, так что при выполнении операций CRUD для car и truck изменения отражаются в engine также?
  • Каков наилучший способ реализации этого отношения в Java с использованием Spring Data JDBC?

Вот мой дубль, который не работает, но должен уточнить, что я пытаюсь достичь.

Car.java

package com.example.dao.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Persistable;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;

import java.util.UUID;

@Table("car")
public class Car implements Persistable<UUID> {

    @Id
    private UUID id;

    String brand;

    String model;

    @Column("engine_id")
    Engine engine;

    public void setId(UUID id) {
        this.id = id;
    }

    @Override
    public UUID getId() {
        return id;
    }

    @Override
    public boolean isNew() {
        return id == null;
    }
}

Engine.java

package com.example.dao.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Persistable;
import org.springframework.data.relational.core.mapping.Table;

import java.time.LocalDateTime;
import java.util.UUID;

@Table("engine")
public class Engine implements Persistable<UUID> {

    @Id
    private UUID id;

    String name;

    LocalDateTime dateCreated;

    String type;

    public void setId(UUID id) {
        this.id = id;
    }

    @Override
    public UUID getId() {
        return id;
    }

    @Override
    public boolean isNew() {
        return id == null;
    }
}

Truck.java

package com.example.dao.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Persistable;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;

import java.util.UUID;

@Table("truck")
public class Truck implements Persistable<UUID> {

    @Id
    private UUID id;

    String brand;

    String model;

    Integer cargoMaxWeight;

    String truckType;

    @Column("engine_id")
    Engine engine;

    public void setId(UUID id) {
        this.id = id;
    }

    @Override
    public UUID getId() {
        return id;
    }

    @Override
    public boolean isNew() {
        return id == null;
    }
}

Ответы [ 2 ]

0 голосов
/ 05 декабря 2018

Я вижу четыре варианта для моделирования этого на Java.Обратите внимание, что большинство из них требуют настройки схемы базы данных.

Общая проблема заключается в том, что Spring Data JDBC предполагает, что у ссылочной сущности (Engine) в таблице есть столбец, который ссылается на сущность-владельца (Car)./ Vehicle).Для этого существует проблема: https://jira.spring.io/browse/DATAJDBC-128 Начиная с этого, у вас есть следующие опции:

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

    public class Car {
    
        @Id
        Long id;
        String name;
    
        Engine engine;
    }
    
    public class Truck {
    
        @Id
        Long id;
        String name;
    
        Engine engine;
    }
    
    public class Engine {
    
        String name;
    }
    
    CREATE TABLE CAR (
      id   BIGINT IDENTITY,
      NAME VARCHAR(200)
    );
    
    CREATE TABLE TRUCK (
      ID   BIGINT IDENTITY,
      NAME VARCHAR(200)
    );
    
    CREATE TABLE ENGINE (
      TRUCK BIGINT,
      CAR   BIGINT,
      NAME  VARCHAR(200),
      FOREIGN KEY (TRUCK) REFERENCES TRUCK (ID),
      FOREIGN KEY (CAR) REFERENCES CAR (ID)
    );
    

    Я предоставил полный пример на GitHub: https://github.com/schauder/so-sd-jdbc-multipleonetoone.

  2. Если вам не нравятся эти двастолбцы вы можете изменить отображение, чтобы использовать один и тот же столбец для обеих ссылок.Но тогда вы должны убедиться, что идентификаторы Car и Vehicle различны.Даже тогда с этим подходом возникает большая проблема:

    deleteAll в хранилище Car или на транспортном средстве Truck удалит ВСЕ двигатели !!! Поэтому такой подход не рекомендуется!

    Если вы все еще хотите использовать его, вот код для схемы и сущностей.

    public class Car {
    
        @Id
        Long id;
        String name;
    
        @Column(value = "vehicle")
        Engine engine;
    }
    
    public class Truck {
    
        @Id
        Long id;
        String name;
    
        @Column(value = "vehicle")
        Engine engine;
    }
    
    public class Engine {
    
        String name;
    }
    
    
    CREATE TABLE CAR (
      id   BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY ,
      NAME VARCHAR(200)
    );
    
    CREATE TABLE TRUCK (
      ID   BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH -1, INCREMENT BY -1) PRIMARY KEY ,
      NAME VARCHAR(200)
    );
    
    CREATE TABLE ENGINE (
      VEHICLE   BIGINT,
      NAME  VARCHAR(200),
    );
    

    И полный пример этого коммита:https://github.com/schauder/so-sd-jdbc-multipleonetoone/tree/5570979ef85e30fe7a17a8ce48d867fdb79e212a.

  3. Имеют два отдельных Engine класса и таблицы.Один для Car с и один для Truck с.

  4. Если вы не хотите или не можете изменить схему базы данных, вы можете рассмотреть Engine, Carи Truck три отдельных агрегата.у вас будет Long engineId в Car и Truck.Затем можно выполнить каскадное удаление с помощью прослушивателя событий для AfterDeleteEvent.

0 голосов
/ 04 декабря 2018

Мне удалось найти решение, и проблема заключалась в том, что я не мог разобраться с ссылками на классы Car и Truck из Engine, когда в базе данных модель отличалась.

Автомобиль.java

package com.backend.dao.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Persistable;

import java.util.Objects;
import java.util.UUID;

public class Car implements Persistable<UUID> {

    @Id
    private UUID id;

    private String brand;

    private String model;

    public void setId(UUID id) {
        this.id = id;
    }

    @Override
    public UUID getId() {
        return id;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getModel() {
        return model;
    }

    public void setModel(String model) {
        this.model = model;
    }

    @Override
    public boolean isNew() {
        return id == null;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Car)) {
            return false;
        }
        Car car = (Car) o;
        return Objects.equals(id, car.id) &&
            Objects.equals(brand, car.brand) &&
            Objects.equals(model, car.model);
    }

    @Override
    public int hashCode() {

        return Objects.hash(id, brand, model);
    }
}

Truck.java

package com.backend.dao.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Persistable;
import org.springframework.data.relational.core.mapping.Table;

import java.util.Objects;
import java.util.UUID;

@Table("truck")
public class Truck implements Persistable<UUID> {

    @Id
    private UUID id;

    private String brand;

    private String model;

    private Integer cargoMaxWeight;

    private String truckType;

    public void setId(UUID id) {
        this.id = id;
    }

    @Override
    public UUID getId() {
        return id;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getModel() {
        return model;
    }

    public void setModel(String model) {
        this.model = model;
    }

    public Integer getCargoMaxWeight() {
        return cargoMaxWeight;
    }

    public void setCargoMaxWeight(Integer cargoMaxWeight) {
        this.cargoMaxWeight = cargoMaxWeight;
    }

    public String getTruckType() {
        return truckType;
    }

    public void setTruckType(String truckType) {
        this.truckType = truckType;
    }

    @Override
    public boolean isNew() {
        return id == null;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Truck)) {
            return false;
        }
        Truck truck = (Truck) o;
        return Objects.equals(id, truck.id) &&
            Objects.equals(brand, truck.brand) &&
            Objects.equals(model, truck.model) &&
            Objects.equals(cargoMaxWeight, truck.cargoMaxWeight) &&
            Objects.equals(truckType, truck.truckType);
    }

    @Override
    public int hashCode() {

        return Objects.hash(id, brand, model, cargoMaxWeight, truckType);
    }
}

Engine.java

package com.backend.dao.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Persistable;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;

import java.time.LocalDateTime;
import java.util.Objects;
import java.util.UUID;

@Table("engine")
public class Engine implements Persistable<UUID> {

    @Id
    private UUID id;

    private String name;

    private LocalDateTime dateCreated;

    private String type;

    @Column("engine_id")
    private Car car;

    @Column("engine_id")
    private Truck truck;

    public void setId(UUID id) {
        this.id = id;
    }

    @Override
    public UUID getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public LocalDateTime getDateCreated() {
        return dateCreated;
    }

    public void setDateCreated(LocalDateTime dateCreated) {
        this.dateCreated = dateCreated;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    @Override
    public boolean isNew() {
        return id == null;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Engine)) {
            return false;
        }
        Engine engine = (Engine) o;
        return Objects.equals(id, engine.id) &&
            Objects.equals(name, engine.name) &&
            Objects.equals(dateCreated, engine.dateCreated) &&
            Objects.equals(type, engine.type);
    }

    @Override
    public int hashCode() {

        return Objects.hash(id, name, dateCreated, type);
    }

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    public Truck getTruck() {
        return truck;
    }

    public void setTruck(Truck truck) {
        this.truck = truck;
    }
}

Однако это решение не соответствует требованию - операции CRUD выполняются на Engine.java отражены Car.java и Truck.java.И я бы хотел, чтобы операции CRUD, выполняемые на Car.java и Truck.java, отражались на Engine.java.

Если у кого-то есть лучшее решение, напишите, пожалуйста.

...