ClownFish:比寫程式碼還快的通用資料訪問層
閱讀目錄
開始
ClownFish是什麼?
比手寫程式碼還快的執行速度
簡單,一個呼叫完成你要的全部功能
方便,你需要的程式碼已經準備好了
定義資料實體型別不再是費力的體力勞動
通用,可以非常簡單地實現對多種資料庫的支援
靈活,SQL語句放在哪裡隨便你
XmlCommand是什麼?
可監控,圖形的工具會告訴你每個資料訪問的細節
關於示例程式碼
最近花了二個月的業餘時間重寫了我以前的通用資料訪問層,由於是重寫,所以我給這個專案取了個新名字:ClownFish
ClownFish是什麼?
ClownFish 是我編寫的一個通用資料訪問層,設計它的目的是為了:
1. 方便在 .net 專案中執行資料訪問任務。
2. 避免直接使用ADO.NET帶來的一大堆高度類似的繁瑣程式碼。
3. 提供出色的效能滿足實際專案需要。
ClownFish 具有以下一些技術特色:
1. 高效能:比手寫程式碼還快的執行速度。
2. 簡單:執行查詢、將查詢結果轉成實體列表、獲取輸出引數。 一個呼叫完成三個步驟。
3. 方便:提供專用的程式碼生成器,直接生成呼叫程式碼或者實體型別定義程式碼。
4. 通用:可以非常簡單地實現對多種資料庫的支援。
5. 靈活:支援儲存過程,引數化SQL,或者將SQL語句儲存在XML配置檔案中。
6. 可監控:提供一個Profiler工具,讓您可以隨時瞭解詳細的資料庫訪問情況。
ClownFish不僅繼承了老版本的通用資料訪問層的全部優點,而且在高效能,方便性,靈活性,以及程式碼可讀性方面有了更出色的設計。
在最新的版本中,ClownFish不僅僅只是一個通用資料訪問層,還提供了:專用的程式碼生成器,XmlCommand管理工具,Profiler工具,它們都會為ClownFish提供更多功能。
ClownFish 是一個可免費的資料訪問元件,您可以把它應用在您的 .net 專案中,讓它簡化您的開發工作。
比手寫程式碼還快的執行速度
提高效能是建立ClownFish專案的重要原因之一,這次最佳化的主要目標是:比手寫程式碼還快的執行速度。
為了讓您對ClownFish的效能留有較深刻的印象,下面我將透過一個實際案例來測試它的速度。
首先,我定義了一個實體型別:
public class OrderInfo { public int OrderID { get; set; } public DateTime OrderDate { get; set; } public decimal SumMoney { get; set; } public string Comment { get; set; } public bool Finished { get; set; } public int ProductID { get; set; } public decimal UnitPrice { get; set; } public int Quantity { get; set; } public string ProductName { get; set; } public int CategoryID { get; set; } public string Unit { get; set; } public string Remark { get; set; } // 注意:客戶資訊有可能會是DBNull public int? CustomerID { get; set; } public string CustomerName { get; set; } public string ContactName { get; set; } public string Address { get; set; } public string PostalCode { get; set; } public string Tel { get; set; } }
定義這個實體型別,我關注的是它所包含的資料成員的數量,而不是那些資料成員的含義。
我認為這個型別的資料成員數量應該還是比較接近多數實際場景的。
下面再來看看如何從資料庫中載入它們:
public static class TestHelper { public static readonly string QueryText = @" select top (@TopN) d.OrderID, d.OrderDate, d.SumMoney, d.Comment, d.Finished, dt.ProductID, dt.UnitPrice, dt.Quantity, p.ProductName, p.CategoryID, p.Unit, p.Remark, c.CustomerID, c.CustomerName, c.ContactName, c.Address, c.PostalCode, c.Tel from Orders d inner join [Order Details] dt on d.OrderId = dt.OrderId inner join Products p on dt.ProductId = p.ProductId left join Customers c on d.CustomerId = c.CustomerId "; public static ListExecuteQuery(SqlConnection conn, UiParameters uiParam) { SqlCommand command = new SqlCommand(QueryText, conn); command.Parameters.Add("TopN", SqlDbType.Int).Value = uiParam.PageSize; List list = new List (uiParam.PageSize); using( SqlDataReader reader = command.ExecuteReader() ) { while( reader.Read() ) list.Add(LoadOrderInfo(reader)); } return list; } private static OrderInfo LoadOrderInfo(SqlDataReader reader) { OrderInfo info = new OrderInfo(); info.OrderID = (int)reader["OrderID"]; info.OrderDate = (DateTime)reader["OrderDate"]; info.SumMoney = (decimal)reader["SumMoney"]; info.Comment = (string)reader["Comment"]; info.Finished = (bool)reader["Finished"]; info.ProductID = (int)reader["ProductID"]; info.UnitPrice = (decimal)reader["UnitPrice"]; info.Quantity = (int)reader["Quantity"]; info.ProductName = (string)reader["ProductName"]; info.CategoryID = (int)reader["CategoryID"]; info.Unit = (string)reader["Unit"]; info.Remark = (string)reader["Remark"]; object customerId = reader["CustomerID"]; if( customerId != DBNull.Value ) { info.CustomerID = (int)customerId; info.CustomerName = (string)reader["CustomerName"]; info.ContactName = (string)reader["ContactName"]; info.Address = (string)reader["Address"]; info.PostalCode = (string)reader["PostalCode"]; info.Tel = (string)reader["Tel"]; } return info; }
程式碼中,首先定義了一個引數化的SQL查詢語句,然後就是純手工的ADO.NET程式碼,迴圈呼叫SqlDataReader.Read()方法,再從SqlDataReader中根據欄位名稱獲取資料寫入到實體物件的屬性中。
這段程式碼夠【手工化】吧? 由於這段程式碼沒用使用反射程式碼,所以如果想追求效能,我想大家都會這樣寫。
在我的測試程式中,上面那段程式碼被下面這段程式碼呼叫:
public interface IPerformanceTest : IDisposable { ListRun(); } [TestMethod("手工程式碼,SQLSERVER", 1)] public class Test_Adonet_ShareConnection : IPerformanceTest { private UiParameters uiParam; private SqlConnection conn; public Test_Adonet_ShareConnection(UiParameters param) { this.uiParam = param; this.conn = new SqlConnection(Program.ConnectionString); this.conn.Open(); } public List Run() { return TestHelper.ExecuteQuery(conn, uiParam); } public void Dispose() { conn.Dispose(); } }
測試程式碼應該沒有問題吧?
再來看一下使用ClownFish的等效測試程式碼:
[TestMethod("ClownFish,SQLSERVER", 2)] public class Test_ClownFish_ShareConnection : IPerformanceTest { private UiParameters uiParam; private ClownFish.DbContext db; public Test_ClownFish_ShareConnection(UiParameters param) { this.uiParam = param; this.db = new ClownFish.DbContext(false); } public ListRun() { var parameter = new { TopN = uiParam.PageSize }; return ClownFish.DbHelper.FillList ( TestHelper.QueryText, parameter, db, ClownFish.CommandKind.SqlTextWithParams); } public void Dispose() { db.Dispose(); } }
比較這二段程式碼,不難看出:使用 ClownFish 所需的程式碼量要 少很多。
如果我以下面的測試引數執行效能測試:
可以得到下面的測試結果:
圖形反映的結果很直觀:ClownFish 完成測試所需時間比手寫程式碼要略快點。
或許有些人認為:快這麼一點,意義不大!
但您想過沒有:ClownFish PK的物件是【手工版的專用程式碼】啊!
其實這個測試並沒有把 ClownFish 效能很好的體現出來,因為中間有SQLSERVER的執行時間,以及跨程式的呼叫開銷。這二個因素所花的成本影響了ClownFish的效能優勢。
下面,我又做了一組測試:直接從DataTable中載入資料。
我之所以選擇這個測試方法,是因為它也是一種常見的使用方案:
我們可以將原始匯出到XML檔案中,然後使用XML檔案做離線資料,這樣可以減少對資料庫的訪問壓力。
老版本的通用資料訪問層也一直支援這個功能,所以這次就選擇了這個測試方法。
說明:實際使用時,我會從XML讀出資料到DataTable,供後面使用(轉成實體型別只是其中的一種使用資料的方式)。
在測試之前,我們來看一下手工程式碼是什麼樣的:
public static class TestHelper { // 省略前面貼過的程式碼 public static ListLoadFromDataTable(DataTable table) { List list = new List (table.Rows.Count); foreach(DataRow dataRow in table.Rows) list.Add(LoadOrderInfo(dataRow)); return list; } private static OrderInfo LoadOrderInfo(DataRow dataRow) { OrderInfo info = new OrderInfo(); info.OrderID = (int)dataRow["OrderID"]; info.OrderDate = (DateTime)dataRow["OrderDate"]; info.SumMoney = (decimal)dataRow["SumMoney"]; info.Comment = (string)dataRow["Comment"]; info.Finished = (bool)dataRow["Finished"]; info.ProductID = (int)dataRow["ProductID"]; info.UnitPrice = (decimal)dataRow["UnitPrice"]; info.Quantity = (int)dataRow["Quantity"]; info.ProductName = (string)dataRow["ProductName"]; info.CategoryID = (int)dataRow["CategoryID"]; info.Unit = (string)dataRow["Unit"]; info.Remark = (string)dataRow["Remark"]; object customerId = dataRow["CustomerID"]; if( customerId != DBNull.Value ) { info.CustomerID = (int)customerId; info.CustomerName = (string)dataRow["CustomerName"]; info.ContactName = (string)dataRow["ContactName"]; info.Address = (string)dataRow["Address"]; info.PostalCode = (string)dataRow["PostalCode"]; info.Tel = (string)dataRow["Tel"]; } return info; } private static DataTable s_OrderInfoTable; public static DataTable GetOrderInfoTable() { // 把結果用靜態變數快取起來,避免影響測試時間 // 由於在執行測試前,會有一次單獨的呼叫,所以並沒有執行緒安全問題。 if( s_OrderInfoTable == null ) { s_OrderInfoTable = ClownFish.DbHelper.FillDataTable( TestHelper.QueryText, new { TopN = 50 }, ClownFish.CommandKind.SqlTextWithParams); } return s_OrderInfoTable; } }
看到這個版本的LoadOrderInfo方法,您會有什麼感覺?是不是很無奈?
沒辦法,ADO.NET就是這樣設計的。寫這樣的程式碼會讓人心煩(這是我的感受)!
測試呼叫程式碼:
[TestMethod("手工程式碼,DataTable", 5)] public class Test_Adonet_LoadDataTable : IPerformanceTest { public Test_Adonet_LoadDataTable(UiParameters param) { } public void Dispose() { } public ListRun() { return TestHelper.LoadFromDataTable(TestHelper.GetOrderInfoTable()); } }
測試程式碼應該沒有問題吧?
再來看一下使用ClownFish的等效測試程式碼:
[TestMethod("ClownFish,DataTable", 6)] public class Test_ClownFish_LoadDataTable : IPerformanceTest { public Test_ClownFish_LoadDataTable(UiParameters param) { } public void Dispose() { } public ListRun() { return ClownFish.DbHelper.FillListFromTable (TestHelper.GetOrderInfoTable()); } }
使用 ClownFish 的程式碼仍然要短很多!
下面繼續使用前面的測試引數來執行測試程式,得到以下測試結果:
Excel圖形直觀反映出 ClownFish 的速度要 比手工程式碼 快一倍 還不止。
看完這二個測試,ClownFish 有沒有給您留下二個印象?
1. 程式碼量很少。
2. 效能很好。
簡單,一個呼叫完成你要的全部功能
呼叫下面這個儲存過程,您需要多少行C#程式碼?
create procedure InsertProduct( @ProductName nvarchar(50), @CategoryID int, @Unit nvarchar(10), @UnitPrice money, @Quantity int, @Remark nvarchar(max), @ProductID int output ) as begin insert into Products (ProductName, CategoryID, Unit, UnitPrice, Quantity, Remark) values( @ProductName, @CategoryID, @Unit, @UnitPrice, @Quantity, @Remark); set @ProductID = scope_identity(); end
在這裡,我不想再想寫那種手工版本的ADO.NET程式碼,我懶得寫。
來看一下我從示例程式碼中摘選出來的呼叫程式碼吧:
Product product = CreateTestProduct(); // 插入一條新記錄 DbHelper.ExecuteNonQuery("InsertProduct", product); sb.AppendFormat("InsertProduct OK, ProductId is : {0}rn", product.ProductID);
看到了吧,其實只有一行程式碼(中間那行)。
再來看一個有分頁的吧:
create procedure GetProductByCategoryId( @CategoryID int, @PageIndex int = 0, @PageSize int = 20, @TotalRecords int output ) as begin declare @ResultTable table ( RowIndex int, ProductID int, ProductName nvarchar(50), CategoryID int, Unit nvarchar(10), UnitPrice money, Quantity int ); insert into @ResultTable select row_number() over (order by ProductID asc) as RowIndex, p.ProductID, p.ProductName, p.CategoryID, p.Unit, p.UnitPrice, p.Quantity from Products as p where CategoryID = @CategoryID; select @TotalRecords = count(*) from @ResultTable; select * from @ResultTable where RowIndex > (@PageSize * @PageIndex) and RowIndex相應的呼叫程式碼:
// 查詢一個分頁列表 var parameters = new GetProductByCategoryIdParameters { CategoryID = 1, PageIndex = 0, PageSize = 3, TotalRecords = 0 }; Listlist = DbHelper.FillList ("GetProductByCategoryId", parameters); sb.AppendFormat("存在 {0} 條符合條件的記錄。條件:CategoryID = {1}rn", parameters.TotalRecords, parameters.CategoryID); 還是 一個呼叫 就能完成全部的資料庫訪問工作。
老版本的通用資料訪問層有這樣一個設計目標:
呼叫儲存過程,不管輸入引數多麼複雜,不管有多少輸出引數,包含轉換結果集到實體列表,只需要一行C#程式碼。這一優點在ClownFish中得到了繼承:簡單,一個呼叫完成你要的全部功能。
方便,你需要的程式碼已經準備好了
看到前面的那段演示程式碼,你會不會想:我還要定義一個GetProductByCategoryIdParameters型別啊,太麻煩了!
真是那樣嗎? 請看下面的截圖:
注意:ClownFish並非僅僅支援儲存過程,後面將要介紹的XmlCommand也有同樣好的支援。
在工具中,不僅可以直接檢視儲存過程程式碼,還能生成呼叫程式碼。
工具生成二個呼叫程式碼,你可以選擇其中的一個(因為我不知道你需要哪個)。說明:呼叫GetProductByCategoryId需要定義一個型別是因為它包含了輸出引數,如果沒有輸出引數,工具會生成匿名型別:
接下來的事情我想大家都知道該怎麼做:把工具生成的程式碼COPY到專案中,修改一下呼叫引數就好了。
定義資料實體型別不再是費力的體力勞動
對於喜歡使用資料實體型別的人來說,手工定義這些型別是件費力且枯燥的體力勞動。
現在好了,ClownFish 的生成器可以減輕你的工作負擔:
還記得本文一開始的那個OrderInfo型別嗎,它是根據一個查詢語句生成的:
說明:根據查詢生成的型別,工具不知道如何命名,需要你在使用時改名。
ClownFish 不僅僅能生成資料實體型別,還能生成對應的增刪改命令及其對應的命令引數:
這裡涉及到另一個名詞:XmlCommand。它是什麼,我後面再說。
這個截圖是想告訴你:以後不必再為增刪改命令以及那些命令引數煩惱了。
執行圖片中那個選單操作,然後再到ClownFish的另一個輔助工具(XmlCommandTool)去貼上就可以了。
通用,可以非常簡單地實現對多種資料庫的支援
ClownFish已經從前輩版本中繼承了對多資料庫種類的支援。
本文的結尾處,我提供了可供下載的完整示例,其中就包含有:SQLSERVER, MySql, ACCESS的示例程式碼。以下是其中一個示例的截圖:ClownFish 支援多種資料庫的原因在於:
ClownFish 在內部使用了DbConnection, DbCommand, DbTransaction這些抽象型別。
ClownFish 提供的API是不區分資料庫種類的,您在註冊連線時,指定什麼型別,就是什麼型別。因此,如果您使用儲存過程,或者XmlCommand的話,完全可以做到:一份C#程式碼同時支援多種資料庫的。
靈活,SQL語句放在哪裡隨便你
對於資料庫的訪問方式目前有以下5種方案:
1. 有些人喜歡使用儲存過程。
2. 有些人不喜歡儲存過程也不喜歡把SQL語句放在C#程式碼中。
3. 有些人會在C#中嵌入引數化的SQL語句。
4. 有些人就是喜歡在C#程式碼中拼接SQL語句。
5. 還有些人不寫SQL語句而在使用ORM工具。
當然了,還有些人同時混合使用多種方案。
我不知道您屬於哪一類,如果是最後一類,那麼我只能說:ClownFish不適合你。
除此之外,ClownFish完全可以滿足您的需要。對於前4類方式,ClownFish在內部定義一個列舉來表示:
////// 表示要執行什麼型別的命令 /// public enum CommandKind { ////// 表示要執行一個儲存過程或者是一個XmlCommand /// SpOrXml, ////// 表示要執行一個儲存過程 /// StoreProcedure, ////// 表示要執行一個XmlCommand /// XmlCommand, ////// 表示要執行一條沒有引數的SQL語句 /// SqlTextNoParams, ////// 表示要執行一條包含引數的SQL語句 /// SqlTextWithParams }ClownFish提供了一個工具類:DbHelper,可以讓您方便地訪問資料庫,它的許多資料庫訪問方法可以指定一個CommandKind列舉表示要執行的命令型別:
public static class DbHelper { public static int ExecuteNonQuery( string nameOrSql, object inputParams); public static int ExecuteNonQuery( string nameOrSql, object inputParams, CommandKind cmdKind); public static int ExecuteNonQuery( string nameOrSql, object inputParams, DbContext dbContext); public static int ExecuteNonQuery( string nameOrSql, object inputParams, DbContext dbContext, CommandKind cmdKind); // 其它的資料庫訪問方法就不一一列出了,可以查閱API文件。 }DbHelper的所有資料庫訪問方法都提供與以上類似的4個過載方法。
那些方法中,第一個引數可以傳入一個SQL語句,或者一個儲存過程的名字,或者一個XmlCommand的名稱。
引數 cmdKind 的意義就是解釋第一個引數的含義。
XmlCommand是什麼?
前面已經多次提到了XmlCommand,這裡來解釋一下XmlCommand是什麼。
上一小節中的那些資料訪問方法中,有一類較為特殊:不使用儲存過程,也不把SQL語句混在C#程式碼中。
對於這種方案,就需要把SQL語句放在配置檔案中。或許有些人知道:我是喜歡用儲存過程的。我之所以喜歡儲存過程,是因為儲存過程的程式碼與專案程式碼是獨立的,我可以單獨修改儲存過程。而且,我還喜歡引數化的查詢,反對使用拼接SQL語句。我認為我是將資料庫做為我的SQL語句儲存容器在使用,儲存過程只是這一種方式而已(因為它有引數管理功能)。
除了儲存過程之外,使用配置檔案也能完成同樣功能:1. SQL語句的儲存容器,2. 提供引數管理功能。
雖然ClownFish的前輩版本也可以支援這種方案,但是卻沒有定義一種配置檔案的存放格式,因此,需要自己設計配置檔案格式,以及提供自己的簡化包裝方法。對於這種方案來說,本身是沒有任何技術難度的,只是需要定義一種配置檔案的存放格式。現在,ClownFish已經正式提供這種支援,並提供了一個管理工具。
我把儲存在配置檔案中的SQL語句稱為 XmlCommand ,一個XML檔案中可以儲存多個XmlCommand,每個XmlCommand都有一個名稱以便在執行時區分。下面是一個XmlCommand的程式碼片段:
看到這段程式碼,您會不會想:維護它們是不是很麻煩?
其實不是的,ClownFish提供了一個工具,可以輕鬆地管理它們:
在這個對話方塊中,每個XmlCommand的屬性都提供了控制元件編輯功能,完全不需要手工維護那段XML文字。
雙擊某個XmlCommand節點,還可以檢視呼叫程式碼:
工具生成的呼叫程式碼會告訴你那個XmlCommnad有多少個引數,您根本不用記住它們。
還有一點:
XmlCommand的XML檔案可以是多個,可以按照不同的業務邏輯來組織它們,例如:對於一個ASP.NET專案來說,ClownFish 還會監視這個已載入的目錄,如果目錄中的配置檔案有修改,會自動重新載入。
可監控,圖形的工具會告訴你每個資料訪問的細節
由於ClownFish是個資料訪問層,因此,它知道每次執行資料庫操作的所有資訊。所以,我提供了一個監控工具,用於檢視解詳細的資料庫訪問情況。
上圖反映了在執行MySql,Access的訪問情況。
上圖反映了事務的執行情況,以及某個語句執行失敗的情況。
根據工具視窗,我們可以直觀的檢視到每個連線中執行了多少次命令,它們是否屬於同一個事務,它們的型別是儲存過程,XmlCommand,SQL語句,全都一目瞭然。而且,還可以知道每個命令的執行引數是什麼:
或許有些人看了這個工具後,會認為它是多餘的,他總認為有個【SQL Server Profiler】,其它的東西都是沒有意義的。
我不知道他有沒有想過:用SQL Server Profiler去監視一個區域網的SQL Server例項,你還能分辨哪些操作是由你引發的嗎??我的工具則不同,它是配合我的資料訪問層一起工作的,由我的資料訪問層告訴工具當前使用者觸發了什麼資料庫的操作。
因此在分析本機程式訪問資料庫時,可以很直觀的知道:
您的某個操作引發了什麼樣的資料庫呼叫,以及呼叫的各種細節引數。更何況我的工具不止針對SQL Server有效,理論上,.net支援的,我的資料訪問層應該都支援。
前面也說了,它是配合我的資料訪問層一起工作的,
所以,只要是使用ClownFish,哪怕是訪問Access資料庫,也能有這樣的監控效果。(前面有圖片證明)
關於示例程式碼
為了能讓更多的人選擇ClownFish,這次我為大家準備了大量的示例程式碼。
1. ClownFishDEMO是一個綜合示例,網站型專案。執行效果圖:
2. MultiAccountDEMO是一個多帳套的演示網站。演示了一套程式碼處理多帳套的功能。
3. PerformanceTestApp是前面介紹的效能測試專案,你也可以將其它的資料訪問層加入其中,一起做效能測試。
不僅僅是以上三個專案演示瞭如何使用ClownFish,我還為以前的 MyMVC框架 的示例程式增加了資料庫訪問功能(以前只支援XML檔案)。
現在,MyMVC框架 的演示程式也能透過ClownFish來訪問SQLSERVER。
可以修改 MyMVC框架 演示網站中的web.config來切換資料訪問方法:<!--DataSoureceKind 表示不同的資料訪問方式,目前支援三個可選的配置值(注意大小寫): 1. XmlFile ,表示使用XML檔案中的資料,此選項為預設值。 2. XmlCommand ,表示使用配置檔案中的SQL命令去訪問SQLSERVER 3. StoreProcedure ,表示呼叫SQLSERVER中的儲存過程去訪問資料庫 你可以修改下面的配置,並結合ClownFishSQLProfiler.exe工具去觀察它們的差別。 --> 考慮到有些人在配置SQLSERVER時,會遇到一些問題,MyMVC框架的演示程式仍然預設使用XML檔案,而不是SQLSERVER資料庫。
但是,由於ClownFish是一個資料訪問層,它的示例只能訪問資料庫了。
如果不知道如何配置示例,請參考我的部落格:如何在IIS6,7中部署ASP.NET網站
還有一件讓我憂慮的事情:示例中還演示了 MySql的訪問!
我建議:如果你平時不使用MySql,那麼就跳過示例中的MySql演示部分吧。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2480/viewspace-2808426/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 如何寫出高效能程式碼之優化資料訪問優化
- spring mvc 的jpa JpaRepository資料層訪問SpringMVC
- 資料庫訪問幾種方式對比資料庫
- 阿里巴巴開源的通用快取訪問框架JetCache介紹阿里快取框架
- Vue原始碼之資料的代理訪問Vue原始碼
- MySQL 創始人:寫程式碼比打遊戲還爽!MySql遊戲
- 【譯】解決問題比寫程式碼更重要
- 02 | 編寫Model層程式碼
- 告訴你幾個Windows下寫程式碼比Linux還順手的工具WindowsLinux
- 排查一個潛在的記憶體訪問問題 — 用 C 寫程式碼的日常記憶體
- Spring資料訪問Spring
- JDBC資料庫訪問JDBC資料庫
- 資料訪問 - EntityFramework整合Framework
- PHP記錄訪問ip程式碼PHP
- 如何使用 ST05 事物碼,快速找到訪問指定資料庫表的 ABAP 程式碼試讀版資料庫
- (精華)2020年8月22日 ABP vNext 領域層和資料訪問層的單元測試
- 程式碼都寫不完,還寫個錘子註釋!
- 漫談“資料拆分層次對比”
- SpringBoot資料訪問之Druid資料來源的使用Spring BootUI
- 【SpringBoot實戰】資料訪問Spring Boot
- .net core下訪問控制層的實現
- 打造一個通用、可配置、多控制程式碼的資料上報 SDK
- 訪問vector元素方法的效率比較
- javascript訪問不同物件的速度比較JavaScript物件
- js 判斷是手機訪問,還是pc訪問JS
- NEON彙編比純C程式碼快17倍!C程式
- 你還在手寫TS型別程式碼嗎型別
- MySQL資料庫快問快答MySql資料庫
- 比nestjs更優雅的ioc:跨模組訪問資源JS
- 覺得還是敲程式碼比較來勁
- PHP FFI呼叫go,居然比go還快PHPGo
- 【計算機網路】資料鏈路層——基於通道劃分的介質訪問控制計算機網路
- 程式碼解決快取穿透和快取雪崩問題快取穿透
- MySQL每秒57萬的寫入,快還是慢?MySql
- sql server編寫archive通用模板指令碼實現自動分批刪除資料SQLServerHive指令碼
- 究竟先操作快取,還是資料庫?快取資料庫
- 外網訪問MySQL資料庫MySql資料庫
- Ext迭代陣列訪問資料陣列