最近一段時(shí)間,我使用??golang?
?開發(fā)了一個(gè)新的??ORM?
?庫(kù)。
為了讓這個(gè)庫(kù)更好用,我比較研究了各語言的主流??ORM?
?庫(kù),發(fā)現(xiàn)有一些語言的??ORM?
?庫(kù)確實(shí)很好用,而有另外一些語言的庫(kù)那不是一般的難用。
【資料圖】
然后我總結(jié)了他們呢的一些共性和差異點(diǎn),于是形成了本文的主要內(nèi)容。
本文會(huì)先說明什么是SQL編寫難題,以及探討一下 ??code first?
? 和 ??database first?
? 的優(yōu)缺點(diǎn)。然后依據(jù)這兩個(gè)問題的結(jié)論去審視目前主流后端語言??java?
?, ??c#?
?, ??php?
?, ??python?
?, ??go?
?各自的orm庫(kù),對(duì)比研究下他們的優(yōu)缺點(diǎn)。最后給出總結(jié)和參考文檔。
如果你需要做技術(shù)選型,或者做技術(shù)研究,或者類似于我做框架開發(fā),或者單純地了解各語言的差異,或者就是想吹個(gè)牛,建議保存或收藏。如果本文所涉及到的內(nèi)容有任何不正確,歡迎批評(píng)指正。
溫馨提示,本文會(huì)有一些戲謔或者調(diào)侃成分,并非對(duì)某些語言或者語言的使用者有任何歧視意見。如果對(duì)你造成了某些傷害,請(qǐng)多包涵。
如果你是做web開發(fā),那么必然需要保存數(shù)據(jù)到數(shù)據(jù)庫(kù),這個(gè)時(shí)候你必須熟悉使用sql語句來讀寫數(shù)據(jù)庫(kù)。
sql本身不難,命令也就那幾個(gè),關(guān)鍵字也不算多,但是為什么編寫sql會(huì)成為難題呢?
比如下面的sql
select * from user insert user (name,mobile) values ("tang","18600000000")
它有什么難題? 簡(jiǎn)單的單表操作嘛,一點(diǎn)難題沒有,但凡學(xué)過點(diǎn)??sql?
?的程序員都能寫出來,并且保證正確。我估計(jì)比例能超過90%
但是,如果你需要寫下面的sql呢?
SELECT article.*, person.name as person_name FROM article LEFT JOIN person ON person.id=article.person_id WHERE article.type = 0 AND article.age IN (18,20)
這個(gè)也不復(fù)雜,就是你在做查詢列表的時(shí)候,會(huì)經(jīng)常用到的聯(lián)表查詢。你是否還有勇氣說,寫出來的??sql?
?絕對(duì)正確。我估計(jì)比例不超過70%
再稍微復(fù)雜點(diǎn),如果是下面的sql?
SELECT o.*, d.department_name, (SELECT Sum(so.goods_fee) AS task_detail_target_completed_tem FROM sale_order so WHERE so.merchant_id = "356469725829664768" AND so.create_date BETWEEN (20230127) AND (20230212) AND so.delete_state = 2 AND so.department_id = o.department_id ) AS task_detail_target_completed FROM task_detail o LEFT JOIN department d ON d.department_id=o.department_id WHERE o.merchant_id = "356469725829664768" AND o.task_id = "356469725972271104768"
這是我項(xiàng)目里真實(shí)的sql語句,目的是統(tǒng)計(jì)出所有部門在某時(shí)間段內(nèi)各自的業(yè)績(jī)。邏輯上也不太復(fù)雜,但你是否還有勇氣說,寫出來的??sql?
?絕對(duì)正確。我估計(jì)比例不超過40%
如上面的sql所示,SQL編寫難題在于以下幾方面。
要保證字段正確
應(yīng)該有的字段不能少,不應(yīng)該有的字段不能多。
比如你把??mobile?
?誤打成??mobike?
?,這屬于拼寫錯(cuò)誤,但是這個(gè)拼寫錯(cuò)誤只有在實(shí)際運(yùn)行的時(shí)候才會(huì)告訴你字段名錯(cuò)了。
并且項(xiàng)目越大,表越多,字段越多,這種拼寫錯(cuò)誤發(fā)生的可能性越大。以至于可以肯定的說,100%的可能性會(huì)出現(xiàn)。
要特別注意sql語法
例如你在查詢的時(shí)候必須寫??from?
?,絕對(duì)不能誤寫成??form?
?,但是在實(shí)際開發(fā)過程中,很容易就打錯(cuò)了。
這種錯(cuò)誤,也只有運(yùn)行的時(shí)候才會(huì)告訴你語法錯(cuò)了。并且??sql?
?越復(fù)雜,這種語法錯(cuò)誤發(fā)生的可能性越大。
編輯器不會(huì)有sql的語法提示
常見的編碼用的軟件,對(duì)于sql相關(guān)的代碼,不會(huì)有語法提示,也不會(huì)有表名提示,字段名提示。
最終的代碼質(zhì)量如何全憑你的眼力,經(jīng)驗(yàn),能力。
很顯然,既然存在該難題,那么哪個(gè)ORM能解決該難題,就應(yīng)該算得上好,如果不能解決,則不能稱之為好。
這倆概念并不是新概念,但是我估計(jì)大多數(shù)開發(fā)者并不熟悉。
所謂 code first, 相近的詞是 model fist, 意思是模型優(yōu)先,指的是在設(shè)計(jì)和開發(fā)系統(tǒng)時(shí),優(yōu)先和重點(diǎn)做的工作是設(shè)計(jì)業(yè)務(wù)模型,然后根據(jù)業(yè)務(wù)模型去創(chuàng)建數(shù)據(jù)庫(kù)。
所謂 database first,意思是數(shù)據(jù)庫(kù)優(yōu)先,指的是在設(shè)計(jì)和開發(fā)系統(tǒng)時(shí),優(yōu)先和重點(diǎn)做的工作是創(chuàng)建數(shù)據(jù)庫(kù)結(jié)構(gòu),然后去實(shí)現(xiàn)業(yè)務(wù)。
這里我提到了幾個(gè)詞語,可能在不同的語言里叫法不一樣,可能不同的人的叫法也不一樣,為了下述方便,我們舉例子來說。
假設(shè)我是一個(gè)對(duì)電商系統(tǒng)完全不懂的小白,手頭上也沒有如何設(shè)計(jì)電商系統(tǒng)的資料,我和我的伙伴只是模糊地知道電商系統(tǒng)主要業(yè)務(wù)就是處理訂單。
然后我大概會(huì)知道這個(gè)訂單,主要的信息包括哪個(gè)用戶下單,什么時(shí)間下單,有哪幾種商品,數(shù)量分別是多少,根據(jù)這些已有的信息,我可以設(shè)計(jì)出來業(yè)務(wù)模型如下
public class OrderModel { //訂單編號(hào) Integer orderId; //用戶編號(hào) Integer userId; //訂單時(shí)間 Integer createTime; //訂單詳情(包含商品編號(hào),商品數(shù)量) String orderDetail;}
很簡(jiǎn)單,對(duì)吧,這個(gè)模型很匹配我目前對(duì)系統(tǒng)的認(rèn)知。接下來會(huì)做各種業(yè)務(wù)邏輯,最后要做的是將訂單模型的數(shù)據(jù)保存到數(shù)據(jù)庫(kù)。但是在保存數(shù)據(jù)到數(shù)據(jù)庫(kù)的時(shí)候,就有一些考慮了。
我可以將上面??OrderModel?
?業(yè)務(wù)模型建立一張對(duì)應(yīng)表,里面的4個(gè)屬性,對(duì)應(yīng)數(shù)據(jù)表里的4個(gè)字段,這完全可以。但是我是電商小白,不是數(shù)據(jù)庫(kù)小白啊,這樣存儲(chǔ)的話,肯定不利于統(tǒng)計(jì)訂單商品的。
所以我換一種策略,將??OrderModel?
?的信息進(jìn)行拆分,將前三個(gè)屬性 orderId, userId, createTime 放到一個(gè)新的類里。然后將 orderDetail 的信息進(jìn)行再次分解,放到另一個(gè)類里
public class OrderEntity { Integer orderId; Integer userId; Integer createTime;}public class OrderDetailEntity { Integer orderDetailId; Integer orderId; Integer goodsId; Integer goodsCount;}
最后,在數(shù)據(jù)庫(kù)建立兩張表??order?
?,??order_detail?
?,表結(jié)構(gòu)分別對(duì)應(yīng)類??OrderEntity?
?,??OrderDetailEntity?
?的結(jié)構(gòu)。
至此,我們完成了從業(yè)務(wù)模型??OrderModel?
?到數(shù)據(jù)表??order?
?,??order_detail?
?的過程。
這就是 code first ,注意這個(gè)過程的關(guān)鍵點(diǎn),我優(yōu)先考慮的是模型和業(yè)務(wù)實(shí)現(xiàn),后面將業(yè)務(wù)模型數(shù)據(jù)進(jìn)行分解和保存是次要的,非優(yōu)先的。
假設(shè)我是一個(gè)對(duì)電商系統(tǒng)非常熟悉的老鳥,之前做過很多電商系統(tǒng),那么我在做新的電商系統(tǒng)的時(shí)候,就完全可以先設(shè)計(jì)數(shù)據(jù)庫(kù)。
??order?
?表放訂單主要數(shù)據(jù),里面有xxx幾個(gè)字段,分別有什么作用,有哪些狀態(tài)值
??order_detail?
?表放訂單詳情數(shù)據(jù),,里面有xxx幾個(gè)字段,分別有什么作用
這些都可以很清楚和明確。然后根據(jù)表信息,生成??OrderEntity?
?,以及??OrderDetailEntity?
?即可開始接下來的編碼工作。這種情況下??OrderModel?
?可能有,也可能沒有。
這就是 database first ,注意這個(gè)過程的關(guān)鍵點(diǎn),我優(yōu)先考慮的是數(shù)據(jù)庫(kù)結(jié)構(gòu)和數(shù)據(jù)表結(jié)構(gòu)。
code first 模式下, 系統(tǒng)設(shè)計(jì)者優(yōu)先考慮的是業(yè)務(wù)模型??OrderModel?
?, 它可以描述清楚一個(gè)完整業(yè)務(wù),包括它的所有業(yè)務(wù)細(xì)節(jié)(什么人的訂單,什么時(shí)候的訂單,訂單包含哪些商品,數(shù)量多少),有利于設(shè)計(jì)者對(duì)于系統(tǒng)的整體把控。
database first 模式下, 系統(tǒng)設(shè)計(jì)者優(yōu)先考慮的是數(shù)據(jù)表??order?
?,??order_detail?
?,他們中任何一張表都不能完整的描述清楚一個(gè)完整業(yè)務(wù),只能夠描述局部細(xì)節(jié),不利于設(shè)計(jì)者對(duì)于系統(tǒng)的整體把控。
在這里,調(diào)皮的同學(xué)會(huì)問,在 database first 模式下, 我把??order?
?,??order_detail?
?的信息一起看,不就知道完整的業(yè)務(wù)細(xì)節(jié)了嗎?
確實(shí)是這樣,但這里有一個(gè)前提,前提是你必須明確的知道??order?
?,??order_detail?
?是需要一起看的,而你知道他們需要一起看的前提是你了解電商系統(tǒng)。 如果你設(shè)計(jì)的不是電商系統(tǒng),而是電路系統(tǒng),你還了解嗎?還知道哪些表需要一起看嗎?
至此,我們可以有以下粗淺的判斷:
對(duì)于新項(xiàng)目,不熟悉的業(yè)務(wù),code first 模式更適合一些
對(duì)于老項(xiàng)目,熟悉的業(yè)務(wù),database first 模式更合適一些
如果兩種模式都可以的話,優(yōu)先使用 code first 模式,便于理解業(yè)務(wù),把控項(xiàng)目
如果哪個(gè)ORM支持 code first , 我們可以稍稍認(rèn)為它更好一些
Java語言是web開發(fā)領(lǐng)域處于領(lǐng)先地位,這一點(diǎn)無可置疑。它的優(yōu)點(diǎn)很明顯,但是缺點(diǎn)也不是沒有。
國(guó)內(nèi)應(yīng)用比較廣泛的orm是Mybatis,以及衍生品Mybatis-plus等
實(shí)際上Mybatis團(tuán)隊(duì)還出了另外一款產(chǎn)品,MyBatis Dynamic SQL,國(guó)內(nèi)我見用的不多,討論都較少。英文還可以的同學(xué),可以看下面的文檔。
另外還有 jOOQ, 實(shí)際上跟 MyBatis Dynamic SQL 非常類似,有興趣的可以去翻翻
下面,我們舉一些例子,來對(duì)比一下他們的基本操作
單就orm這一塊,國(guó)內(nèi)用的最多的應(yīng)該是Mybatis,說到它的使用體驗(yàn)吧,那簡(jiǎn)直是一言難盡。
你需要先定義模型,然后編寫??xml?
?文件用來映射數(shù)據(jù),然后創(chuàng)建mapper文件,用來執(zhí)行??xml?
?里定于的sql。從這個(gè)流程可以看出,中間的??xml?
?文件起到核心作用,里面不光有數(shù)據(jù)類型轉(zhuǎn)換,還有最核心的??sql?
?語句。
典型的??xml?
?文件內(nèi)容如下
insert into user (id,name,mobile) values (#{id},#{name},#{mobile}) update user set name = #{name}, mobile = #{mobile} where id = #{id} delete from user where id = #{id}
你在編寫這個(gè)??xml?
?文件的時(shí)候,這個(gè)手寫sql沒有本質(zhì)區(qū)別,一定會(huì)遇到剛才說到的??SQL編寫難題?
?。
這里有必要提一下 Mybatis-plus,它是國(guó)內(nèi)的團(tuán)隊(duì)開發(fā)出來的工具,算是對(duì)Mybatis的擴(kuò)展吧,它減少了??xml?
?文件內(nèi)容的編寫,減少了一些開發(fā)的痛苦。比如,你可以使用如下的代碼來完成以上相同的工作
userService.insert(user); userService.update(user); userService.deleteById(user); ListuserList = userService.selectList(queryWrapper);
完成這些工作,你不需要編寫任何??xml?
?文件,也不需要編寫??sql?
?語句,如之前所述,減少了一些開發(fā)的痛苦。
但是,請(qǐng)你注意我的用詞,是減少了一些。
對(duì)于連表操作,嵌套查詢等涉及到多表操作的事情,它就不行了,為啥不行,因?yàn)楦揪筒恢С职 S龅竭@種情況,你就老老實(shí)實(shí)的去寫??xml?
?吧,然后你還會(huì)遇到剛才說到的??SQL編寫難題?
?。
值得一提的是Mybatis3 Dynamic Sql,翻譯一下就是動(dòng)態(tài)sql。還是剛才說的國(guó)內(nèi)我見用的不多,討論都較少,但是評(píng)價(jià)看上去挺好。
簡(jiǎn)單來說,可以根據(jù)不同條件拼接出sql語句。不同于上面的Mybatis,這些sql語句是程序運(yùn)行時(shí)生成的,而不是提前寫好的,或者定義好的。
它的使用流程是,先在數(shù)據(jù)庫(kù)里定義好數(shù)據(jù)表,然后創(chuàng)建模型文件,讓然后通過命令行工具,將每一個(gè)表生成如下的支持文件
public final class PersonDynamicSqlSupport { public static final Person person = new Person(); public static final SqlColumnid = person.id; public static final SqlColumn firstName = person.firstName; public static final SqlColumn lastName = person.lastName; public static final SqlColumn birthDate = person.birthDate; public static final SqlColumn employed = person.employed; public static final SqlColumn occupation = person.occupation; public static final SqlColumn addressId = person.addressId; public static final class Person extends SqlTable { public final SqlColumn id = column("id", JDBCType.INTEGER); public final SqlColumn firstName = column("first_name", JDBCType.VARCHAR); public final SqlColumn lastName = column("last_name", JDBCType.VARCHAR, "examples.simple.LastNameTypeHandler"); public final SqlColumn birthDate = column("birth_date", JDBCType.DATE); public final SqlColumn employed = column("employed", JDBCType.VARCHAR, "examples.simple.YesNoTypeHandler"); public final SqlColumn occupation = column("occupation", JDBCType.VARCHAR); public final SqlColumn addressId = column("address_id", JDBCType.INTEGER); public Person() { super("Person"); } }}
可以看出,這里的主要功能能是將表內(nèi)的字段,與java項(xiàng)目里的類里面的屬性,做了一一映射。
接下來你在開發(fā)的時(shí)候,就不用關(guān)心表名,以及字段名了,直接使用剛才生成的類,以及類下面的那些屬性。具體如下
SelectStatementProvider selectStatement = select(id.as("A_ID"), firstName, lastName, birthDate, employed,occupation, addressId) .from(person) .where(id, isEqualTo(1)) .or(occupation, isNull()) .build() .render(RenderingStrategies.MYBATIS3); Listrows = mapper.selectMany(selectStatement);
如上面的代碼,好處有以下四點(diǎn)
你不再需要手寫sql也不用在意字段名了,因?yàn)槭褂玫亩际穷?,或者屬性,編寫代碼的時(shí)候編輯器會(huì)有提示,編譯的時(shí)候如果有錯(cuò)誤也會(huì)提示,實(shí)際運(yùn)行的時(shí)候就不會(huì)有問題了。聯(lián)表查詢,嵌套查詢啥的,也都支持完美避開了??SQL編寫難題?
?當(dāng)然帶來了額外的事情,比如你要使用工具來生成??PersonDynamicSqlSupport?
?類,比如你要先建表。
先建表這事兒,很明顯就屬于 ??database first?
? 模式。
C# 在工業(yè)領(lǐng)域,游戲領(lǐng)域用的多一些,在web領(lǐng)域少一些。
它也有自己的orm,名字叫 Entity Framework Core, 一直都是微軟公司在維護(hù)。
下面是一個(gè)典型的聯(lián)表查詢
var id = 1; var query = database.Posts .Join(database.Post_Metas, post => post.ID, meta => meta.Post_ID, (post, meta) => new { Post = post, Meta = meta } ) .Where(postAndMeta => postAndMeta.Post.ID == id);
這句代碼的主要作用是,將數(shù)據(jù)庫(kù)里的Posts表,與Post_Metas表做內(nèi)聯(lián)操作,然后取出Post.ID等于1的數(shù)據(jù)
這里出現(xiàn)的Post,以及Meta都是提前定義好的模型,也就是類。 ??Post.ID?? 是 Post 的一個(gè)屬性,也是提前定義好的。
整個(gè)功能的優(yōu)點(diǎn)很多,你不再需要手寫sql,不需要關(guān)心字段名,不需要生成額外類,也不會(huì)有語法錯(cuò)誤,你只需要提前定義好模型,完全沒有??SQL編寫難題?
?,很明顯就屬于 ??code first?
? 模式。
對(duì)比java的Mybatis以及Mybatis3 Dynamic Sql來說,你可以腦補(bǔ)一下下面的場(chǎng)景
php體系內(nèi),框架也非常多,比如常見的??laravel?
?,??symfony?
?,這里我們就看這兩個(gè),比較有代表性
使用php語言開發(fā)web應(yīng)用的也很多,其中比較出名的是??laravel?
?框架,比較典型的操作數(shù)據(jù)庫(kù)的代碼如下
$user = DB::table("users")->where("name", "John")->first();
這里沒有使用模型(就算使用了也差不多),代碼里出現(xiàn)的 users 就是數(shù)據(jù)庫(kù)表的名字, name 是 users 表里的字段名,他們是被直接寫入代碼的
很明顯它會(huì)產(chǎn)生??SQL編寫難題?
?
并且,因?yàn)槭窍仍O(shè)計(jì)數(shù)據(jù)庫(kù),肯定也屬于 ??database first?
? 模式
這個(gè)框架歷史也比較悠久了,它使用了 Doctrine 找個(gè)類庫(kù)作為orm
使用它之前,也需要先定義模型,然后生成支持文件,然后建表,但是在實(shí)際使用的時(shí)候,還是和laravel一樣,表名,字段名都需要硬編碼
$repository = $this->getDoctrine()->getRepository("AppBundle:Product"); // query for a single product by its primary key (usually "id")// 通過主鍵(通常是id)查詢一件產(chǎn)品$product = $repository->find($productId); // dynamic method names to find a single product based on a column value// 動(dòng)態(tài)方法名稱,基于字段的值來找到一件產(chǎn)品$product = $repository->findOneById($productId);$product = $repository->findOneByName("Keyboard");// query for multiple products matching the given name, ordered by price// 查詢多件產(chǎn)品,要匹配給定的名稱和價(jià)格$products = $repository->findBy( array("name" => "Keyboard"), array("price" => "ASC"));
很明顯它也會(huì)產(chǎn)生??SQL編寫難題?
?
另外,并不是先設(shè)計(jì)表,屬于 ??code first?
? 模式
在python領(lǐng)域,有一個(gè)非常著名的框架,叫django, 另外一個(gè)比較出名的叫flask, 前者追求大而全,后者追求小而精
django推薦的開發(fā)方法,也是先建模型,但是在查詢的時(shí)候,這建立的模型,基本上毫無用處
res=models.Author.objects.filter(name="jason").values("author_detail__phone","name") print(res) # 反向 res = models.AuthorDetail.objects.filter(author__name="jason") # 拿作者姓名是jason的作者詳情 res = models.AuthorDetail.objects.filter(author__name="jason").values("phone","author__name") print(res) # 2.查詢書籍主鍵為1的出版社名稱和書的名稱 res = models.Book.objects.filter(pk=1).values("title","publish__name") print(res) # 反向 res = models.Publish.objects.filter(book__id=1).values("name","book__title") print(res)
如上連表查詢的代碼,values("title","publish__name") 這里面寫的全都是字段名,硬編碼進(jìn)去,進(jìn)而產(chǎn)生sql語句,查詢出結(jié)果
很顯然,它也會(huì)產(chǎn)生??SQL編寫難題?
?
另外,并不是先設(shè)計(jì)表,屬于 ??code first?
? 模式
flask本身沒有orm,一般搭配 sqlalchemy 使用
使用 sqlalchemy 的時(shí)候,一般也是先建模型,然后查詢的時(shí)候,可以直接使用模型的屬性,而無須硬編碼
result = session. query(User.username,func.count(Article.id)).join(Article,User.id==Article.uid).group_by(User.id).order_by(func.count(Article.id).desc()).all()
如上 ??Article.id?? 即是 Article 模型下的 id 屬性
很顯然,它不會(huì)產(chǎn)生??SQL編寫難題?
?
另外,并不是先設(shè)計(jì)表,屬于 ??code first?
? 模式
在go體系,orm比較多,屬于百花齊放的形態(tài),比如國(guó)內(nèi)用的多得gorm以及gorm gen,國(guó)外比較多的ent, 當(dāng)然還有我自己寫的 arom
使用gorm,一般的流程是你先建立模型,然后使用類似如下的代碼進(jìn)行操作
type User struct { Id int Age int}type Order struct { UserId int FinishedAt *time.Time}query := db.Table("order").Select("MAX(order.finished_at) as latest").Joins("left join user user on order.user_id = user.id").Where("user.age > ?", 18).Group("order.user_id")db.Model(&Order{}).Joins("join (?) q on order.finished_at = q.latest", query).Scan(&results)
這是一個(gè)嵌套查詢,雖然定義了模型,但是查詢的時(shí)候并沒有使用模型的屬性,而是輸入硬編碼
很顯然,它會(huì)產(chǎn)生??SQL編寫難題?
?
另外,是先設(shè)計(jì)模型,屬于 ??code first?
? 模式
gorm gen 是 gorm 團(tuán)隊(duì)開發(fā)的另一款產(chǎn)品,和mybaits下的Mybatis3 Dynamic Sql比較像
它的流程是 先創(chuàng)建數(shù)據(jù)表,然后使用工具生成結(jié)構(gòu)體(類)和支持代碼, 然后再使用生成的結(jié)構(gòu)體
它生成的比較關(guān)鍵的代碼如下
func newUser(db *gorm.DB) user { _user := user{} _user.userDo.UseDB(db) _user.userDo.UseModel(&model.User{}) tableName := _user.userDo.TableName() _user.ALL = field.NewAsterisk(tableName) _user.ID = field.NewInt64(tableName, "id") _user.Name = field.NewString(tableName, "name") _user.Age = field.NewInt64(tableName, "age") _user.Balance = field.NewFloat64(tableName, "balance") _user.UpdatedAt = field.NewTime(tableName, "updated_at") _user.CreatedAt = field.NewTime(tableName, "created_at") _user.DeletedAt = field.NewField(tableName, "deleted_at") _user.Address = userHasManyAddress{ db: db.Session(&gorm.Session{}), RelationField: field.NewRelation("Address", "model.Address"), } _user.fillFieldMap() return _user}
注意看,其中大多數(shù)代碼的作用是啥?不意外,就是將結(jié)構(gòu)體的屬性與表字段做映射關(guān)系
_user.Name 對(duì)應(yīng) name_user.Age 對(duì)應(yīng) age
如此,跟mybaits下的Mybatis3 Dynamic Sql的思路非常一致
典型查詢代碼如下
u := query.Usererr := u.WithContext(ctx). Select(u.Name, u.Age.Sum().As("total")). Group(u.Name). Having(u.Name.Eq("group")). Scan(&users)// SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group"
這是一個(gè)分組查詢,定義了模型,也使用了模型的屬性。
但是呢,它需要使用工具生成額外的支持代碼,并且需要先定義數(shù)據(jù)表
很顯然,它不會(huì)產(chǎn)生??SQL編寫難題?
?
另外,它是先設(shè)計(jì)表,屬于 ??database first?
? 模式
ent 是 facebook公司開發(fā)的Orm產(chǎn)品,與 gorm gen 有相通,也有不同
相同點(diǎn)在于,都是利用工具生成實(shí)體與數(shù)據(jù)表字段的映射關(guān)系
不同點(diǎn)在于gorm gen先有表和字段,然后生成實(shí)體
ent是沒有表和字段,你自己手動(dòng)配置,配置完了一起生成實(shí)體和建表
接下來,看一眼ent生成的映射關(guān)系
const ( // Label holds the string label denoting the user type in the database. Label = "user" // FieldID holds the string denoting the id field in the database. FieldID = "id" // FieldName holds the string denoting the name field in the database. FieldName = "name" // FieldAge holds the string denoting the age field in the database. FieldAge = "age" // FieldAddress holds the string denoting the address field in the database. FieldAddress = "address" // Table holds the table name of the user in the database. Table = "users")
有了映射關(guān)系,使用起來就比較簡(jiǎn)單了
u, err := client.User. Query(). Where(user.Name("realcp")). Only(ctx)
注意,這里沒有硬編碼
它需要使用工具生成額外的支持代碼,并且需要先配置表結(jié)構(gòu)
很顯然,它不會(huì)產(chǎn)生??SQL編寫難題?
?
另外,它屬于先設(shè)計(jì)表,屬于 ??database first?
? 模式
aorm 是我自己開發(fā)的orm庫(kù),吸取了ef core 的一些優(yōu)點(diǎn),比較核心的步驟如下
和大多數(shù)orm一樣,需要先建立模型,比如
type Person struct { Id null.Int `aorm:"primary;auto_increment" json:"id"` Name null.String `aorm:"size:100;not null;comment:名字" json:"name"` Sex null.Bool `aorm:"index;comment:性別" json:"sex"` Age null.Int `aorm:"index;comment:年齡" json:"age"` Type null.Int `aorm:"index;comment:類型" json:"type"` CreateTime null.Time `aorm:"comment:創(chuàng)建時(shí)間" json:"createTime"` Money null.Float `aorm:"comment:金額" json:"money"` Test null.Float `aorm:"type:double;comment:測(cè)試" json:"test"` }
然后實(shí)例化它,并且保存起來
//Instantiation the struct var person = Person{} //Store the struct object aorm.Store(&person)
然后即可使用
var personItem Person err := aorm.Db(db).Table(&person).WhereEq(&person.Id, 1).OrderBy(&person.Id, builder.Desc).GetOne(&personItem) if err != nil { fmt.Println(err.Error()) }
很顯然,它不會(huì)產(chǎn)生??SQL編寫難題?
?
另外,它屬于先設(shè)計(jì)模型,屬于 ??code first?
? 模式
本文,我們提出了兩個(gè)衡量orm功能的原則,并且對(duì)比了幾大主流后端語言的orm,匯總列表如下
框架 | 語言 | SQL編寫難題 | code first | 額外創(chuàng)建文件 |
MyBatis 3 | java | 有難度 | 不是 | 需要 |
MyBatis-Plus | java | 有難度 | 不是 | 不需要 |
MyBatis Dynamic SQL | java | 沒有 | 不是 | 需要 |
jOOQ | java | 沒有 | 不是 | 需要 |
ef core | c# | 沒有 | 是 | 不需要 |
laravel | php | 有難度 | 不是 | 不需要 |
symfony | php | 有難度 | 不是 | 需要 |
django | python | 有難度 | 不是 | 不需要 |
sqlalchemy | python | 沒有 | 是 | 不需要 |
grom | go | 有難度 | 是 | 不需要 |
grom gen | go | 沒有 | 不是 | 需要 |
ent | go | 沒有 | 不是 | 需要 |
aorm | go | 沒有 | 是 | 不需要 |
單就從這張表來說,不考慮其他條件,在做orm技術(shù)選型時(shí),
如果你使用java語言,請(qǐng)選擇 MyBatis Dynamic SQL 或者 jOOQ,因?yàn)檫x擇他們不會(huì)有??SQL編寫難題?
?
如果你使用c#語言,請(qǐng)選擇 ef core, 這已經(jīng)是最棒的orm了,不會(huì)有??SQL編寫難題?
?,支持??code first?
?,并且不需要額外的工作
如果你使用php語言,請(qǐng)選擇 laravel 而不是 symfony, 反正都有??SQL編寫難題?
?,那就挑個(gè)容易使用的
如果你使用python語言,請(qǐng)選擇 sqlalchemy 庫(kù), 不會(huì)有??SQL編寫難題?
?,支持??code first?
?,并且不需要額外的工作
如果你使用go語言,請(qǐng)選擇 aorm 庫(kù), 不會(huì)有??SQL編寫難題?
?,支持??code first?
?,并且不需要額外的工作
好了,文章寫兩天了,終于寫完了。如果對(duì)你有幫助,記得點(diǎn)贊,收藏,轉(zhuǎn)發(fā)。
如果我有說的不合適,或者不對(duì)的地方,請(qǐng)?jiān)谙旅婧莺莸呐u(píng)我。
??MyBatis 3??MyBatis-PlusMyBatis Dynamic SQLjOOQ: The easiest way to write SQL in JavaEntity Framework Core 概述 - EF Core | Microsoft Learn數(shù)據(jù)庫(kù)和Doctrine ORM - Symfony開源 - Symfony中國(guó) (symfonychina.com)Django(ORM查詢、多表、跨表、子查詢、聯(lián)表查詢) - 知乎 (zhihu.com)Sqlalchemy join連表查詢_FightAlita的博客-CSDN博客_sqlalchemy 連表查詢Gorm + Gen自動(dòng)生成數(shù)據(jù)庫(kù)結(jié)構(gòu)體_Onemorelight95的博客-CSDN博客_gorm 自動(dòng)生成??tangpanqing/aorm: Operate Database So Easy For GoLang Developer (github.com)??