Skip to content

shenghui0779/yiigo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

yiigo

golang GitHub release pkg.go.dev Apache 2.0 license

一个好用的轻量级 Go 开发通用库。如果你不喜欢过度封装的重量级框架,这个库可能是个不错的选择 😊

Features

  • 支持 MySQL
  • 支持 PostgreSQL
  • 支持 SQLite3
  • 支持 MongoDB
  • 支持 Redis
  • 支持 NSQ
  • SQL使用 sqlx
  • ORM推荐 ent
  • 日志使用 zap
  • gRPC Client 连接池
  • 轻量的 SQL Builder
  • 环境配置使用 dotenv,支持(包括 k8s configmap)热加载
  • 实用的辅助方法,包含:http、cypto、date、IP、validator、version compare 等

Installation

go get -u github.com/shenghui0779/yiigo

Usage

ENV

  • load
// 默认加载当前目录下的`.env`文件
yiigo.LoadEnv()

// 加载指定配置文件
yiigo.LoadEnv(yiigo.WithEnvFile("mycfg.env"))

// 热加载
yiigo.LoadEnv(yiigo.WithEnvWatcher(func(e fsnotify.Event) {
    fmt.Println(e.String())
}))
  • .env
ENV=dev
  • usage
fmt.Println(os.Getenv("ENV"))
// output: dev

DB

  • register
yiigo.Init(
    yiigo.WithMySQL(yiigo.Default, &yiigo.DBConfig{
        DSN: "dsn",
        Options: &yiigo.DBOptions{
            MaxOpenConns:    20,
            MaxIdleConns:    10,
            ConnMaxLifetime: 10 * time.Minute,
            ConnMaxIdleTime: 5 * time.Minute,
        },
    }),

    yiigo.WithMySQL("other", &yiigo.DBConfig{
        DSN: "dsn",
        Options: &yiigo.DBOptions{
            MaxOpenConns:    20,
            MaxIdleConns:    10,
            ConnMaxLifetime: 10 * time.Minute,
            ConnMaxIdleTime: 5 * time.Minute,
        },
    }),
)
  • sqlx
// default db
yiigo.DB().Get(&User{}, "SELECT * FROM user WHERE id = ?", 1)

// other db
yiigo.DB("other").Get(&User{}, "SELECT * FROM user WHERE id = ?", 1)
  • ent
import "<your_project>/ent"

// default driver
client := ent.NewClient(ent.Driver(yiigo.EntDriver()))

// other driver
client := ent.NewClient(ent.Driver(yiigo.EntDriver("other")))

MongoDB

// register
yiigo.Init(
    yiigo.WithMongo(yiigo.Default, "dsn"),
    yiigo.WithMongo("other", "dsn"),
)

// default mongodb
yiigo.Mongo().Database("test").Collection("numbers").InsertOne(context.Background(), bson.M{"name": "pi", "value": 3.14159})

// other mongodb
yiigo.Mongo("other").Database("test").Collection("numbers").InsertOne(context.Background(), bson.M{"name": "pi", "value": 3.14159})

Redis

// register
yiigo.Init(
    yiigo.WithRedis(yiigo.Default, &yiigo.RedisConfig{
        Addr: "addr",
        Options: &yiigo.RedisOptions{
            ConnTimeout:  10 * time.Second,
            ReadTimeout:  10 * time.Second,
            WriteTimeout: 10 * time.Second,
            PoolSize:     10,
            IdleTimeout:  5 * time.Minute,
        },
    }),

    yiigo.WithRedis("other", &yiigo.RedisConfig{
        Addr: "addr",
        Options: &yiigo.RedisOptions{
            ConnTimeout:  10 * time.Second,
            ReadTimeout:  10 * time.Second,
            WriteTimeout: 10 * time.Second,
            PoolSize:     10,
            IdleTimeout:  5 * time.Minute,
        },
    }),
)

// default redis
conn, err := yiigo.Redis().Get(context.Background())

if err != nil {
    log.Fatal(err)
}

defer yiigo.Redis().Put(conn)

conn.Do("SET", "test_key", "hello world")

// other redis
conn, err := yiigo.Redis("other").Get(context.Background())

if err != nil {
    log.Fatal(err)
}

defer yiigo.Redis("other").Put(conn)

conn.Do("SET", "test_key", "hello world")

Logger

// register
yiigo.Init(
    yiigo.WithLogger(yiigo.Default, yiigo.LoggerConfig{
        Filename: "filename",
        Options: &yiigo.LoggerOptions{
            Stderr: true,
        },
    }),

    yiigo.WithLogger("other", yiigo.LoggerConfig{
        Filename: "filename",
        Options: &yiigo.LoggerOptions{
            Stderr: true,
        },
    }),
)

// default logger
yiigo.Logger().Info("hello world")

// other logger
yiigo.Logger("other").Info("hello world")

gRPC Pool

// create pool
pool := yiigo.NewGrpcPool(&yiigo.GrpcPoolConfig{
    Dialer: func() (*grpc.ClientConn, error) {
        return grpc.DialContext(context.Background(), "target",
            grpc.WithInsecure(),
            grpc.WithBlock(),
            grpc.WithKeepaliveParams(keepalive.ClientParameters{
                Time:    time.Second * 30,
                Timeout: time.Second * 10,
            }),
        )
    },
    Options: &yiigo.PoolOptions{
        PoolSize:    10,
        IdleTimeout: 5 * time.Minute,
    },
})

// use pool
conn, err := pool.Get(context.Background())

if err != nil {
    return err
}

defer pool.Put(conn)

// coding...

HTTP

// default client
ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
yiigo.HTTPGet(ctx, "URL")

// new client
client := yiigo.NewHTTPClient(*http.Client)

ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
client.Do(ctx, http.MethodGet, "URL", nil)

SQL Builder

😊 为不想手写SQL的你生成SQL语句,用于 sqlx 的相关方法;

⚠️ 作为辅助方法,目前支持的特性有限,复杂的SQL(如:子查询等)还需自己手写

builder := yiigo.NewMySQLBuilder()
// builder := yiigo.NewSQLBuilder(yiigo.MySQL)
  • Query
ctx := context.Background()

builder.Wrap(
    yiigo.Table("user"),
    yiigo.Where("id = ?", 1),
).ToQuery(ctx)
// SELECT * FROM user WHERE id = ?
// [1]

builder.Wrap(
    yiigo.Table("user"),
    yiigo.Where("name = ? AND age > ?", "shenghui0779", 20),
).ToQuery(ctx)
// SELECT * FROM user WHERE name = ? AND age > ?
// [shenghui0779 20]

builder.Wrap(
    yiigo.Table("user"),
    yiigo.WhereIn("age IN (?)", []int{20, 30}),
).ToQuery(ctx)
// SELECT * FROM user WHERE age IN (?, ?)
// [20 30]

builder.Wrap(
    yiigo.Table("user"),
    yiigo.Select("id", "name", "age"),
    yiigo.Where("id = ?", 1),
).ToQuery(ctx)
// SELECT id, name, age FROM user WHERE id = ?
// [1]

builder.Wrap(
    yiigo.Table("user"),
    yiigo.Distinct("name"),
    yiigo.Where("id = ?", 1),
).ToQuery(ctx)
// SELECT DISTINCT name FROM user WHERE id = ?
// [1]

builder.Wrap(
    yiigo.Table("user"),
    yiigo.LeftJoin("address", "user.id = address.user_id"),
    yiigo.Where("user.id = ?", 1),
).ToQuery(ctx)
// SELECT * FROM user LEFT JOIN address ON user.id = address.user_id WHERE user.id = ?
// [1]

builder.Wrap(
    yiigo.Table("address"),
    yiigo.Select("user_id", "COUNT(*) AS total"),
    yiigo.GroupBy("user_id"),
    yiigo.Having("user_id = ?", 1),
).ToQuery(ctx)
// SELECT user_id, COUNT(*) AS total FROM address GROUP BY user_id HAVING user_id = ?
// [1]

builder.Wrap(
    yiigo.Table("user"),
    yiigo.Where("age > ?", 20),
    yiigo.OrderBy("age ASC", "id DESC"),
    yiigo.Offset(5),
    yiigo.Limit(10),
).ToQuery(ctx)
// SELECT * FROM user WHERE age > ? ORDER BY age ASC, id DESC LIMIT ? OFFSET ?
// [20, 10, 5]

wrap1 := builder.Wrap(
    Table("user_1"),
    Where("id = ?", 2),
)

builder.Wrap(
    Table("user_0"),
    Where("id = ?", 1),
    Union(wrap1),
).ToQuery(ctx)
// (SELECT * FROM user_0 WHERE id = ?) UNION (SELECT * FROM user_1 WHERE id = ?)
// [1, 2]

builder.Wrap(
    Table("user_0"),
    Where("id = ?", 1),
    UnionAll(wrap1),
).ToQuery(ctx)
// (SELECT * FROM user_0 WHERE id = ?) UNION ALL (SELECT * FROM user_1 WHERE id = ?)
// [1, 2]

builder.Wrap(
    Table("user_0"),
    WhereIn("age IN (?)", []int{10, 20}),
    Limit(5),
    Union(
        builder.Wrap(
            Table("user_1"),
            Where("age IN (?)", []int{30, 40}),
            Limit(5),
        ),
    ),
).ToQuery(ctx)
// (SELECT * FROM user_0 WHERE age IN (?, ?) LIMIT ?) UNION (SELECT * FROM user_1 WHERE age IN (?, ?) LIMIT ?)
// [10, 20, 5, 30, 40, 5]
  • Insert
ctx := context.Background()

type User struct {
    ID     int    `db:"-"`
    Name   string `db:"name"`
    Age    int    `db:"age"`
    Phone  string `db:"phone,omitempty"`
}

builder.Wrap(Table("user")).ToInsert(ctx, &User{
    Name: "yiigo",
    Age:  29,
})
// INSERT INTO user (name, age) VALUES (?, ?)
// [yiigo 29]

builder.Wrap(yiigo.Table("user")).ToInsert(ctx, yiigo.X{
    "name": "yiigo",
    "age":  29,
})
// INSERT INTO user (name, age) VALUES (?, ?)
// [yiigo 29]
  • Batch Insert
ctx := context.Background()

type User struct {
    ID     int    `db:"-"`
    Name   string `db:"name"`
    Age    int    `db:"age"`
    Phone  string `db:"phone,omitempty"`
}

builder.Wrap(Table("user")).ToBatchInsert(ctx, []*User{
    {
        Name: "shenghui0779",
        Age:  20,
    },
    {
        Name: "yiigo",
        Age:  29,
    },
})
// INSERT INTO user (name, age) VALUES (?, ?), (?, ?)
// [shenghui0779 20 yiigo 29]

builder.Wrap(yiigo.Table("user")).ToBatchInsert(ctx, []yiigo.X{
    {
        "name": "shenghui0779",
        "age":  20,
    },
    {
        "name": "yiigo",
        "age":  29,
    },
})
// INSERT INTO user (name, age) VALUES (?, ?), (?, ?)
// [shenghui0779 20 yiigo 29]
  • Update
ctx := context.Background()

type User struct {
    Name   string `db:"name"`
    Age    int    `db:"age"`
    Phone  string `db:"phone,omitempty"`
}

builder.Wrap(
    Table("user"),
    Where("id = ?", 1),
).ToUpdate(ctx, &User{
    Name: "yiigo",
    Age:  29,
})
// UPDATE user SET name = ?, age = ? WHERE id = ?
// [yiigo 29 1]

builder.Wrap(
    yiigo.Table("user"),
    yiigo.Where("id = ?", 1),
).ToUpdate(ctx, yiigo.X{
    "name": "yiigo",
    "age":  29,
})
// UPDATE user SET name = ?, age = ? WHERE id = ?
// [yiigo 29 1]

builder.Wrap(
    yiigo.Table("product"),
    yiigo.Where("id = ?", 1),
).ToUpdate(ctx, yiigo.X{
    "price": yiigo.Clause("price * ? + ?", 2, 100),
})
// UPDATE product SET price = price * ? + ? WHERE id = ?
// [2 100 1]
  • Delete
ctx := context.Background()

builder.Wrap(
    yiigo.Table("user"),
    yiigo.Where("id = ?", 1),
).ToDelete(ctx)
// DELETE FROM user WHERE id = ?
// [1]

builder.Wrap(Table("user")).ToTruncate(ctx)
// TRUNCATE user

Documentation

Enjoy 😊