手动将模型映射到数据库 - 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,其中包含约会列表。 AppointmentStudent 的子实体。

现在让我们说,无论出于何种原因,域对象 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,因为它不是聚合根。在我的例子中,AppointmentStudent 具有相同的生命周期,例如,如果 Student 注销,则会被丢弃。所以把它放在一个单独的聚合中是没有意义的。

所以我的主要问题是:如果您的域对象未 1:1 映射到数据库结构,那么持久化域对象(包括其子实体)的最佳方法是什么?

AppointmentID 是代理键。重申一下,代理键不是从应用程序数据中导出的代理键的唯一意义是作为主键。 Wikipedia

因此,我们可以在必要时为同一条约会记录丢弃并一次又一次地生成ID。

所以你有三个选项可供选择:

  1. 从 table 中删除所有约会,并在您坚持时使用新的代理 ID 重新填充。

  2. 将代理 ID 加载到域中并将其作为预约数据的一部分保存

  3. 构造一个哈希键来唯一地表示一个约会(从它的字段中)并将该键用作约会的唯一 ID

所有三种方法都是可接受的table,但您可以选择最适合您的用例的trade-off。例如:

  • 如果一个学生可以有超过 100 个约会,擦除数据并重新填充整个列表将是一种浪费。
  • 如果您不能为每个约会构造一个唯一的散列键,将代理键加载到域中会更容易,即使约会是 sub-entities。

以此类推