开源Golang ORM框架Queryx:支持自动Schema管理

开源Golang ORM框架Queryx:支持自动Schema管理 Github 链接:GitHub - swiftcarrot/queryx: 面向 Golang 和 TypeScript 的 Schema-first 和类型安全的 ORM

安装:

curl -sf https://raw.githubusercontent.com/swiftcarrot/queryx/main/install.sh  | sh

schema.hcl

Queryx 使用 schema.hcl 来描述数据库。以下示例定义了数据库环境和模型:

database "db" {
  adapter = "postgresql"

  config "development" {
    url = "postgres://postgres:postgres@localhost:5432/blog_development?sslmode=disable"
  }

  config "production" {
    url = env("DATABASE_URL")
  }

  generator "client-golang" {}

  model "Post" {
    column "title" {
      type = string
    }
    column "content" {
      type = text
    }
  }
}

运行 queryx db:create 命令来创建 PostgreSQL 数据库,然后运行 queryx db:migrate 来自动创建数据库迁移文件和数据库结构。Queryx 的数据库模式管理建立在 Atlas 之上。

CRUD

运行 queryx g 会在 db 目录中生成相应的 ORM 代码。生成的代码基于数据库模式生成 Go 类型,除了 lib/pq 驱动外没有其他第三方依赖。我们希望自动生成的代码简洁且可读。

以下是一些 CRUD 操作的示例代码:

// 创建
newPost := c.ChangePost().SetTitle("post title")
post, err := c.QueryPost().Create(newPost)

// 读取
post, err := c.QueryPost().Find(1)
posts, err := c.QueryPost().Where(c.PostTitle.EQ("post title")).All()

// 更新
updatePost := c.ChangePost().SetTitle("new post title")
err := post.Update(updatePost)
updated, err := c.QueryPost().Where(c.PostTitle.EQ("post title")).UpdateAll(updatePost)

// 删除
err := post.Delete()
deleted, err := c.QueryPost().Where(c.PostTitle.EQ("post title")).DeleteAll()

关系

模型之间的关系,包括 belongs_tohas_onehas_many,也可以在 schema.hcl 中声明。例如:

model "User" {
  belongs_to "group" {}

  column "name" {
    type = string
  }
}

model "Group" {
  has_many "users" {}

  column "name" {
    type = string
  }
}

声明关系后,您可以使用生成的 preload 方法来避免 n+1 查询。例如:

users, err := c.QueryUser().PreloadGroup().All()
// users[0].Groups

groups, err := c.QueryGroup().PreloadUsers().All()
// groups[0].User

如果您熟悉 Rails,您会发现 Queryx 借鉴了 ActiveRecord 的许多设计理念。我们希望复现 ActiveRecord 的开发体验。有关 queryx 的更多用法,请参阅 README 文档,并欢迎在 issue、讨论和回复中交流。Queryx 目前处于测试阶段(v0),许多功能仍在开发中,例如 MySQL 支持。我们希望在未来的版本中持续改进开发体验。


更多关于开源Golang ORM框架Queryx:支持自动Schema管理的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于开源Golang ORM框架Queryx:支持自动Schema管理的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Queryx确实为Go的ORM生态带来了新的思路,特别是其Schema-first的设计理念和与Atlas的深度集成。以下是一些技术层面的分析和示例:

类型安全与代码生成

Queryx通过HCL定义Schema,然后生成类型安全的Go代码,这比基于反射的ORM有更好的编译时检查:

// 生成的类型安全查询示例
post, err := c.QueryPost().
    Where(c.PostTitle.EQ("post title")).
    Where(c.PostCreatedAt.GT(time.Now().AddDate(0, -1, 0))).
    First()

// 编译时会检查字段名和类型
// 错误的字段名会导致编译错误
// err := c.QueryPost().Where(c.PostNonExistentField.EQ("test")) // 编译错误

Schema迁移的原子性

基于Atlas的迁移系统提供了可靠的版本控制:

// 迁移文件示例(自动生成)
// migrations/20240101000001_create_posts.up.sql
CREATE TABLE posts (
    id SERIAL PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    content TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

// 回滚文件
// migrations/20240101000001_create_posts.down.sql
DROP TABLE IF EXISTS posts;

关联查询优化

Queryx的Preload机制通过JOIN或批量查询避免N+1问题:

// 预加载关联数据
posts, err := c.QueryPost().
    PreloadAuthor().  // 预加载作者
    PreloadComments(). // 预加载评论
    Where(c.PostPublished.EQ(true)).
    All()

// 生成的关联查询代码
type PostWithRelations struct {
    *Post
    Author   *User      `json:"author"`
    Comments []*Comment `json:"comments"`
}

事务处理

Queryx支持标准的事务操作:

// 事务示例
tx, err := c.Begin()
if err != nil {
    return err
}
defer tx.Rollback()

// 在事务中创建关联记录
post := tx.ChangePost().
    SetTitle("New Post").
    SetContent("Post content")
createdPost, err := tx.QueryPost().Create(post)

comment := tx.ChangeComment().
    SetPostID(createdPost.ID).
    SetContent("First comment")
_, err = tx.QueryComment().Create(comment)

return tx.Commit()

查询构建器

生成的查询构建器支持复杂的查询条件:

// 复杂查询示例
posts, err := c.QueryPost().
    Select(c.PostTitle, c.PostCreatedAt). // 选择特定字段
    Where(c.PostTitle.LIKE("%Go%")).
    Where(c.PostCreatedAt.Between(
        time.Now().AddDate(0, -1, 0),
        time.Now(),
    )).
    OrderBy(c.PostCreatedAt.Desc()).
    Limit(10).
    Offset(0).
    All()

// 统计查询
count, err := c.QueryPost().
    Where(c.PostPublished.EQ(true)).
    Count()

// 分组查询
type PostCount struct {
    Date  string `json:"date"`
    Count int    `json:"count"`
}
var results []PostCount
err := c.QueryPost().
    Select("DATE(created_at) as date", "COUNT(*) as count").
    GroupBy("DATE(created_at)").
    Scan(&results)

自定义类型支持

Queryx支持自定义类型映射:

// schema.hcl 中定义自定义类型
model "Product" {
    column "price" {
        type = decimal
    }
    
    column "metadata" {
        type = jsonb
    }
    
    column "tags" {
        type = string[]
    }
}
// Go中使用自定义类型
type ProductMetadata struct {
    SKU      string            `json:"sku"`
    Features map[string]string `json:"features"`
}

product := c.ChangeProduct().
    SetPrice(decimal.NewFromFloat(99.99)).
    SetMetadata(ProductMetadata{
        SKU: "PROD-001",
        Features: map[string]string{"color": "red"},
    }).
    SetTags([]string{"electronics", "gadget"})

created, err := c.QueryProduct().Create(product)

Queryx的Schema-first方法确实为Go ORM提供了新的范式,特别是在团队协作和数据库版本控制方面。其代码生成策略减少了运行时错误,但需要适应生成代码的工作流程。

回到顶部