簡體   English   中英

哪個ORM框架可以最好地處理MVCC數據庫設計?

[英]Which ORM framework can best handle an MVCC database design?

在設計數據庫以使用MVCC(多版本並發控制)時,您可以使用類似“IsLatest”的布爾字段或整數“VersionId”創建表,並且您永遠不會進行任何更新,只會在事情發生變化時插入新記錄。

MVCC為需要詳細歷史記錄的應用程序提供自動審計,並且還減輕了數據庫對更新鎖的壓力。 缺點是,由於獲得最新版本所需的額外條款,它會使您的數據大小更大並減慢選擇速度。 它還使外鍵更復雜。

(請注意,我不是在討論SQL Server的快照隔離級別等RDBMS中的本機MVCC支持)

這已在Stack Overflow上的其他帖子中討論過。 [todo - 鏈接]

我想知道,哪些流行的實體/ ORM框架(Linq to Sql,ADO.NET EF,Hibernate等)可以干凈地支持這種類型的設計? 這是對典型ActiveRecord設計模式的重大改變,因此我不確定那里的大多數工具是否可以幫助那些決定使用他們的數據模型走這條路線的人。 我對如何處理外鍵特別感興趣,因為我甚至不確定將數據建模為支持MVCC的最佳方法。

我可能會考慮純粹在DB中實現MVCC層,使用存儲過程和視圖來處理我的數據操作。 然后,您可以向任何能夠映射到存儲過程和從存儲過程映射的ORM提供合理的API,並且您可以讓DB處理數據完整性問題(因為它幾乎是為此構建的)。 如果你這樣做,你可能想看一個更純粹的映射解決方案,如IBatis或IBatis.net。

我設計了一個類似的數據庫(只有INSERT - 沒有UPDATE,沒有DELETE)。

幾乎所有的SELECT查詢都只針對每個表的當前行的視圖(最高版本號)。

觀點看起來像這樣......

SELECT
    dbo.tblBook.BookId,
    dbo.tblBook.RevisionId,
    dbo.tblBook.Title,
    dbo.tblBook.AuthorId,
    dbo.tblBook.Price,
    dbo.tblBook.Deleted
FROM
    dbo.tblBook INNER JOIN
    (
        SELECT
            BookId,
            MAX(RevisionId) AS RevisionId
        FROM
            dbo.tblBook
        GROUP BY
            BookId
    ) AS CurrentBookRevision ON
    dbo.tblBook.BookId = CurrentBookRevision.BookId AND
    dbo.tblBook.RevisionId = CurrentBookRevision.RevisionId
WHERE
    dbo.tblBook.Deleted = 0

我的插入(以及更新和刪除)都由存儲過程處理(每個表一個)。

存儲過程看起來像這樣......

ALTER procedure [dbo].[sp_Book_CreateUpdateDelete]
    @BookId      uniqueidentifier,
    @RevisionId  bigint,
    @Title       varchar(256),
    @AuthorId    uniqueidentifier,
    @Price       smallmoney,
    @Deleted     bit
as
    insert into tblBook
        (
            BookId,
            RevisionId,
            Title,
            AuthorId,
            Price,
            Deleted
        )
    values
        (
            @BookId,
            @RevisionId,
            @Title,
            @AuthorId,
            @Price,
            @Deleted
        )

修訂號在Visual Basic代碼中按事務處理...

Shared Sub Save(ByVal UserId As Guid, ByVal Explanation As String, ByVal Commands As Collections.Generic.Queue(Of SqlCommand))
    Dim Connection As SqlConnection = New SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings("Connection").ConnectionString)
    Connection.Open()
    Dim Transaction As SqlTransaction = Connection.BeginTransaction
    Try
        Dim RevisionId As Integer = Nothing
        Dim RevisionCommand As SqlCommand = New SqlCommand("sp_Revision_Create", Connection)
        RevisionCommand.CommandType = CommandType.StoredProcedure
        RevisionCommand.Parameters.AddWithValue("@RevisionId", 0)
        RevisionCommand.Parameters(0).SqlDbType = SqlDbType.BigInt
        RevisionCommand.Parameters(0).Direction = ParameterDirection.Output
        RevisionCommand.Parameters.AddWithValue("@UserId", UserId)
        RevisionCommand.Parameters.AddWithValue("@Explanation", Explanation)
        RevisionCommand.Transaction = Transaction
        LogDatabaseActivity(RevisionCommand)
        If RevisionCommand.ExecuteNonQuery() = 1 Then 'rows inserted
            RevisionId = CInt(RevisionCommand.Parameters(0).Value) 'generated key
        Else
            Throw New Exception("Zero rows affected.")
        End If
        For Each Command As SqlCommand In Commands
            Command.Connection = Connection
            Command.Transaction = Transaction
            Command.CommandType = CommandType.StoredProcedure
            Command.Parameters.AddWithValue("@RevisionId", RevisionId)
            LogDatabaseActivity(Command)
            If Command.ExecuteNonQuery() < 1 Then 'rows inserted
                Throw New Exception("Zero rows affected.")
            End If
        Next
        Transaction.Commit()
    Catch ex As Exception
        Transaction.Rollback()
        Throw New Exception("Rolled back transaction", ex)
    Finally
        Connection.Close()
    End Try
End Sub

我為每個表創建了一個對象,每個對象都有構造函數,實例屬性和方法,create-update-delete命令,一堆finder函數和IComparable排序函數。 這是一個龐大的代碼量。

VB對象的一對一數據庫表...

Public Class Book
    Implements iComparable

#Region " Constructors "

    Private _BookId As Guid
    Private _RevisionId As Integer
    Private _Title As String
    Private _AuthorId As Guid
    Private _Price As Decimal
    Private _Deleted As Boolean

    ...

    Sub New(ByVal BookRow As DataRow)
        Try
            _BookId = New Guid(BookRow("BookId").ToString)
            _RevisionId = CInt(BookRow("RevisionId"))
            _Title = CStr(BookRow("Title"))
            _AuthorId = New Guid(BookRow("AuthorId").ToString)
            _Price = CDec(BookRow("Price"))
        Catch ex As Exception
            'TO DO: log exception
            Throw New Exception("DataRow does not contain valid Book data.", ex)
        End Try
    End Sub

#End Region

...

#Region " Create, Update & Delete "

    Function Save() As SqlCommand
        If _BookId = Guid.Empty Then
            _BookId = Guid.NewGuid()
        End If
        Dim Command As SqlCommand = New SqlCommand("sp_Book_CreateUpdateDelete")
        Command.Parameters.AddWithValue("@BookId", _BookId)
        Command.Parameters.AddWithValue("@Title", _Title)
        Command.Parameters.AddWithValue("@AuthorId", _AuthorId)
        Command.Parameters.AddWithValue("@Price", _Price)
        Command.Parameters.AddWithValue("@Deleted", _Deleted)
        Return Command
    End Function

    Shared Function Delete(ByVal BookId As Guid) As SqlCommand
        Dim Doomed As Book = FindByBookId(BookId)
        Doomed.Deleted = True
        Return Doomed.Save()
    End Function

    ...

#End Region

...

#Region " Finders "

    Shared Function FindByBookId(ByVal BookId As Guid, Optional ByVal TryDeleted As Boolean = False) As Book
        Dim Command As SqlCommand
        If TryDeleted Then
            Command = New SqlCommand("sp_Book_FindByBookIdTryDeleted")
        Else
            Command = New SqlCommand("sp_Book_FindByBookId")
        End If
        Command.Parameters.AddWithValue("@BookId", BookId)
        If Database.Find(Command).Rows.Count > 0 Then
            Return New Book(Database.Find(Command).Rows(0))
        Else
            Return Nothing
        End If
    End Function

這樣的系統保留了每行的所有過去版本,但是管理起來真的很痛苦。

優點:

  • 保留了總歷史
  • 存儲過程較少

缺點:

  • 依賴於非數據庫應用程序來實現數據完整性
  • 要編寫的大量代碼
  • 數據庫中沒有管理外鍵(再見自動Linq-to-SQL風格的對象生成)
  • 我還沒有找到一個好的用戶界面來檢索所有保留過去的版本。

結論:

  • 如果沒有一些易於使用的開箱即用的ORM解決方案,我不會在新項目上遇到麻煩。

我很好奇微軟實體框架能否很好地處理這樣的數據庫設計。

Jeff和Stack Overflow團隊的其他成員必須在開發Stack Overflow時處理類似的問題:已編輯的問題和答案的過去修訂版本已保存並可檢索。

我相信傑夫已經聲明他的團隊使用Linq來SQL和MS SQL Server。

我想知道他們是如何處理這些問題的

查看Envers項目 - 適用於JPA / Hibernate應用程序並且基本上可以幫助您 - 在另一個表中跟蹤每個實體的不同版本,並為您提供類似SVN的可能性(“Gimme使用的人員版本2008-11 -05 ...“)

http://www.jboss.org/envers/

/延

據我所知,ORM框架將要為您生成CRUD代碼,因此必須明確設計它們以實現MVCC選項; 我不知道任何這樣做是開箱即用的。

從實體框架的角度來看,CSLA根本不為您實現持久性 - 它只是定義了一個“數據適配器”接口,您可以使用它來實現所需的持久性。 因此,您可以設置代碼生成(CodeSmith等)模板,以便為與MVCC數據庫體系結構一起使用的CSLA實體自動生成CRUD邏輯。

這種方法適用於任何實體框架,很可能不僅僅是CSLA,但它在CSLA中將是一個非常“干凈”的實現。

我們所做的只是使用普通的ORM(休眠)並使用views +而不是觸發器來處理MVCC。

因此,有一個v_emp視圖,它看起來像一個普通的表,你可以很好地插入和更新它,但是當你這樣做時,觸發器處理實際上將正確的數據插入到基表中。

不..我討厭這個方法:)我會使用Tim建議的存儲過程API。

我總是認為你在更新和刪除時使用db觸發器將這些行推送到TableName_Audit表中。

這與ORM一起使用,為您提供歷史記錄,並且不會在該表上消除選擇性能。 這是個好主意還是我錯過了什么?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM