您當前的位置 :云南之窗 > 游戲 >  內容正文
投稿

大量SQL的解決方案——sdmap

云南之窗 2020-03-23 08:48:21 來源: 閱讀:

大量SQL的解決方案——sdmap

最近看到群里面經常討論大型應用中 SQL的管理辦法,有人說用EF/EFCore,但很多人不信任它生成SQL的語句;有人說用Dapper,但將SQL寫到代碼中有些人覺得不合適;有人提出用存儲過程,但現在輿論紛紛反對這種做法;有人提出了iBatis.NET,它可以配置確保高靈活性高性能,也提供動態SQL的功能,但已經多年沒有維護。

在幾年前,我們某項目中就有總共 4MB以上的SQL語句文本,我也注意到產品做大后會,一定出現這個問題,所以我就依照MyBatis的核心思想,支持可配置、動態SQL,但去除了臃腫的xml,自己實現了一套簡單好用的語法,然后開源了出來,名字就叫sdmap。

在我的介紹頁面上已經指出, sdmap的如下特性:

  • 非常簡單的語法來描述動態 SQL;

  • 使用了 EmitCIL來確保性能;

  • VisualStudio插件支持,實現了代碼高亮、代碼折疊、快速導航的特性;

  • 支持所有主流數據庫,如 MySQL、SQLServer、SQLite等(只要Dapper能支持);

  • 可以擴展支持非關系型數據庫,如 Neo4j;

  • 單元測試全覆蓋。

語法

如圖:

該語法有如下特點:

  • namespace關鍵字表達名字空間;

  • sql關鍵字表示模板語句;

  • #號的特殊語法可以進行一些判斷,里面有isEqual<>、#isNotEmpty<>等特殊語法;

  • #include<>,可以包含另一個SQL語句;

  • 語句可以嵌套, sql{}中可以包含另一個sql{}。

我們可以對比一下 iBatis/MyBatis的語法:

  1. <!DOCTYPE mapper

  2. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

  3. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


  4. <mappernamespace="org.apache.ibatis.submitted.rounding.Mapper">

  5. <resultMaptype="org.apache.ibatis.submitted.rounding.User" id="usermap">

  6. <idcolumn="id" property="id"/>

  7. <resultcolumn="name" property="name"/>

  8. <resultcolumn="funkyNumber" property="funkyNumber"/>

  9. <resultcolumn="roundingMode" property="roundingMode"/>

  10. </resultMap>


  11. <selectid="getUser" resultMap="usermap">

  12. select * from users

  13. </select>

  14. <insertid="insert">

  15. insert into users (id, name, funkyNumber, roundingMode) values (

  16. #{id}, #{name}, #{funkyNumber}, #{roundingMode}

  17. )

  18. </insert>


  19. <resultMaptype="org.apache.ibatis.submitted.rounding.User" id="usermap2">

  20. <idcolumn="id" property="id"/>

  21. <resultcolumn="name" property="name"/>

  22. <resultcolumn="funkyNumber" property="funkyNumber"/>

  23. <resultcolumn="roundingMode" property="roundingMode"

  24. typeHandler="org.apache.ibatis.type.EnumTypeHandler"/>

  25. </resultMap>

  26. <selectid="getUser2" resultMap="usermap2">

  27. select * from users2

  28. </select>

  29. <insertid="insert2">

  30. insert into users2 (id, name, funkyNumber, roundingMode) values (

  31. #{id}, #{name}, #{funkyNumber}, #{roundingMode, typeHandler=org.apache.ibatis.type.EnumTypeHandler}

  32. )

  33. </insert>


  34. </mapper>

相比之下,由于 XML的存在,MyBatis的語法有更多噪音。

簡單應用

Hello World

其實和 MyBatis不同,sdmap設計之初就只考慮好好做一個小巧、精致、快速的模板引擎。因此sdmap可以不依賴于任何數據庫,只做字符串解析,最簡單的代碼是:

  1. // 安裝NuGet包:sdmap

  2. varc =newSdmapCompiler;

  3. c.AddSourceCode("sql v1 {Hello World}");

  4. Console.WriteLine(c.Emit("v1", )); // Hello World

參數傳入

當然有一些前端輸入,這樣就需要第二個參數:

  1. varc =newSdmapCompiler;

  2. c.AddSourceCode("sql v1 {Hello #prop<Name>!}");

  3. Console.WriteLine(c.Emit("v1", new{ Name = "Hero"}));// Hello Hero!

注意我使用了一個 #prop<>的語法,這是sdmap中調用指令的語句,表示將Name屬性按原樣顯示在此處。

參數判斷

有些語句需要根據前端的不同而不同,比如典型的“動態 SQL”問題,如果前端傳了參數,則執行過濾,沒有傳則不過濾,這樣的代碼如下:

  1. varc =newSdmapCompiler;

  2. c.AddSourceCode(@"

  3. sql v1

  4. {

  5. SELECT * FROM [Customer]

  6. WHERE 1=1

  7. #isNotEmpty<Location, sql {AND Location = '#prop<Location>'}

  8. }");

  9. Console.WriteLine(c.Emit("v1", new{ Id = 1, Location = "長沙"}));

  10. Console.WriteLine(c.Emit("v1", new{ Id = 2, Location = ""}));

輸出結果如下:

  1. SELECT * FROM [Customer]

  2. WHERE 1=1

  3. AND Location = '長沙'


  4. SELECT * FROM [Customer]

  5. WHERE 1=1

可見,關鍵的那個 isNotEmpty<>控制了Location判斷的語句。

擴展:sdmap.ext

每次使用時,都需要實例化一個 SdmapCompiler來加載sdmap語句很麻煩,在項目中,這部分邏輯重用度非常高,因此我寫了一個擴展:sdmap.ext,定義了ISdmapEmiter接口,該接口定義如下:

  1. publicinterfaceISdmapEmiter

  2. {

  3. stringEmit(stringstatementId,objectparameters);

  4. }

相當于最簡單的生成器,然后我寫了幾個內置實現,可以直接從文件系統或者程序集嵌入的資源中讀入這些文件:

  1. publicclassEmbeddedResourceSqlEmiter : ISdmapEmiter

  2. {

  3. publicstaticEmbeddedResourceSqlEmiter CreateFrom(Assembly assembly);

  4. // ...

  5. }


  6. publicclassMultipleAssemblyEmbeddedResourceSqlEmiter : ISdmapEmiter

  7. {

  8. publicstaticMultipleAssemblyEmbeddedResourceSqlEmiter CreateFrom(paramsAssembly[] assemblies);

  9. // ...

  10. }


  11. publicclassFileSystemSqlEmiter : ISdmapEmiter

  12. {

  13. publicstaticFileSystemSqlEmiter FromSqlDirectory(

  14. stringsqlDirectory,

  15. boolensureCompiled =false);


  16. publicstaticFileSystemSqlEmiter FromSqlDirectoryAndWatch(

  17. stringsqlDirectory,

  18. boolensureCompiled =false);

  19. // ...

  20. }

那么有人會問,數據庫參數化該如何實現呢?

擴展:sdmap.ext.Dapper

答案是 Dapper,sdmap訪問數據庫時,依賴Dapper做參數化。其實很好理解,sdmap只做數據庫訪問時的SQL模板引擎前端,Dapper做后端(當然不一定非要用Dapper),sdmap只負責生成SQL語句。

但隨著大家使用越來越多,我也注意到確實可以寫一些東西,便于大家更好地配合 Dapper一起使用。因此我寫了另外兩個擴展:sdmap.extsdmap.ext.Dapper。

其中 sdmap.ext仍然和數據庫無關,定義了一些.sdmap文件的讀取和自動加載邏輯;sdmap.ext.Dapper依賴于Dapper,定義了一些便利方法:

如圖,用過 Dapper的朋友知道,DapperIDbConnection定義了一套擴展方法,這里我也為IDbConnection定義了一套一樣的擴展,只要最后加了ByMap后綴,第二個參數都為sqlMapName,與其傳入原始的SQL語句,此處將傳入定義在.sdmap文件中的配置,如原先使用Dapper的朋友,代碼可能這樣寫:

  1. vardata = _db.Query<Customer>("SELECT * FROM [Customer] Where Id = @Id");

換成 sdmap后,代碼應該是這樣寫:

  1. vardata = _db.QueryByMap<Customer>("Customers.GetById");

然后 sdmap配置如下:

  1. namespaceCustomers

  2. {

  3. sql GetById

  4. {

  5. SELECT * FROM [Customer] WHERE Id = @Id

  6. }

  7. }

注意, sdmap使用了Dapper的參數化方式,只需在SQL中寫@Id這樣的語句,即可自動實現參數化,得出結果完全一樣,并且SQL不存在注入問題,代碼中不包含SQL語句,語句都寫在配置文件中。

數組參數化

由于 Dapper的存在,sdmap相當于也自動支持了數組的參數化,只要像Dapper那樣寫IN即可:

  1. namespaceCustomer

  2. {

  3. sql GetByIds

  4. {

  5. SELECT * FROM [Customer] WHERE Id IN @Ids

  6. }

  7. }

相關鏈接

Github地址

https://github.com/sdcb/sdmap 我的 Github首頁還包含了使用sdmap.ext.Dapper的一步一步使用教程,可以依照上面的使用。

文檔地址

https://github.com/sdcb/sdmap/wiki

所有指令參考鏈接:

https://github.com/sdcb/sdmap/wiki/Common-macros

NuGet包地址

  • https://www.nuget.org/packages/sdmap

  • https://www.nuget.org/packages/sdmap.ext

  • https://www.nuget.org/packages/sdmap.ext.Dapper

VisualStudio插件地址

https://marketplace.visualstudio.com/items?itemName=sdmapvstool.sdmapvstool

VS插件提供了.sdmap文件代碼高亮、自動定位、代碼折疊的功能,可以不裝,但不裝就沒這些體驗。

總結

我寫 sdmap最初純粹是因為想挑戰自己,它包含了【編譯器前端——ANTLR】、【編譯器后端——CIL】、【VisualStudio插件如何制作】、單元測試、文檔等主題。

但后來隨著這個項目的發展,越來越多的朋友用了起來。用過的都紛紛提出了自己的想法,然后做了許多潤色,解決了不少局限性,但我從未做過推廣——這是我第一次將這個項目用文字的形式發表出來。希望這個項目能給大家以管理大量 SQL的啟發。

(正文已結束)

推薦閱讀:adobe有哪些軟件

免責聲明及提醒:此文內容為本網所轉載企業宣傳資訊,該相關信息僅為宣傳及傳遞更多信息之目的,不代表本網站觀點,文章真實性請瀏覽者慎重核實!任何投資加盟均有風險,提醒廣大民眾投資需謹慎!

網站簡介 - 聯系我們 - 營銷服務 - 老版地圖 - 版權聲明 - 網站地圖
Copyright.2002-2019 云南之窗 版權所有 本網拒絕一切非法行為 歡迎監督舉報 如有錯誤信息 歡迎糾正
饿了么商户赚钱 一分快3和值计划 腾讯分分彩任选一人工计划 广西快乐十分出号走势图 股票指数计算方法 加权法综合法 湖北11选五任五走势图 湖北体彩十一选五走势 股票查询在线 秒秒彩的原理 广东好彩1最新开奖结果查询 广东11选5前三和值统计