ORM哪家強?java,c#,php,python,go 逐一對比, 網友直呼:全面客觀

湯哥搞開發發表於2023-01-29

前言

最近一段時間,我使用golang開發了一個新的ORM庫。

為了讓這個庫更好用,我比較研究了各語言的主流ORM庫,發現有一些語言的ORM庫確實很好用,而有另外一些語言的庫那不是一般的難用。

然後我總結了他們呢的一些共性和差異點,於是形成了本文的主要內容。

本文會先說明什麼是SQL編寫難題,以及探討一下 code firstdatabase first 的優缺點。
然後依據這兩個問題的結論去審視目前主流後端語言java, c#, php, python, go各自的orm庫,對比研究下他們的優缺點。最後給出總結和參考文件。

如果你需要做技術選型,或者做技術研究,或者類似於我做框架開發,或者單純地瞭解各語言的差異,或者就是想吹個牛,建議儲存或收藏。如果本文所涉及到的內容有任何不正確,歡迎批評指正。

溫馨提示,本文會有一些戲謔或者調侃成分,並非對某些語言或者語言的使用者有任何歧視意見。
如果對你造成了某些傷害,請多包涵。

renzhen.png

什麼是SQL編寫難題

如果你是做web開發,那麼必然需要儲存資料到資料庫,這個時候你必須熟悉使用sql語句來讀寫資料庫。

sql本身不難,命令也就那幾個,關鍵字也不算多,但是為什麼編寫sql會成為難題呢?

比如下面的sql

    select * from user
    
    insert user (name,mobile) values ('tang','18600000000')

它有什麼難題? 簡單的單表操作嘛,一點難題沒有,但凡學過點sql的程式設計師都能寫出來,並且保證正確。我估計比例能超過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)

這個也不復雜,就是你在做查詢列表的時候,會經常用到的聯表查詢。你是否還有勇氣說,寫出來的sql絕對正確。我估計比例不超過70%

再稍微複雜點,如果是下面的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'

這是我專案裡真實的sql語句,目的是統計出所有部門在某時間段內各自的業績。邏輯上也不太複雜,但你是否還有勇氣說,寫出來的sql絕對正確。我估計比例不超過40%

如上面的sql所示,SQL編寫難題在於以下幾方面。

要保證欄位正確

應該有的欄位不能少,不應該有的欄位不能多。

比如你把mobile誤打成mobike,這屬於拼寫錯誤,但是這個拼寫錯誤只有在實際執行的時候才會告訴你欄位名錯了。

並且專案越大,表越多,欄位越多,這種拼寫錯誤發生的可能性越大。以至於可以肯定的說,100%的可能性會出現。

要特別注意sql語法

例如你在查詢的時候必須寫from,絕對不能誤寫成form,但是在實際開發過程中,很容易就打錯了。

這種錯誤,也只有執行的時候才會告訴你語法錯了。並且sql越複雜,這種語法錯誤發生的可能性越大。

編輯器不會有sql的語法提示

常見的編碼用的軟體,對於sql相關的程式碼,不會有語法提示,也不會有表名提示,欄位名提示。

最終的程式碼質量如何全憑你的眼力,經驗,能力。

下載.png

很顯然,既然存在該難題,那麼哪個ORM能解決該難題,就應該算得上好,如果不能解決,則不能稱之為好。

什麼是code first 和 database first

這倆概念並不是新概念,但是我估計大多數開發者並不熟悉。

所謂 code first, 相近的詞是 model fist, 意思是模型優先,指的是在設計和開發系統時,優先和重點做的工作是設計業務模型,然後根據業務模型去建立資料庫。

所謂 database first,意思是資料庫優先,指的是在設計和開發系統時,優先和重點做的工作是建立資料庫結構,然後去實現業務。

這裡我提到了幾個詞語,可能在不同的語言裡叫法不一樣,可能不同的人的叫法也不一樣,為了下述方便,我們舉例子來說。

code first 例子

假設我是一個對電商系統完全不懂的小白,手頭上也沒有如何設計電商系統的資料,我和我的夥伴只是模糊地知道電商系統主要業務就是處理訂單。

然後我大概會知道這個訂單,主要的資訊包括哪個使用者下單,什麼時間下單,有哪幾種商品,數量分別是多少,根據這些已有的資訊,我可以設計出來業務模型如下

public class OrderModel {
    //訂單編號
    Integer orderId;
    //使用者編號
    Integer userId;
    //訂單時間
    Integer createTime;
    //訂單詳情(包含商品編號,商品數量)
    String  orderDetail;
}

很簡單,對吧,這個模型很匹配我目前對系統的認知。接下來會做各種業務邏輯,最後要做的是將訂單模型的資料儲存到資料庫。但是在儲存資料到資料庫的時候,就有一些考慮了。

我可以將上面OrderModel業務模型建立一張對應表,裡面的4個屬性,對應資料表裡的4個欄位,這完全可以。
但是我是電商小白,不是資料庫小白啊,這樣儲存的話,肯定不利於統計訂單商品的。

所以我換一種策略,將OrderModel的資訊進行拆分,將前三個屬性 orderId, userId, createTime 放到一個新的類裡。
然後將 orderDetail 的資訊進行再次分解,放到另一個類裡

public class OrderEntity {
    Integer orderId;
    Integer userId;
    Integer createTime;
}

public class OrderDetailEntity {
    Integer orderDetailId;
    Integer orderId;
    Integer goodsId;
    Integer goodsCount;
}

最後,在資料庫建立兩張表order,order_detail,表結構分別對應類OrderEntity,OrderDetailEntity的結構。

至此,我們完成了從業務模型OrderModel到資料表order,order_detail的過程。

這就是 code first ,注意這個過程的關鍵點,我優先考慮的是模型和業務實現,後面將業務模型資料進行分解和儲存是次要的,非優先的。

database first 例子

假設我是一個對電商系統非常熟悉的老鳥,之前做過很多電商系統,那麼我在做新的電商系統的時候,就完全可以先設計資料庫。

order表放訂單主要資料,裡面有xxx幾個欄位,分別有什麼作用,有哪些狀態值

order_detail表放訂單詳情資料,,裡面有xxx幾個欄位,分別有什麼作用

這些都可以很清楚和明確。然後根據表資訊,生成OrderEntity,以及OrderDetailEntity即可開始接下來的編碼工作。這種情況下OrderModel可能有,也可能沒有。

這就是 database first ,注意這個過程的關鍵點,我優先考慮的是資料庫結構和資料表結構。

兩種方式對比

code first 模式下, 系統設計者優先考慮的是業務模型OrderModel, 它可以描述清楚一個完整業務,包括它的所有業務細節(什麼人的訂單,什麼時候的訂單,訂單包含哪些商品,數量多少),有利於設計者對於系統的整體把控。

database first 模式下, 系統設計者優先考慮的是資料表order,order_detail,他們中任何一張表都不能完整的描述清楚一個完整業務,只能夠描述區域性細節,不利於設計者對於系統的整體把控。

在這裡,調皮的同學會問,在 database first 模式下, 我把order,order_detail的資訊一起看,不就知道完整的業務細節了嗎?

確實是這樣,但這裡有一個前提,前提是你必須明確的知道order,order_detail是需要一起看的,而你知道他們需要一起看的前提是你瞭解電商系統。 如果你設計的不是電商系統,而是電路系統,你還了解嗎?還知道哪些表需要一起看嗎?

至此,我們可以有以下粗淺的判斷:

對於新專案,不熟悉的業務,code first 模式更適合一些

對於老專案,熟悉的業務,database first 模式更合適一些

如果兩種模式都可以的話,優先使用 code first 模式,便於理解業務,把控專案

如果哪個ORM支援 code first , 我們可以稍稍認為它更好一些

Java體系的orm

Java語言是web開發領域處於領先地位,這一點無可置疑。它的優點很明顯,但是缺點也不是沒有。

國內應用比較廣泛的orm是Mybatis,以及衍生品Mybatis-plus等

實際上Mybatis團隊還出了另外一款產品,MyBatis Dynamic SQL,國內我見用的不多,討論都較少。英文還可以的同學,可以看下面的文件。

另外還有 jOOQ, 實際上跟 MyBatis Dynamic SQL 非常類似,有興趣的可以去翻翻

下面,我們舉一些例子,來對比一下他們的基本操作

Java體系的Mybatis

單就orm這一塊,國內用的最多的應該是Mybatis,說到它的使用體驗吧,那簡直是一言難盡。

你需要先定義模型,然後編寫xml檔案用來對映資料,然後建立mapper檔案,用來執行xml裡定於的sql。
從這個流程可以看出,中間的xml檔案起到核心作用,裡面不光有資料型別轉換,還有最核心的sql語句。

典型的xml檔案內容如下

<mapper namespace="xxx.mapper.UserMapper">
    <insert id="insertUser" parameterType="UserEntity">
        insert into user (id,name,mobile)
        values (#{id},#{name},#{mobile})
    </insert>

    <update id="updateUser" parameterType="UserEntity">
        update user set
        name = #{name},
        mobile = #{mobile}
        where id = #{id}
    </update>

    <delete id="deleteUser">
        delete from user where id = #{id}
    </delete>

    <select id="selectUsers" resultType="UserVO">
        select u.*, (select count(*) from article a where a.uid=u.id) as article_count
        from user u
        where u.id = #{id}
    </select>
</mapper>

你在編寫這個xml檔案的時候,這個手寫sql沒有本質區別,一定會遇到剛才說到的SQL編寫難題

Java體系的Mybatis-plus

這裡有必要提一下 Mybatis-plus,它是國內的團隊開發出來的工具,算是對Mybatis的擴充套件吧,它減少了xml檔案內容的編寫,減少了一些開發的痛苦。比如,你可以使用如下的程式碼來完成以上相同的工作

    userService.insert(user);

    userService.update(user);

    userService.deleteById(user);

    List<UserEntity> userList = userService.selectList(queryWrapper);

完成這些工作,你不需要編寫任何xml檔案,也不需要編寫sql語句,如之前所述,減少了一些開發的痛苦。

但是,請你注意我的用詞,是減少了一些。

對於連表操作,巢狀查詢等涉及到多表操作的事情,它就不行了,為啥不行,因為根本就不支援啊。
遇到這種情況,你就老老實實的去寫xml吧,然後你還會遇到剛才說到的SQL編寫難題

Java體系的Mybatis3 Dynamic Sql

值得一提的是Mybatis3 Dynamic Sql,翻譯一下就是動態sql。還是剛才說的國內我見用的不多,討論都較少,但是評價看上去挺好。

簡單來說,可以根據不同條件拼接出sql語句。不同於上面的Mybatis,這些sql語句是程式執行時生成的,而不是提前寫好的,或者定義好的。

它的使用流程是,先在資料庫裡定義好資料表,然後建立模型檔案,讓然後透過命令列工具,將每一個表生成如下的支援檔案

public final class PersonDynamicSqlSupport {
    public static final Person person = new Person();
    public static final SqlColumn<Integer> id = person.id;
    public static final SqlColumn<String> firstName = person.firstName;
    public static final SqlColumn<LastName> lastName = person.lastName;
    public static final SqlColumn<Date> birthDate = person.birthDate;
    public static final SqlColumn<Boolean> employed = person.employed;
    public static final SqlColumn<String> occupation = person.occupation;
    public static final SqlColumn<Integer> addressId = person.addressId;

    public static final class Person extends SqlTable {
        public final SqlColumn<Integer> id = column("id", JDBCType.INTEGER);
        public final SqlColumn<String> firstName = column("first_name", JDBCType.VARCHAR);
        public final SqlColumn<LastName> lastName = column("last_name", JDBCType.VARCHAR, "examples.simple.LastNameTypeHandler");
        public final SqlColumn<Date> birthDate = column("birth_date", JDBCType.DATE);
        public final SqlColumn<Boolean> employed = column("employed", JDBCType.VARCHAR, "examples.simple.YesNoTypeHandler");
        public final SqlColumn<String> occupation = column("occupation", JDBCType.VARCHAR);
        public final SqlColumn<Integer> addressId = column("address_id", JDBCType.INTEGER);

        public Person() {
            super("Person");
        }
    }
}

可以看出,這裡的主要功能能是將表內的欄位,與java專案裡的類裡面的屬性,做了一一對映。

接下來你在開發的時候,就不用關心表名,以及欄位名了,直接使用剛才生成的類,以及類下面的那些屬性。具體如下

        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);

        List<PersonRecord> rows = mapper.selectMany(selectStatement);

如上面的程式碼,好處有以下四點

  1. 你不再需要手寫sql
  2. 也不用在意欄位名了,因為使用的都是類,或者屬性,編寫程式碼的時候編輯器會有提示,編譯的時候如果有錯誤也會提示,實際執行的時候就不會有問題了。
  3. 聯表查詢,巢狀查詢啥的,也都支援
  4. 完美避開了SQL編寫難題

當然帶來了額外的事情,比如你要使用工具來生成PersonDynamicSqlSupport類,比如你要先建表。

先建表這事兒,很明顯就屬於 database first 模式。

事情不大.png

C#體系的orm

C# 在工業領域,遊戲領域用的多一些,在web領域少一些。

它也有自己的orm,名字叫 Entity Framework Core, 一直都是微軟公司在維護。

下面是一個典型的聯表查詢

    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);

這句程式碼的主要作用是,將資料庫裡的Posts表,與Post_Metas表做內聯操作,然後取出Post.ID等於1的資料

這裡出現的Post,以及Meta都是提前定義好的模型,也就是類。 Post.ID 是 Post 的一個屬性,也是提前定義好的。

整個功能的優點很多,你不再需要手寫sql,不需要關心欄位名,不需要生成額外類,也不會有語法錯誤,你只需要提前定義好模型,完全沒有SQL編寫難題,很明顯就屬於 code first 模式。

對比java的Mybatis以及Mybatis3 Dynamic Sql來說,你可以腦補一下下面的場景

javavsc#.png

PHP體系的orm

php體系內,框架也非常多,比如常見的laravel,symfony,這裡我們就看這兩個,比較有代表性

PHP體系的laravel

使用php語言開發web應用的也很多,其中比較出名的是laravel框架,比較典型的運算元據庫的程式碼如下

$user = DB::table('users')->where('name', 'John')->first();

這裡沒有使用模型(就算使用了也差不多),程式碼裡出現的 users 就是資料庫表的名字, name 是 users 表裡的欄位名,他們是被直接寫入程式碼的

很明顯它會產生SQL編寫難題

並且,因為是先設計資料庫,肯定也屬於 database first 模式

PHP體系的symfony

這個框架歷史也比較悠久了,它使用了 Doctrine 找個類庫作為orm

使用它之前,也需要先定義模型,然後生成支援檔案,然後建表,但是在實際使用的時候,還是和laravel一樣,表名,欄位名都需要硬編碼

$repository = $this->getDoctrine()->getRepository('AppBundle:Product');
 
// query for a single product by its primary key (usually "id")
// 透過主鍵(通常是id)查詢一件產品
$product = $repository->find($productId);
 
// dynamic method names to find a single product based on a column value
// 動態方法名稱,基於欄位的值來找到一件產品
$product = $repository->findOneById($productId);
$product = $repository->findOneByName('Keyboard');

// query for multiple products matching the given name, ordered by price
// 查詢多件產品,要匹配給定的名稱和價格
$products = $repository->findBy(
    array('name' => 'Keyboard'),
    array('price' => 'ASC')
);

很明顯它也會產生SQL編寫難題

另外,並不是先設計表,屬於 code first 模式

下載.png

python體系的orm

在python領域,有一個非常著名的框架,叫django, 另外一個比較出名的叫flask, 前者追求大而全,後者追求小而精

python體系的django

django推薦的開發方法,也是先建模型,但是在查詢的時候,這建立的模型,基本上毫無用處

    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') 這裡面寫的全都是欄位名,硬編碼進去,進而產生sql語句,查詢出結果

很顯然,它也會產生SQL編寫難題

另外,並不是先設計表,屬於 code first 模式

python體系的flask

flask本身沒有orm,一般搭配 sqlalchemy 使用

使用 sqlalchemy 的時候,一般也是先建模型,然後查詢的時候,可以直接使用模型的屬性,而無須硬編碼

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 屬性

很顯然,它不會產生SQL編寫難題

另外,並不是先設計表,屬於 code first 模式

下載 (1).png

go體系的orm

在go體系,orm比較多,屬於百花齊放的形態,比如國內用的多得gorm以及gorm gen,國外比較多的ent, 當然還有我自己寫的 arom

go體系下的gorm

使用gorm,一般的流程是你先建立模型,然後使用類似如下的程式碼進行操作

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)

這是一個巢狀查詢,雖然定義了模型,但是查詢的時候並沒有使用模型的屬性,而是輸入硬編碼

很顯然,它會產生SQL編寫難題

另外,是先設計模型,屬於 code first 模式

go體系下的gorm gen

gorm gen 是 gorm 團隊開發的另一款產品,和mybaits下的Mybatis3 Dynamic Sql比較像

它的流程是 先建立資料表,然後使用工具生成結構體(類)和支援程式碼, 然後再使用生成的結構體

它生成的比較關鍵的程式碼如下

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
}

注意看,其中大多數程式碼的作用是啥?不意外,就是將結構體的屬性與表欄位做對映關係

_user.Name 對應 name
_user.Age 對應 age

如此,跟mybaits下的Mybatis3 Dynamic Sql的思路非常一致

典型查詢程式碼如下

u := query.User
err := 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"

這是一個分組查詢,定義了模型,也使用了模型的屬性。

但是呢,它需要使用工具生成額外的支援程式碼,並且需要先定義資料表

很顯然,它不會產生SQL編寫難題

另外,它是先設計表,屬於 database first 模式

go體系下的ent

ent 是 facebook公司開發的Orm產品,與 gorm gen 有相通,也有不同

相同點在於,都是利用工具生成實體與資料表欄位的對映關係

不同點在於gorm gen先有表和欄位,然後生成實體

ent是沒有表和欄位,你自己手動配置,配置完了一起生成實體和建表

接下來,看一眼ent生成的對映關係

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"
)

有了對映關係,使用起來就比較簡單了

u, err := client.User.
        Query().
        Where(user.Name("realcp")).
        Only(ctx)

注意,這裡沒有硬編碼

它需要使用工具生成額外的支援程式碼,並且需要先配置表結構

很顯然,它不會產生SQL編寫難題

另外,它屬於先設計表,屬於 database first 模式

go體系下的aorm

aorm 是我自己開發的orm庫,吸取了ef core 的一些優點,比較核心的步驟如下

和大多數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:建立時間" json:"createTime"`
        Money      null.Float  `aorm:"comment:金額" json:"money"`
        Test       null.Float  `aorm:"type:double;comment:測試" json:"test"`
    }
    

然後例項化它,並且儲存起來

    //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())
    }

很顯然,它不會產生SQL編寫難題

另外,它屬於先設計模型,屬於 code first 模式

下載 (2).png

總結

本文,我們提出了兩個衡量orm功能的原則,並且對比了幾大主流後端語言的orm,彙總列表如下

框架語言SQL編寫難題code first額外建立檔案
MyBatis 3java有難度不是需要
MyBatis-Plusjava有難度不是不需要
MyBatis Dynamic SQLjava沒有不是需要
jOOQjava沒有不是需要
ef corec#沒有不需要
laravelphp有難度不是不需要
symfonyphp有難度不是需要
djangopython有難度不是不需要
sqlalchemypython沒有不需要
gromgo有難度不需要
grom gengo沒有不是需要
entgo沒有不是需要
aormgo沒有不需要

單就從這張表來說,不考慮其他條件,在做orm技術選型時,

如果你使用java語言,請選擇 MyBatis Dynamic SQL 或者 jOOQ,因為選擇他們不會有SQL編寫難題

如果你使用c#語言,請選擇 ef core, 這已經是最棒的orm了,不會有SQL編寫難題,支援code first,並且不需要額外的工作

如果你使用php語言,請選擇 laravel 而不是 symfony, 反正都有SQL編寫難題,那就挑個容易使用的

如果你使用python語言,請選擇 sqlalchemy 庫, 不會有SQL編寫難題,支援code first,並且不需要額外的工作

如果你使用go語言,請選擇 aorm 庫, 不會有SQL編寫難題,支援code first,並且不需要額外的工作

好了,文章寫兩天了,終於寫完了。如果對你有幫助,記得點贊,收藏,轉發。

如果我有說的不合適,或者不對的地方,請在下面狠狠的批評我。

微信圖片_20221226163643.png

參考文件

MyBatis 3
MyBatis-Plus
MyBatis Dynamic SQL
jOOQ: The easiest way to write SQL in Java
Entity Framework Core 概述 - EF Core | Microsoft Learn
資料庫和Doctrine ORM - Symfony開源 - Symfony中國 (symfonychina.com)
Django(ORM查詢、多表、跨表、子查詢、聯表查詢) - 知乎 (zhihu.com)
Sqlalchemy join連表查詢_FightAlita的部落格-CSDN部落格_sqlalchemy 連表查詢
Gorm + Gen自動生成資料庫結構體_Onemorelight95的部落格-CSDN部落格_gorm 自動生成
tangpanqing/aorm: Operate Database So Easy For GoLang Developer (github.com)

相關文章