Spring Data JDBC 和 R2DBC 4.0 將支援複合 ID

工程 | Jens Schauder | 2025年7月22日 | ...

我很高興地宣佈,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 建立工單。

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有