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

2023-01-29 18:01:25

前言

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

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

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

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

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

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

什麼是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相關的程式碼,不會有語法提示,也不會有表名提示,欄位名提示。

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

很顯然,既然存在該難題,那麼哪個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 模式。

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來說,你可以腦補一下下面的場景

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 模式

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 模式

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 模式

總結

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

框架 語言 SQL編寫難題 code first 額外建立檔案
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技術選型時,

如果你使用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,並且不需要額外的工作

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

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

參考檔案

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)