資料庫與物件導向的不匹配阻抗 (一) - 什麼是不匹配阻抗?

我們可以從維基百科看定義:

Object–relational impedance mismatch is a set of difficulties going between data in relational data stores and data in domain-driven object models.

翻譯蒟蒻:

物件關聯阻抗不匹配是一組在關聯式資料庫中的資料與領域驅動物件模型中的資料之間轉換時遇到的困難。。

不匹配阻抗 (Impedance Mismatch) 是什麼?為何資料轉換這麼困難?

在物件導向中,我們通常會用物件來表示現實世界中的事物,並且這些物件之間存在關聯。

UML 圖

例如,一個團隊(Team)可以包含多位成員(TeamMember)。用 UML 圖來表示,會是以下的類別圖:

ER 圖

然而,當我們設計資料庫時,通常會用關聯式數據模型來表示這些關係。這時候,資料庫中的表結構可能會是這樣的:

資料轉換困難及複雜

在這個例子中,物件導向模型中是 Team 類別持有 TeamMember 的資訊,而在資料庫模型中,則是 TeamMember 持有 TeamId 的資訊。這種結構上的差異會導致在轉換資料時需要額外的處理,這種情況被稱為 不匹配阻抗 (Impedance Mismatch)。

此外,程式語言中的資料型態和資料庫中的欄位型態也經常不一致。例如,C# 中的 DateTime 型態在 SQL 資料庫中可能對應到 datetime 或 timestamp 欄位,這些型態的差異也會增加轉換的困難。

那跟日常開發又有什麼關係 ?

在日常開發上就必須額外處理這一段,這邊使用上面類別圖及簡單範例去處理物件關聯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
using (var connection = new SqlConnection("Your Connection String"))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
try
{
var team = new Team
{
Name = "Development Team",
TeamMembers = new List<TeamMember>
{
new() { Name = "Alice" },
new() { Name = "Bob" }
}
};

var teamInsertQuery = "INSERT INTO Team (Name) OUTPUT INSERTED.Id VALUES (@Name);";
int teamId = connection.QuerySingle<int>(teamInsertQuery, team, transaction: transaction);

foreach (var teamMember in team.TeamMembers)
{
var teamMemberInsertQuery = "INSERT INTO TeamMember (Name, TeamId) VALUES (@Name, @TeamId);";
connection.Execute(teamMemberInsertQuery, new { teamMember.Name, TeamId = teamId }, transaction: transaction);
}
transaction.Commit();

}
catch
{
transaction.Rollback();
}
}
connection.Close();
}

結語

當我們將物件導向的資料寫入資料庫時,必須先將 Team 的資訊寫入,並取得其 Id,然後才能將 TeamMember 的資訊寫入資料庫。這個過程中,由於物件導向與資料庫的結構不一致,我們需要額外的步驟來進行轉換,這就是所謂的不匹配阻抗。

在日常開發中,處理這些不匹配阻抗常常會占用很長的時間。開發者需要花費大量時間和精力來管理這些轉換,確保資料的正確性和一致性。

參考資料來源

Wikipedia 維基百科

範例程式碼

您可以在以下 GitHub 中查看這個範例的完整程式碼:

ImpedanceMismatchSample