@

概述

定义

GORM 官网地址 https://gorm.io/ 最新版本v1.25.1

GORM 官网文档地址 https://gorm.io/docs/

GORM 源码地址 https://github.com/go-gorm/gorm

GORM 是Golang语言中一个功能齐全的优秀的ORM 框架,对开发者友好,支持多种数据库,并提供了丰富的功能和 API,可以让开发者更加方便地进行数据库操作。

核心功能

  • ORM功能丰富、完整
  • 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
  • Create,Save,Update,Delete,Find 中钩子方法
  • 支持 Preload、Joins 的预加载
  • 事务,嵌套事务,Save Point,Rollback To Saved Point
  • Context、预编译模式、DryRun 模式
  • 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
  • SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询
  • 复合主键,索引,约束
  • Auto Migration
  • 自定义 Logger
  • 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
  • 每个特性都经过了测试的重重考验
  • 开发者友好

声明模型与约定

模型是标准的 struct,由 Go 的基本数据类型、实现了 Scanner和 Valuer接口的自定义类型及其指针或别名组成,示例如:

type User struct {
ID uint
Name string
Email *string
Age uint8
Birthday *time.Time
MemberNumber sql.NullString
ActivatedAt sql.NullTime
CreatedAt time.Time
UpdatedAt time.Time
}

GORM 倾向于约定优于配置 默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间。如果遵循GORM采用的约定,则只需编写很少的配置/代码。如果约定不符合需求,GORM也允许指定配置。

gorm.Model

GORM定义了gorm.Model,其中包括字段ID, CreatedAt, UpdatedAt, DeletedAt,可以将其嵌入到结构中以包含这些字段

字段级权限

可导出的字段在使用 GORM 进行 CRUD 时拥有全部的权限,此外,GORM 允许您用标签控制字段级别的权限。这样就可以让一个字段的权限是只读、只写、只创建、只更新或者被忽略

type User struct {
Name string `gorm:"<-:create"` // 允许读和创建
Name string `gorm:"<-:update"` // 允许读和更新
Name string `gorm:"<-"` // 允许读和写(创建和更新)
Name string `gorm:"<-:false"` // 允许读,禁止写
Name string `gorm:"->"` // 只读(除非有自定义配置,否则禁止写)
Name string `gorm:"->;<-:create"` // 允许读和写
Name string `gorm:"->:false;<-:create"` // 仅创建(禁止从 db 读)
Name string `gorm:"-"` // 通过 struct 读写会忽略该字段
Name string `gorm:"-:all"` // 通过 struct 读写、迁移会忽略该字段
Name string `gorm:"-:migration"` // 通过 struct 迁移会忽略该字段
}

时间惯例

GORM按惯例使用CreatedAt、UpdatedAt来跟踪创建/更新时间,如果定义了字段,GORM将在创建/更新时设置当前时间。要使用具有不同名称的字段,可以使用标签autoCreateTime、autoUpdateTime来配置这些字段。如果希望节省存储不采用时间格式改为采用UNIX(毫/纳)秒,可以简单地更改字段的数据类型。

type User struct {
CreatedAt time.Time // 如果创建时为零,则设置为当前时间
UpdatedAt int // 在更新时设置为当前unix秒数,或者在创建时设置为零
Updated int64 `gorm:"autoUpdateTime:nano"` // 使用unix纳秒作为更新时间
Updated int64 `gorm:"autoUpdateTime:milli"`// 使用unix毫秒作为更新时间
Created int64 `gorm:"autoCreateTime"` // 使用unix seconds作为创建时间
}

嵌入结构

例如将Go内置gorm.Model结构体嵌入到User结构体里

type User struct {
gorm.Model
Name string
}
// 这个定义等价于上面
type User struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Name string
}

但对于普通的struct字段,可以将其嵌入标签,例如:

type Author struct {
Name string
Email string
} type Blog struct {
ID int
Author Author `gorm:"embedded"`
Upvotes int32
}
// 这个定义等价于上面Blog内嵌Author
type Blog struct {
ID int64
Name string
Email string
Upvotes int32
}

可以使用标签embeddedPrefix为嵌入字段的数据库名称添加前缀,例如:

type Blog struct {
ID int
Author Author `gorm:"embedded;embeddedPrefix:author_"`
Upvotes int32
}
// 这个定义等价于上面Blog内嵌Author
type Blog struct {
ID int64
AuthorName string
AuthorEmail string
Upvotes int32
}

字段标签

Tag Name Description
column 数据库表列名
type 列数据类型,例如:bool, int, uint, float, string, time, bytes,这适用于所有数据库,并且可以与其他标签一起使用,如' not null ', ' size ', ' autoIncrement '…指定的数据库数据类型,如' varbinary(8) '也支持,当使用指定的数据库数据类型时,它需要是一个完整的数据库数据类型,例如: MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT
serializer 指定如何将数据序列化和反序列化到db的序列化器, e.g: serializer:json/gob/unixtime
size 指定列数据大小/长度, e.g: size:256
primaryKey 指定列为主键
unique 将列指定为唯一约束
default 指定列默认值
precision 指定列精度
scale 指定列的比例
not null 指定列为NOT NULL
autoIncrement 指定列可自动递增
autoIncrementIncrement 自动递增步长,控制连续列值之间的间隔
embedded 嵌入字段
embeddedPrefix 嵌入字段的列名前缀
autoCreateTime 创建时跟踪当前时间,对于' int '字段,它将跟踪Unix秒,使用值' nano ' / ' milli '来跟踪Unix纳米/毫秒, e.g: autoCreateTime:nano
autoUpdateTime 在创建/更新时跟踪当前时间,对于' int '字段,它将跟踪Unix秒,使用值' nano ' / ' milli '来跟踪Unix纳米/毫秒, e.g: autoUpdateTime:milli
index 对多个字段使用相同的名称创建复合索引
uniqueIndex 与' index '相同,但创建唯一的索引
check 创建检查约束, eg: check:age > 13
<- 设置字段的写权限,' <-:create ' create-only字段,' <-:update ' update-only字段,' <-:false '无写权限,' <- '创建和更新权限
-> 设置字段的读权限,' ->:false '没有读权限
- 忽略该字段,' - '无读写权限,' -:migration '无迁移权限,' -:all '无读写迁移权限
comment 在迁移时为字段添加注释

使用

安装

# 引入gorm
go get -u gorm.io/gorm
# 引入mySQL驱动
go get -u gorm.io/driver/mysql
# 引入sqlite驱动
go get -u gorm.io/driver/sqlite

数据库链接

import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
) func main() {
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}

MySQL Driver提供了一些可以在初始化时使用的高级配置,例如:

db, err := gorm.Open(mysql.New(mysql.Config{
DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // data source name
DefaultStringSize: 256, // default size for string fields
DisableDatetimePrecision: true, // disable datetime precision, which not supported before MySQL 5.6
DontSupportRenameIndex: true, // drop & create when rename index, rename index not supported before MySQL 5.7, MariaDB
DontSupportRenameColumn: true, // `change` when rename column, rename column not supported before MySQL 8, MariaDB
SkipInitializeWithVersion: false, // auto configure based on currently MySQL version
}), &gorm.Config{})

连接池

GORM使用数据库/sql维护连接池

sqlDB, err := db.DB()// SetMaxIdleConns设置空闲连接池中的最大连接数。sqlDB.SetMaxIdleConns(10)// SetMaxOpenConns设置数据库的最大打开连接数。sqlDB.SetMaxOpenConns(100)// SetConnMaxLifetime设置连接可能被重用的最大时间。sqlDB.SetConnMaxLifetime(time.Hour)

CRUD 接口

创建

package main

import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
) type User struct {
gorm.Model
Name string
Age int8
} func main() {
dsn := "root:123456@tcp(mysqlserver8:3306)/test?charset=utf8&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("data error")
} db.AutoMigrate(&User{}) // 单个插入创建
db.Create(&User{Name: "zhangsan", Age: 20}) users := []User{
{Name: "lisi", Age: 25},
{Name: "wangwu", Age: 26},
} // 多个插入并演示返回值
result := db.Create(users)
if result != nil {
fmt.Println(result.RowsAffected)
fmt.Println(result.Error)
} var user User
// 根据主键ID查询主键值为1的记录并返回数据
db.First(&user, 1)
fmt.Println(user)
}

MySQL的test数据库及对应表users信息如下:

// 指定批处理大小
var users = []User{{Name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}
db.CreateInBatches(users, 100) // 初始化GORM时使用CreateBatchSize选项,所有INSERT在创建记录和关联时都将遵循此选项
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
CreateBatchSize: 1000,
})
db := db.Session(&gorm.Session{CreateBatchSize: 1000})
users = [5000]User{{Name: "jinzhu", Pets: []Pet{pet1, pet2, pet3}}...}
db.Create(&users)

钩子函数示例

package main

import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
) type User struct {
gorm.Model
Name string
Age int8
} func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
if u.Name == "admin" {
fmt.Println("before create hint admin")
}
return
} func (u *User) AfterCreate(tx *gorm.DB) (err error) {
if u.Name == "admin" {
fmt.Println("after create hint admin")
}
return
} func (u *User) BeforeSave(tx *gorm.DB) (err error) {
if u.Name == "admin" {
fmt.Println("before save hint admin")
}
return
} func (u *User) AfterSave(tx *gorm.DB) (err error) {
if u.Name == "admin" {
fmt.Println("after save hint admin")
}
return
} func main() {
dsn := "root:123456@tcp(mysqlserver8:3306)/test?charset=utf8&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("data error")
} db.Create(&User{Name: "admin", Age: 40})
}

如果你想跳过Hooks方法,可以使用SkipHooks会话模式

DB.Session(&gorm.Session{SkipHooks: true}).Create(&user)
DB.Session(&gorm.Session{SkipHooks: true}).Create(&users)
DB.Session(&gorm.Session{SkipHooks: true}).CreateInBatches(users, 100)

查询

GORM提供了First、Take、Last方法来从数据库中检索单个对象,它在查询数据库时添加了LIMIT 1条件,如果没有找到记录,它将返回错误ErrRecordNotFound。

First和Last方法将按主键顺序分别查找第一个和最后一个记录。只有当指向目标结构的指针作为参数传递给方法时,或者使用db.Model()指定模型时,它们才有效。此外,如果没有为相关模型定义主键,则模型将按第一个字段排序。

package main

import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
) type User struct {
gorm.Model
Name string
Age int8
} func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
if u.Name == "admin" {
fmt.Println("before create hint admin")
}
return
} func (u *User) AfterCreate(tx *gorm.DB) (err error) {
if u.Name == "admin" {
fmt.Println("after create hint admin")
}
return
} func (u *User) BeforeSave(tx *gorm.DB) (err error) {
if u.Name == "admin" {
fmt.Println("before save hint admin")
}
return
} func (u *User) AfterSave(tx *gorm.DB) (err error) {
if u.Name == "admin" {
fmt.Println("after save hint admin")
}
return
} func main() {
dsn := "root:123456@tcp(192.168.50.95:3306)/test?charset=utf8&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("data error")
} var user, user1, user2, user3 User
// 获取一条记录,下面相当于SELECT * FROM users ORDER BY id LIMIT 1;
db.First(&user)
fmt.Println("user=", user) // 获取一条记录, 下面相当于SELECT * FROM users LIMIT 1;
db.Take(&user1)
fmt.Println("user1=", user1) // 获取一条记录, 下面相当于SELECT * FROM users ORDER BY id DESC LIMIT 1;
db.Last(&user2)
fmt.Println(user2) // 指定表获取数据放入map
result := map[string]interface{}{}
db.Table("users").Take(&result)
fmt.Println("result=", result) db.First(&user3, "id = ?", 3)
fmt.Println("user3=", user3) var users, users1 []User
db.Find(&users, []int{1, 2, 3})
fmt.Println("users=", users) // 查询指定字段和条件
db.Select("name").Where("id > ?", 2).Find(&users1)
fmt.Println("users1=", users1)
}

其他详细查看官网

// limit ,SELECT * FROM users LIMIT 3;
db.Limit(3).Find(&users)
// OFFSET ,SELECT * FROM users OFFSET 3;
db.Offset(3).Find(&users)
// group by ,SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group"
db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)
// distinct
db.Distinct("name", "age").Order("name, age desc").Find(&results)
// join ,SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.id
db.Model(&User{}).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{})
// scan,将结果扫描到结构体中的工作方式类似于我们使用Find的方式
var result Result
db.Table("users").Select("name", "age").Where("name = ?", "Antonio").Scan(&result)
// 原始SQL
db.Raw("SELECT name, age FROM users WHERE name = ?", "Antonio").Scan(&result)

高级查询

  • 智能选择字段:GORM允许通过Select选择特定的字段,如果你经常在你的应用程序中使用这个,也许你想为API定义一个更小的结构体,它可以自动选择特定的字段。
type User struct {
ID uint
Name string
Age int
Gender string
// hundreds of fields
} type APIUser struct {
ID uint
Name string
} // 查询时自动选择“id”、“name”,SELECT `id`, `name` FROM `users` LIMIT 10
db.Model(&User{}).Limit(10).Find(&APIUser{})
  • 锁:GORM支持不同类型的锁。
// SELECT * FROM `users` FOR UPDATE
db.Clauses(clause.Locking{Strength: "UPDATE"}).Find(&users)
// SELECT * FROM `users` FOR SHARE OF `users`
db.Clauses(clause.Locking{
Strength: "SHARE",
Table: clause.Table{Name: clause.CurrentTable},
}).Find(&users)
// SELECT * FROM `users` FOR UPDATE NOWAIT
db.Clauses(clause.Locking{
Strength: "UPDATE",
Options: "NOWAIT",
}).Find(&users)
  • SubQuery:子查询可以嵌套在查询中,使用* GORM . db对象作为参数时,GORM可以生成子查询。
// SELECT * FROM "orders" WHERE amount > (SELECT AVG(amount) FROM "orders");
db.Where("amount > (?)", db.Table("orders").Select("AVG(amount)")).Find(&orders) // SELECT AVG(age) as avgage FROM `users` GROUP BY `name` HAVING AVG(age) > (SELECT AVG(age) FROM `users` WHERE name LIKE "name%")
subQuery := db.Select("AVG(age)").Where("name LIKE ?", "name%").Table("users")
db.Select("AVG(age) as avgage").Group("name").Having("AVG(age) > (?)", subQuery).Find(&results)
  • COUNT:获取匹配记录计数。
// SELECT count(1) FROM users WHERE name = 'jinzhu'; (count)
db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count)

修改

  • 保存所有字段
db.First(&user)
user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)
  • 更新字段
db.First(&user, "age = ?", 25)
db.Model(&user).Updates(User{Name: "lisinew1", Age: 44})
db.Model(&user).Updates(map[string]interface{}{"Name": "lisinew2", "Age": 35})
db.Model(&user).Update("age", 30)

删除

如果您的模型包含一个gorm。DeletedAt字段(包含在gorm.Model中),它将自动获得软删除能力!

// GORM允许使用带有内联条件的主键删除对象
db.Delete(&user, 1) // 假如Email's ID is `10`,DELETE from emails where id = 10;
db.Delete(&email) // 带附加条件删除
db.Where("name = ?", "jinzhu").Delete(&email) // 永久删除
db.Unscoped().Delete(&order)

原始SQL

  • 使用Scan查询Raw SQL
type Result struct {
ID int
Name string
Age int
} var result Result
db.Raw("SELECT id, name, age FROM users WHERE id = ?", 3).Scan(&result) var age int
db.Raw("SELECT SUM(age) FROM users WHERE role = ?", "admin").Scan(&age) var users []User
db.Raw("UPDATE users SET name = ? WHERE age = ? RETURNING id, name", "jinzhu", 20).Scan(&users)
  • 执行原始sql
// 执行删除表数据
db.Exec("DROP TABLE users")
// 执行带有表达式的更新
db.Exec("UPDATE users SET money = ? WHERE name = ?", gorm.Expr("money * ? + ?", 10000, 1), "jinzhu")
  • DryRun模式
// Session Configuration
type Session struct {
DryRun bool
PrepareStmt bool
NewDB bool
Initialized bool
SkipHooks bool
SkipDefaultTransaction bool
DisableNestedTransaction bool
AllowGlobalUpdate bool
FullSaveAssociations bool
QueryFields bool
Context context.Context
Logger logger.Interface
NowFunc func() time.Time
CreateBatchSize int
} // session mode
stmt := db.Session(&Session{DryRun: true}).First(&user, 1).Statement
println(stmt.SQL.String())
println(stmt.Vars)

  • Row & Rows
// row
row := db.Table("users").Where("name = ?", "jinzhu").Select("name", "age").Row()
row.Scan(&name, &age) row := db.Raw("select name, age, email from users where name = ?", "jinzhu").Row()
row.Scan(&name, &age, &email) // rows
rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows()
defer rows.Close()
for rows.Next() {
rows.Scan(&name, &age, &email) // do something
} rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows()
defer rows.Close()
for rows.Next() {
rows.Scan(&name, &age, &email) // do something
}

事务

GORM在事务内部执行写(创建/更新/删除)操作以确保数据一致性,如果不需要,可以在初始化时禁用它,之后将获得大约30%以上的性能提升。流程如下:

func CreateAnimals(db *gorm.DB) error {
// 注意,在事务中使用tx作为数据库句柄
tx := db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}() if err := tx.Error; err != nil {
return err
} if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
tx.Rollback()
return err
} if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
tx.Rollback()
return err
} return tx.Commit().Error
}

转换

  • ID为主键:默认情况下,GORM使用名称为ID的字段作为表的主键。可以通过实现Tabler接口来更改默认表名,例如:
// 可以使用标记primaryKey将其他字段设置为主键
type Animal struct {
ID int64
UUID string `gorm:"primaryKey"`
Name string
Age int64
}
  • 复数表名:GORM将结构名复数化为snake_cases作为表名,对于结构User,它的表名按照约定是users
// 转换表名为my_users
func (User) TableName() string {
return "my_users"
}
  • GORM允许用户通过覆盖默认的NamingStrategy来更改默认的命名约定,该策略用于构建TableName, ColumnName, JoinTableName, RelationshipFKName, CheckerName, IndexName
type Animal struct {
AnimalID int64 `gorm:"column:beast_id"` // 将列名设置为 `beast_id`
Birthday time.Time `gorm:"column:day_of_the_beast"` // 将列名设置为 `day_of_the_beast`
Age int64 `gorm:"column:age_of_the_beast"` // 将列名设置为 `age_of_the_beast`
}

分片

Sharding 是一个高性能的 Gorm 分表中间件。它基于 Conn 层做 SQL 拦截、AST 解析、分表路由、自增主键填充,带来的额外开销极小。对开发者友好、透明,使用上与普通 SQL、Gorm 查询无差别,只需要额外注意一下分表键条件。 为您提供高性能的数据库访问。

功能特点

  • 非侵入式设计, 加载插件,指定配置,既可实现分表。
  • 轻快, 非基于网络层的中间件,像 Go 一样快
  • 支持多种数据库。 PostgreSQL 已通过测试,MySQL 和 SQLite 也在路上。
  • 多种主键生成方式支持(Snowflake, PostgreSQL Sequence, 以及自定义支持)Snowflake 支持从主键中确定分表键。

配置分片中间件,注册想要分片的表

import (
"fmt" "gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/sharding"
) dsn := "postgres://localhost:5432/sharding-db?sslmode=disable"
db, err := gorm.Open(postgres.New(postgres.Config{DSN: dsn})) db.Use(sharding.Register(sharding.Config{
ShardingKey: "user_id",
NumberOfShards: 64,
PrimaryKeyGenerator: sharding.PKSnowflake,
}, "orders").Register(sharding.Config{
ShardingKey: "user_id",
NumberOfShards: 256,
PrimaryKeyGenerator: sharding.PKSnowflake,
// 对于show up give notifications, audit_logs表使用相同的分片规则。
}, Notification{}, AuditLog{}))

序列化

序列化器是一个可扩展的接口,允许自定义如何使用databasae序列化和反序列化数据。

package main

import (
"database/sql/driver"
"encoding/json"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
) type Profile struct {
Email string `json:"email"`
Mobile string `json:"mobile"`
} type User struct {
gorm.Model
Name string `json:"name"`
Age int8 `json:"age"`
Profile Profile `json:"profile" gorm:"type:json;comment:'个人信息'"`
} // 转换表名
func (User) TableName() string {
return "new_users"
} // Value 存储数据的时候转换为字符串
func (t Profile) Value() (driver.Value, error) {
return json.Marshal(t)
} // Scan 读取数据的时候转换为json
func (t *Profile) Scan(value interface{}) error {
return json.Unmarshal(value.([]byte), &t)
} func main() {
dsn := "root:123456@tcp(mysqlserver8:3306)/test?charset=utf8&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("data error")
}
db.AutoMigrate(&User{}) user := User{
Name: "刘海",
Age: 23,
Profile: Profile{
Email: "test1@qq.com",
Mobile: "18822334455",
},
}
db.Create(&user) var user1 User
db.Debug().Where("profile->'$.mobile'=(?)", "18822334455").First(&user1)
fmt.Println(user1)
}

查看mysql已有新创建的new_users数据库和对应的数据

  • 本人博客网站IT小神 www.itxiaoshen.com

Go开源世界主流成熟ORM框架gorm实践分享的更多相关文章

  1. 目前的.NET(C#)世界里,主流的ORM框架

    推荐一些常用的asp.net ORM框架 SqlSugar (国内) Dos.ORM (国内) Chloe (国内) StackExchange/Dapper (国外) Entity Framewor ...

  2. .NET(C#)主流的ORM框架

    .NET(C#)主流ORM总揽 SqlSugar (国内) Dos.ORM (国内) Chloe (国内) StackExchange/Dapper (国外) Entity Framework (EF ...

  3. .NET(C#)有哪些主流的ORM框架,SqlSugar,Dapper,EF还是...

    前言 在以前的一篇文章中,为大家分享了<什么是ORM?为什么用ORM?浅析ORM的使用及利弊>.那么,在目前的.NET(C#)的世界里,有哪些主流的ORM,SqlSugar,Dapper, ...

  4. Golang 入门系列(十二)ORM框架gorm

    之前在已经介绍了用的github.com/go-sql-driver/mysql 访问数据库,不太了解的可以看看之前的文章 https://www.cnblogs.com/zhangweizhong/ ...

  5. Go ORM框架 - GORM 踩坑指南

    今天聊聊目前业界使用比较多的 ORM 框架:GORM.GORM 相关的文档原作者已经写得非常的详细,具体可以看这里,这一篇主要做一些 GORM 使用过程中关键功能的介绍,GORM 约定的一些配置信息说 ...

  6. Node.js ORM 框架 sequelize 实践

    最近在做团队的一个内部系统,这次使用的nodejs web框架是团队统一的hapi.js,而数据库依然是mysql,ORM 框架选用有着6000+ stars 的 sequelize.js,hapi- ...

  7. Go orm框架gorm学习

    之前咱们学习过原生的Go连接MYSQL的方法,使用Go自带的"database/sql"数据库连接api,"github.com/go-sql-driver/mysql& ...

  8. 《Andorid开源》greenDao 数据库orm框架

       一 前言:以前没用框架写Andorid的Sqlite的时候就是用SQLiteDatabase ,SQLiteOpenHelper ,SQL语句等一些东西,特别在写SQL语句来进行 数据库操作的时 ...

  9. Chloe.ORM框架应用实践

    Chloe.ORM 是国人开发的一款数据库访问组件,很是简单易用.目前支持四种主流数据库:SqlServer.MySQL.Oracle,以及Sqlite,作者为这四种数据库划分出了各自对应的组件程序集 ...

  10. [Android]Android端ORM框架——RapidORM(v1.0)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4748077.html  Android上主流的ORM框架有很多 ...

随机推荐

  1. Thinkpad键盘操作手册与驱动 thinkpad蓝牙键盘KT-1525 manual

    thinklife mechanical keyboard tk500_win10.exe https://pan.baidu.com/s/1pQM3RcgV-phugew6vcKDLw

  2. 什么是Markdown

    什么是markdown? Markdown是一种轻量级标记语言,它允许人们使用已读一些的纯文本格式编写文档,然后转换成有效的XHTML(或者HTML)文档.这种语言吸收了很多在电子邮件中已有的纯文本标 ...

  3. Mybatisplus----DML编程控制

    乐观锁 (1)业务并发现象带来的问题:秒杀 执行: 1.在类对象中添加version属性,在数据库表中添加version字段(默认值为1) package com.itheima.domain; im ...

  4. 简单入门echart方法

    图表用echart, 然后前端的 HTML 跟 nodejs  , nodejs 去调用 后端PHP的接口 链接:https://www.jianshu.com/p/1f2c37c5c02f 官网:h ...

  5. Vue-router与hash与history区别

    vue-router 基本使用   路由,其实就是指向的意思,当我点击页面上的home按钮时,页面中就要显示home的内容,如果点击页面上的about 按钮,页面中就要显示about 的内容.Home ...

  6. crontab使用说明【一文搞懂Linux定时任务Crontab】

    1.简介 cron是一个在后台运行调度的守护进程,而crontab是一个设置cron的工具.cron调度的是/etc/crontab文件. 2.centos安装crontab yum install ...

  7. 计网学习笔记七 IP protocol basic

    在这一节讲了IP协议的基本内容:包括IPv4提供的操作.数据报在IPv4下是怎么样的结构.数据报是怎样切片发送的.IPv4的编址方式有什么--IPv6在下一节讲网络层协议簇时细讲. IPv4协议的具体 ...

  8. Java设计模式 —— 桥接模式

    10 桥接模式 10.1 桥接模式概述 Bridge Pattern: 将抽象部分与它的实现部分解耦,使得两者都能够独立变化. 桥接模式是一种很实用的结构型模式,如果系统中某个类存在两个独立变化的维度 ...

  9. 全网最详细中英文ChatGPT-GPT-4示例文档-智能多功能学习机从0到1快速入门——官网推荐的48种最佳应用场景(附python/node.js/curl命令源代码,小白也能学)

    目录 Introduce 简介 setting 设置 Prompt 提示 Sample response 回复样本 API request 接口请求 python接口请求示例 node.js接口请求示 ...

  10. 最新版本 Stable Diffusion 开源 AI 绘画工具之图生图进阶篇

    目录 图生图基本参数 图生图(img2img) 涂鸦绘制(Sketch) 局部绘制(Inpaint) 涂鸦蒙版(Inpaint sketch) 上传蒙版(Inpaint upload) 图生图基本参数 ...