如何用 Spring 数据 JDBC 建模一对一关系?
How to model One-To-One relationship with Spring Data JDBC?
我想使用 Spring 数据 JDBC 和 PostgreSQL 建立一对一关系模型,但我无法正确设置根聚合。
有以下场景:Picture, SQL
每个引擎都是唯一的,car
有唯一的列 engine_id
,它是 engine.id
的外键,truck
也是如此。因此 car 和 truck 应该是根聚合,所以当 car 或 truck 被删除时,引擎 table 中的引用行也应该被删除。
根据我的理解 Spring Data JDBC Aggregates
If multiple aggregates reference the same entity, that entity can’t be part of those aggregates referencing it since it only can be part of exactly one aggregate.
所以问题是:
- 由于上面的解释,是否可以解决方法,以便通过对
car
和 truck
执行 CRUD 操作,更改也会反映到 engine
?
- 使用 Spring 数据 JDBC 在 java 中实现这种关系的最佳方式是什么?
这是我的看法,虽然行不通,但应该可以阐明我要完成的工作。
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;
}
}
设法找到解决方案,问题是在数据库中时,我无法从 Engine
中引用 Car
和 Truck
类型号不同
Car.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;
}
}
但是此解决方案不符合要求 - 在 Engine.java
上执行的 CRUD 操作反映在 Car.java
和 Truck.java
上。我想实现在 Car.java
和 Truck.java
上执行的 CRUD 操作反映在 Engine.java
上。
如果有人有更好的解决方案,请post。
我在 Java 中看到了四个对此进行建模的选项。
请注意,其中大多数需要调整您的数据库架构。
一般问题是 Spring 数据 JDBC 假定引用的实体 (Engine
) 在其 table 中有一列引用拥有实体 (Car
/Vehicle
)。
这有一个问题:https://jira.spring.io/browse/DATAJDBC-128
从这里开始,您有以下选择:
添加到引擎的列 table 产生如下所示的实体和模式(所有实体都减少到与问题相关的最小值):
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。
如果您不喜欢这两个列,您可以修改映射以对两个引用使用相同的列。
但是你必须确保 Car
和 Vehicle
的 id 是不同的。
即使那样,这种方法也存在一个大问题:
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),
);
有两个独立的 Engine
类 和 table。
一个用于 Car
s,一个用于 Truck
s.
如果您不想或不能更改数据库架构,您可以考虑 Engine
、Car
和 Truck
三个独立的聚合。
你会在 Car
和 Truck
中有一个 Long engineId
。
然后可以使用 event listener for AfterDeleteEvent
.
完成级联删除
我想使用 Spring 数据 JDBC 和 PostgreSQL 建立一对一关系模型,但我无法正确设置根聚合。
有以下场景:Picture, SQL
每个引擎都是唯一的,car
有唯一的列 engine_id
,它是 engine.id
的外键,truck
也是如此。因此 car 和 truck 应该是根聚合,所以当 car 或 truck 被删除时,引擎 table 中的引用行也应该被删除。
根据我的理解 Spring Data JDBC Aggregates
If multiple aggregates reference the same entity, that entity can’t be part of those aggregates referencing it since it only can be part of exactly one aggregate.
所以问题是:
- 由于上面的解释,是否可以解决方法,以便通过对
car
和truck
执行 CRUD 操作,更改也会反映到engine
? - 使用 Spring 数据 JDBC 在 java 中实现这种关系的最佳方式是什么?
这是我的看法,虽然行不通,但应该可以阐明我要完成的工作。
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;
}
}
设法找到解决方案,问题是在数据库中时,我无法从 Engine
中引用 Car
和 Truck
类型号不同
Car.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;
}
}
但是此解决方案不符合要求 - 在 Engine.java
上执行的 CRUD 操作反映在 Car.java
和 Truck.java
上。我想实现在 Car.java
和 Truck.java
上执行的 CRUD 操作反映在 Engine.java
上。
如果有人有更好的解决方案,请post。
我在 Java 中看到了四个对此进行建模的选项。 请注意,其中大多数需要调整您的数据库架构。
一般问题是 Spring 数据 JDBC 假定引用的实体 (Engine
) 在其 table 中有一列引用拥有实体 (Car
/Vehicle
)。
这有一个问题:https://jira.spring.io/browse/DATAJDBC-128
从这里开始,您有以下选择:
添加到引擎的列 table 产生如下所示的实体和模式(所有实体都减少到与问题相关的最小值):
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。
如果您不喜欢这两个列,您可以修改映射以对两个引用使用相同的列。 但是你必须确保
Car
和Vehicle
的 id 是不同的。 即使那样,这种方法也存在一个大问题: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), );
有两个独立的
Engine
类 和 table。 一个用于Car
s,一个用于Truck
s.如果您不想或不能更改数据库架构,您可以考虑
Engine
、Car
和Truck
三个独立的聚合。 你会在Car
和Truck
中有一个Long engineId
。 然后可以使用 event listener forAfterDeleteEvent
. 完成级联删除