golang高效管理SQL文件并简化数据库操作插件库Dotsql的使用

Golang高效管理SQL文件并简化数据库操作插件库Dotsql的使用

Dotsql是一个Golang库,用于更高效地管理SQL文件并简化数据库操作。它不是ORM,也不是查询构建器,而是一个帮助你将SQL文件集中管理并轻松使用的库。

安装

$ go get github.com/qustavo/dotsql

使用示例

1. 创建SQL文件

首先,你需要在SQL文件中定义查询,每个查询都需要有名称标签(--name:<some name>):

-- name: create-users-table
CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
    name VARCHAR(255),
    email VARCHAR(255)
);

-- name: create-user
INSERT INTO users (name, email) VALUES(?, ?)

-- name: find-users-by-email
SELECT id,name,email FROM users WHERE email = ?

-- name: find-one-user-by-email
SELECT id,name,email FROM users WHERE email = ? LIMIT 1

--name: drop-users-table
DROP TABLE users

2. 在Go代码中使用

// 获取数据库连接
db, err := sql.Open("sqlite3", ":memory:")

// 从文件加载SQL查询
dot, err := dotsql.LoadFromFile("queries.sql")

// 执行查询
res, err := dot.Exec(db, "create-users-table")
res, err := dot.Exec(db, "create-user", "User Name", "main@example.com")
rows, err := dot.Query(db, "find-users-by-email", "main@example.com")
row, err := dot.QueryRow(db, "find-one-user-by-email", "user@example.com")

stmt, err := dot.Prepare(db, "drop-users-table")
result, err := stmt.Exec()

3. 合并多个SQL文件

dot1, err := dotsql.LoadFromFile("queries1.sql")
dot2, err := dotsql.LoadFromFile("queries2.sql")
dot := dotsql.Merge(dot1, dot2)

文本插值

Dotsql支持类似text/template的文本插值功能:

-- name: count-users
SELECT count(*) FROM users {{if .exclude_deleted}}WHERE deleted IS NULL{{end}}

在Go代码中使用:

dotsql.WithData(map[string]any{"exclude_deleted": true}).Query(db, "count-users")

嵌入SQL文件

为了避免将SQL文件与二进制文件一起分发,你可以使用像gotic这样的工具将SQL文件嵌入到二进制文件中。

SQLX支持

如果需要sqlx支持,可以查看dotsqlx项目。

Dotsql提供了一种简洁的方式来管理SQL查询,使你的代码更加整洁,SQL查询更易于维护。通过将SQL与Go代码分离,你可以获得更好的可读性和可维护性。


更多关于golang高效管理SQL文件并简化数据库操作插件库Dotsql的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang高效管理SQL文件并简化数据库操作插件库Dotsql的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Dotsql: Golang中高效管理SQL文件的插件库

Dotsql是一个轻量级的Go库,专门用于管理SQL文件并简化数据库操作。它允许你将SQL查询与代码分离,保持代码整洁,同时提供便捷的方式来执行这些查询。

Dotsql的核心优势

  1. SQL与代码分离:将SQL语句存储在单独的.sql文件中
  2. 命名查询:为每个SQL语句命名,便于调用
  3. 预处理支持:自动处理参数化查询
  4. 轻量级:不引入复杂的ORM概念

安装Dotsql

go get github.com/gchaincl/dotsql

基本使用方法

1. 准备SQL文件

创建一个queries.sql文件:

-- name: create-users-table
CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT NOT NULL UNIQUE,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- name: insert-user
INSERT INTO users (name, email) VALUES (?, ?);

-- name: find-user-by-email
SELECT id, name, email, created_at FROM users WHERE email = ?;

-- name: find-all-users
SELECT id, name, email, created_at FROM users;

2. 加载SQL文件并执行查询

package main

import (
	"database/sql"
	"fmt"
	"log"

	_ "github.com/mattn/go-sqlite3"
	"github.com/gchaincl/dotsql"
)

func main() {
	// 打开数据库连接
	db, err := sql.Open("sqlite3", "./test.db")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// 加载SQL文件
	dot, err := dotsql.LoadFromFile("queries.sql")
	if err != nil {
		log.Fatal(err)
	}

	// 执行创建表的查询
	_, err = dot.Exec(db, "create-users-table")
	if err != nil {
		log.Fatal(err)
	}

	// 插入用户数据
	res, err := dot.Exec(db, "insert-user", "John Doe", "john@example.com")
	if err != nil {
		log.Fatal(err)
	}
	lastID, _ := res.LastInsertId()
	fmt.Printf("Inserted user with ID: %d\n", lastID)

	// 查询单个用户
	row := dot.QueryRow(db, "find-user-by-email", "john@example.com")
	var user struct {
		ID        int
		Name      string
		Email     string
		CreatedAt string
	}
	err = row.Scan(&user.ID, &user.Name, &user.Email, &user.CreatedAt)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Found user: %+v\n", user)

	// 查询所有用户
	rows, err := dot.Query(db, "find-all-users")
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	fmt.Println("All users:")
	for rows.Next() {
		var u struct {
			ID        int
			Name      string
			Email     string
			CreatedAt string
		}
		err = rows.Scan(&u.ID, &u.Name, &u.Email, &u.CreatedAt)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("- %+v\n", u)
	}
}

高级用法

1. 从字符串加载SQL

sql := `
-- name: count-users
SELECT COUNT(*) FROM users;
`

dot, err := dotsql.LoadFromString(sql)

2. 合并多个SQL文件

dot1, err := dotsql.LoadFromFile("queries1.sql")
dot2, err := dotsql.LoadFromFile("queries2.sql")

dot := dotsql.Merge(dot1, dot2)

3. 预处理语句

// 获取预处理语句
stmt, err := dot.Prepare(db, "insert-user")
defer stmt.Close()

// 执行预处理
_, err = stmt.Exec("Jane Doe", "jane@example.com")

4. 事务支持

tx, err := db.Begin()
defer tx.Rollback()

_, err = dot.Exec(tx, "insert-user", "Alice", "alice@example.com")
_, err = dot.Exec(tx, "insert-user", "Bob", "bob@example.com")

err = tx.Commit()

最佳实践

  1. 按功能模块组织SQL文件:例如users.sqlproducts.sql
  2. 使用有意义的查询名称:如get-user-by-id而非query1
  3. 添加注释:说明查询的用途和参数
  4. 版本控制SQL文件:与代码一起管理
  5. 测试SQL查询:为关键查询编写测试

性能考虑

Dotsql本身非常轻量,主要开销在于:

  1. 文件加载和解析(只在启动时一次)
  2. 预处理语句的创建

对于高性能场景,建议:

  • 在应用启动时加载所有SQL文件
  • 对频繁使用的查询使用Prepare缓存预处理语句

与其他库的比较

特性 Dotsql ORM (如GORM) 原生database/sql
SQL可见性
代码整洁度
灵活性
学习曲线
迁移复杂性

Dotsql在需要直接控制SQL但又想保持代码整洁的场景下表现最佳。

总结

Dotsql为Go开发者提供了一种优雅的方式来管理SQL查询,特别适合:

  • 需要精细控制SQL语句的项目
  • 希望保持代码整洁的团队
  • 需要将SQL与业务逻辑分离的架构

通过将SQL外部化,Dotsql使你的代码更易于维护,同时保留了直接使用SQL的灵活性和性能优势。

回到顶部