领先一步
VMware 提供培训和认证,助您加速进步。
了解更多我很高兴地宣布,Spring Data JDBC 和 R2DBC 从版本 4.0.0-M4 开始最终支持复合 ID。
你们中的大多数人可能知道,但为了确保每个人都有相同的理解:从数据库的角度来看,复合 ID(或复合键)是由多个列组成的主键。在 Java 端,这些列被映射到一个实体,每个列都有一个属性。用法应该很简单,我将在下面的文章中为 JDBC 进行演示。R2DBC 中的用法类似。
要开始使用,只需在您的聚合根中引用实体的字段上添加 @Id 注解,而不是简单的类型。
class Employee {
@Id
EmployeeId id;
String name;
// ...
}
record EmployeeId(
Organization organization,
Long employeeNumber) {
}
enum Organization {
RND,
SALES,
MARKETING,
PURCHASING
}
该引用会自动成为嵌入式引用,即实体的字段会成为聚合根表的列。
create table employee
(
organization varchar(20),
employee_number int,
name varchar(100)
);
如果您想调整名称,可以添加 @Embedded 注解,它允许您提供一个前缀。当加载实体时所有值都为 null 时,指定应该发生什么看起来有点奇怪。但是使用 Embedded,您必须这样做,尽管为 null 的主键会在所有地方引起问题并且根本无法工作。
class Employee {
@Id
@Embedded.Nullable(prefix = "id_")
EmployeeId id;
String name;
// ...
}
create table employee
(
id_organization varchar(20),
id_employee_number int,
name varchar(100)
);
与普通 ID 一样,Spring Data Relational 使用 ID 值作为新实体的指示:如果 ID 值为 null,则该实体被视为新实体,将执行插入操作。如果 ID 值不为 null,则需要进行更新。
当保存具有复合 ID 的新实体时,您现在面临一个小问题:复合 ID 无法通过自增列轻松生成,因为它根据定义由多列组成。处理此问题的一种方法是使用 BeforeConvertCallback。
@Bean
BeforeConvertCallback<Employee> idGeneration() {
return new BeforeConvertCallback<>() {
AtomicLong counter = new AtomicLong();
@Override
public Employee onBeforeConvert(Employee employee) {
if (employee.id == null) {
employee.id = new EmployeeId(Organization.RND, counter.addAndGet(1));
}
return employee;
}
};
}
repository.save(new Employee("Mark Paluch"));
在大多数具有复合 ID 的情况下,预先设置 ID 并使用乐观锁(即 null 版本字段将标记实体为新实体)或显式调用 JdbcAggregateTemplate.insert 可能会更容易。
interface EmployeeRepository extends Repository<Employee, EmployeeId>, InsertRepository<Employee> {
Employee findById(EmployeeId id);
Employee save(Employee employee);
}
interface InsertRepository<E> {
E insert(E employee);
}
class InsertRepositoryImpl<E> implements InsertRepository<E> {
@Autowired
private JdbcAggregateTemplate template;
@Override
public E insert(E employee) {
return template.insert(employee);
}
}
@Autowired
EmployeeRepository repository;
// ...
repository.insert(new Employee(new EmployeeId(Organization.RND, 23L), "Jens Schauder"));
我希望您发现 Spring Data Relational 的这一新增功能很有用。本文中使用的示例的完整代码可以在 https://github.com/spring-projects/spring-data-examples/tree/main/jdbc/composite-ids 找到。
如果您发现错误或有改进建议,请在 https://github.com/spring-projects/spring-data-relational/issues 创建工单。