手动将模型映射到数据库 - Spring 数据 JDBC
Manually mapping model to database - Spring Data JDBC
我有以下(大大简化的)域对象
public class Student {
private Long studentId;
private List<Appointment> appointments;
// Business logic
}
public class Appointment {
private TimeRange timeRange;
private LocalDate date;
// Business logic
}
聚合根是 Student
,其中包含约会列表。 Appointment
是 Student
的子实体。
现在让我们说,无论出于何种原因,域对象 Student
都没有完美地映射到我的数据库模型。例如,为了更好地执行业务逻辑,从数据库构建的实体进行了一些转换。需要一个这样的转换,因为我在我的 Appointment
class 中有一个自定义 TimeRange
class,它不能被数据 JDBC.
自动映射
因此我想介绍一个间接寻址,它将被 Spring Data JDBC:
使用
@Table("student")
public class StudentEntity {
@Id
private Long studentId;
@MappedCollection(idColumn = "student_id")
private Set<AppointmentEntity> appointments;
public StudentEntity(Long studentId, Set<AppointmentEntity> appointments) {
this.studentId = studentId;
this.appointments = appointments;
}
}
@Table("appointment")
public class AppointmentEntity {
@Id
private Long appointmentId;
private LocalTime rangeStart;
private LocalTime rangeEnd;
private LocalDate date;
}
在我的存储库实现中,我执行以下操作
@Repository
public class StudentRepositoryImpl implement StudentRepository {
private final StudenDao studentDao;
public StudentRepositoryImpl(StudentDao studentDao) {
this.studentDao = studentDao;
}
public Student findStudent(Long id) {
Optional<StudentEntity> studentEntity = studentDao.findStudentEntityById(id);
return studentEntity.map(this::toStudent).orElse(null);
}
public void saveStudent(Student student) {
// ???
}
private toAppointment(AppointmentEntity appointmentEntity) {
TimeRange timeRange = new TimeRange(appointmentEntity.rangeStart, appointmentEntity.rangeEnd);
return new Appointment(timeRange, appointmentEntity.getDate());
}
private toStudent(StudentEntity studentEntity) {
List<Appointment> appointments = studentEntity.appointments.map(this::toAppointment);
return new Student(studentEntity.getStudentId(), appointments);
}
}
来自 Database -> Entity -> Domain
的流程工作正常,但另一个方向呢?假设我执行了一些操作,并且 Student
域对象的 appointments
字段发生了变化。我想再存入数据库
我必须将 Student
转换为 StudentEntity
,因此也必须将 Appointment
转换为 AppointmentEntity
。但是 Appointment
在域上下文中没有 ID,因为它不是聚合根。在我的例子中,Appointment
与 Student
具有相同的生命周期,例如,如果 Student
注销,则会被丢弃。所以把它放在一个单独的聚合中是没有意义的。
所以我的主要问题是:如果您的域对象未 1:1 映射到数据库结构,那么持久化域对象(包括其子实体)的最佳方法是什么?
AppointmentID
是代理键。重申一下,代理键不是从应用程序数据中导出的和代理键的唯一意义是作为主键。 Wikipedia
因此,我们可以在必要时为同一条约会记录丢弃并一次又一次地生成ID。
所以你有三个选项可供选择:
从 table 中删除所有约会,并在您坚持时使用新的代理 ID 重新填充。
将代理 ID 加载到域中并将其作为预约数据的一部分保存
构造一个哈希键来唯一地表示一个约会(从它的字段中)并将该键用作约会的唯一 ID
所有三种方法都是可接受的table,但您可以选择最适合您的用例的trade-off。例如:
- 如果一个学生可以有超过 100 个约会,擦除数据并重新填充整个列表将是一种浪费。
- 如果您不能为每个约会构造一个唯一的散列键,将代理键加载到域中会更容易,即使约会是 sub-entities。
以此类推
我有以下(大大简化的)域对象
public class Student {
private Long studentId;
private List<Appointment> appointments;
// Business logic
}
public class Appointment {
private TimeRange timeRange;
private LocalDate date;
// Business logic
}
聚合根是 Student
,其中包含约会列表。 Appointment
是 Student
的子实体。
现在让我们说,无论出于何种原因,域对象 Student
都没有完美地映射到我的数据库模型。例如,为了更好地执行业务逻辑,从数据库构建的实体进行了一些转换。需要一个这样的转换,因为我在我的 Appointment
class 中有一个自定义 TimeRange
class,它不能被数据 JDBC.
因此我想介绍一个间接寻址,它将被 Spring Data JDBC:
使用@Table("student")
public class StudentEntity {
@Id
private Long studentId;
@MappedCollection(idColumn = "student_id")
private Set<AppointmentEntity> appointments;
public StudentEntity(Long studentId, Set<AppointmentEntity> appointments) {
this.studentId = studentId;
this.appointments = appointments;
}
}
@Table("appointment")
public class AppointmentEntity {
@Id
private Long appointmentId;
private LocalTime rangeStart;
private LocalTime rangeEnd;
private LocalDate date;
}
在我的存储库实现中,我执行以下操作
@Repository
public class StudentRepositoryImpl implement StudentRepository {
private final StudenDao studentDao;
public StudentRepositoryImpl(StudentDao studentDao) {
this.studentDao = studentDao;
}
public Student findStudent(Long id) {
Optional<StudentEntity> studentEntity = studentDao.findStudentEntityById(id);
return studentEntity.map(this::toStudent).orElse(null);
}
public void saveStudent(Student student) {
// ???
}
private toAppointment(AppointmentEntity appointmentEntity) {
TimeRange timeRange = new TimeRange(appointmentEntity.rangeStart, appointmentEntity.rangeEnd);
return new Appointment(timeRange, appointmentEntity.getDate());
}
private toStudent(StudentEntity studentEntity) {
List<Appointment> appointments = studentEntity.appointments.map(this::toAppointment);
return new Student(studentEntity.getStudentId(), appointments);
}
}
来自 Database -> Entity -> Domain
的流程工作正常,但另一个方向呢?假设我执行了一些操作,并且 Student
域对象的 appointments
字段发生了变化。我想再存入数据库
我必须将 Student
转换为 StudentEntity
,因此也必须将 Appointment
转换为 AppointmentEntity
。但是 Appointment
在域上下文中没有 ID,因为它不是聚合根。在我的例子中,Appointment
与 Student
具有相同的生命周期,例如,如果 Student
注销,则会被丢弃。所以把它放在一个单独的聚合中是没有意义的。
所以我的主要问题是:如果您的域对象未 1:1 映射到数据库结构,那么持久化域对象(包括其子实体)的最佳方法是什么?
AppointmentID
是代理键。重申一下,代理键不是从应用程序数据中导出的和代理键的唯一意义是作为主键。 Wikipedia
因此,我们可以在必要时为同一条约会记录丢弃并一次又一次地生成ID。
所以你有三个选项可供选择:
从 table 中删除所有约会,并在您坚持时使用新的代理 ID 重新填充。
将代理 ID 加载到域中并将其作为预约数据的一部分保存
构造一个哈希键来唯一地表示一个约会(从它的字段中)并将该键用作约会的唯一 ID
所有三种方法都是可接受的table,但您可以选择最适合您的用例的trade-off。例如:
- 如果一个学生可以有超过 100 个约会,擦除数据并重新填充整个列表将是一种浪费。
- 如果您不能为每个约会构造一个唯一的散列键,将代理键加载到域中会更容易,即使约会是 sub-entities。
以此类推