Git Product home page Git Product logo

orm's Introduction

orm Go Report Card codecov PkgGoDev Go version

目前内置了对以下数据库的支持:

数据库 驱动 状态
mysql mysql github.com/go-sql-driver/mysql Mysql
mariadb mysql github.com/go-sql-driver/mysql Mariadb
sqlite3 sqlite3 github.com/mattn/go-sqlite3 Sqlite3
sqlite3 sqlite modernc.org/sqlite Sqlite
postgres postgres github.com/lib/pq Postgres

理论上 <github.com/jackc/pgx/v4/stdlib> 也是可用于 postgres,不过其驱动的注册名称为 pgx。

sqlite 为纯 Go 代码编写,如果涉及到交叉编译的可以采用此驱动,会很方便。

其它数据库,用户可以通过实现 Dialect 接口,来实现相应的支持。 如果用到了 check 约束,则需要 mysql > 8.0.19、mariadb > 10.2.1, mysql 的 MyISAM 是不支持外键约束的。

初始化

默认情况下,orm 包并不会加载任何数据库的实例。所以想要用哪个数据库,需要手动初始化:

import (
    github.com/issue9/orm/v6/dialect  // sqlite3 的 dialect 声明在此处
    _ github.com/mattn/go-sqlite3     // 加载数据库驱动
)

// 初始化一个 DB
db1 := orm.NewDB("./db1", dialect.Sqlite3("sqlite3"))

// 另一个 DB 实例
db2 := orm.NewDB("./db2", dialect.Sqlite3("sqlite3"))

占位符

SQL 语句可以使用 {} 包含一个关键字,使其它成为普通列名,如:

SELECT * FROM user WHERE {group}=1

在实际执行时,相关的占位符就会被替换成与当前环境想容的实例, 如在数据库为 mysql 时,会被替换成以下语句,然后再执行:

 SELECT * FROM user WHERE `group`=1

DB.Query(),DB.Exec(),DB.Prepare().DB.Where() 及 Tx 与之对应的函数都可以使用占位符。

Model 不能指定占位符,它们默认总会使用占位符,且无法取消。

Model:

orm 包通过 struct tag 来描述 model 在数据库中的结构。大概格式如下:

type User struct {
    Id          int64      `orm:"name(id);ai;"`
    FirstName   string     `orm:"name(first_name);index(index_name)"`
    LastName    string     `orm:"name(first_name);index(index_name)"`

    // 此处group会自动加上引号,无须担心是否为关键字
    Group       string     `orm:"name(group)"`
}

func(u *User) TableName() string { return "user" }

// 通过 orm.ApplyModeler 接口,指定表的额外数据。若不需要,可不用实现该接口
func(u *User) ApplyModel(m *core.Model) error {
    m.Options["mysql_engine"] = []string{"innodb"}
    m.Options["mysql_charset"] = []string{"utf8"}
    return nil
}

目前支持以下的 struct tag:

name(fieldName):

指定当前字段在数据表中的名称,如果未指定,则和字段名相同。 只有可导出的字段才有效果。

len(l1, l2):

指定字段的长度。比如 mysql 中的int(5),varchar(255),double(1,2), 不支持该特性的数据,将会忽略该标签的内容,比如 sqlite3。 NOTE:字符串类型必须指定长度,若长度过大或是将长度设置了-1, 想使用类似于 TEXT 等不定长的形式表达。

如果是日期类型,则第一个可选参数表示日期精度。

nullable(true|false):

相当于定义表结构时的 NULL。

pk:

主键,支持联合主键,给多个字段加上 pk 的 struct tag 即可。

ai:

自增,若指定了自增列,则将自动取消其它的 pk 设置。无法指定起始值和步长。 可手动设置一个非零值来更改某条数据的 AI 行为。

unique(index_name):

唯一索引,支持联合索引,index_name 为约束名,会将 index_name 为一样的字段定义为一个联合索引。 在联合索引中,零值会被当作空值处理,当零值是一个有效的值时,应当使用 sql.NullInt 等类型替换 int 类型。

index(index_name):

普通的关键字索引,同 unique 一样会将名称相同的索引定义为一个联合索引。

occ(true|false)

当前列作为乐观锁字段。

作为乐观锁的字段,其值表示的是线上数据的值,在更新时,会自动给线上的值加 1。

default(value):

指定默认值。相当于定义表结构时的 DEFAULT。 当一个字段如果是个零值(reflect.Zero())时,将会使用它的默认值, 但是系统无法判断该零值是人为指定,还是未指定被默认初始化零值的, 所以在需要用到零值的字段,最好不要用 default 的 struct tag。

fk(fk_name,refTable,refColName,updateRule,deleteRule):

定义物理外键,最少需要指定 fk_name、refTable 和 refColName 三个值。分别对应约束名, 引用的表和引用的字段,updateRule,deleteRule,在不指定的情况下,使用数据库的默认值。

接口:

  • TableNamer 指定表名;
  • ApplyModeler 通过 ApplyModeler 接口可以指定一些表级别的属性值;
  • AfterFetcher 在拉到数据之后,对该对象执行的一些额外操作。如果需要根据字段做额外工作的,可以使用该接口;
  • BeforeInserter 在执行插入之前,需要执行的操作;
  • BeforeUpdater 在执行更新之前,需要执行的操作;

约束名:

index、unique、check 和 fk 都是可以指定约束名的,在当前数据库中, 约束名必须是唯一的,即便是不同类型的约束,比如已经有一个 unique 的约束名叫作 name, 那么其它类型的约束,就不能再取这个名称了。

如何使用:

Create:

可以通过 DB.Create() 或是 Tx.Create() 创建一张表。

// 创建表
db.Create(&User{})
Update:
// 将 id 为 1 的记录的 FirstName 更改为 abc;对象中的零值不会被提交。
db.Update(&User{Id:1,FirstName:"abc"})
sqlbuilder.Update(db).Table("table").Where("id=?",1).Set("FirstName", "abc").Exec()
Delete:
// 删除 id 为 1 的记录
e.Delete(&User{Id:1})
sqlbuilder.Delete(e).Table("table").Where("id=?",1).Exec()
Insert:
// 插入一条数据
db.Insert(&User{Id:1,FirstName:"abc"})
// 一次性插入多条数据
tx.InsertMany(&User{Id:1,FirstName:"abc"},&User{Id:1,FirstName:"abc"})
Select:
// 导出 id=1 的数据
_,err := sqlbuilder.Select(e, e.Dialect()).Select("*").From("{table}").Where("id=1").QueryObj(obj)
// 导出 id 为 1 的数据,并回填到 user 实例中
user := &User{Id:1}
err := e.Select(u)
Query/Exec:
// Query 返回参数与 sql.Query 是相同的
sql := "select * from tbl_name where id=?"
rows, err := e.Query(sql, []interface{}{5})
// Exec 返回参数与 sql.Exec 是相同的
sql = "update tbl_name set name=? where id=?"
r, err := e.Exec(sql, []interface{}{"name1", 5})
多表查询
type order struct {
    ID int `orm:"name(id);ai"`
    UID int `orm:"name(uid)"`
    User *User `orm:"name(u)"` // sql 语句的 u. 开头的列导入到此对象中
}

sql := "SELECT o.*, u.id AS 'u.id',u.name AS 'u.name' FROM orders AS o LEFT JOIN  users AS u ON o.uid= u.id where id=?"

// 以下查询会将 u.name 和 u.id 导入到 User 指向的对象中
data := []*order{}
rows,err := e.Query(sql, []interface{}{5})
r, err := fetch.Object(rows, &data)
InsertLastID:

像 postgres 之类的数据库,插入语句返回的 sql.Result 并不支持 LastInsertId() 所以得通过其它方式得到该 ID,可以直接尝试使用 InsertLastID 来代码 Insert 操作。

lastID,err := db.InsertLastID(&User{FirstName:"abc"})

事务:

默认的 DB 是不支持事务的,若需要事务支持,则需要调用 DB.Begin() 返回事务对象 Tx,当然并不是所有的数据库都支持事务操作的。 Tx 拥有一组与 DB 相同的接口。

测试

可通过运行以下脚本执行测试:

go test ./... -dbs=sqlite3,sqlite3

性能测试:

go test -bench=. -dbs=sqlite3,sqlite3

版权

本项目采用 MIT 开源授权许可证,完整的授权说明可在 LICENSE 文件中找到。

orm's People

Contributors

8ugmak1r avatar caixw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

orm's Issues

not enough arguments in call to orm.NewDB

db1,err := orm.NewDB("./db1", "prefix_", dialect.Sqlite3("sqlite3"))

test.go:13:21: not enough arguments in call to orm.NewDB
have (string, string, core.Dialect)
want (string, string, string, orm.Dialect)

db2.Query sql: converting argument $1 type: unsupported type []interface {}, a slice of interface panic: runtime error: invalid memory address or nil pointer dereference

参考文档中 [Query/Exec:] 部分的例子代码

package main

import (
	"fmt"
	"github.com/issue9/orm/v5" // sqlite3    dialect
	_ "github.com/issue9/orm/v5/core"
	"github.com/issue9/orm/v5/dialect"
	"github.com/issue9/orm/v5/fetch"
	_ "github.com/issue9/orm/v5/sqlbuilder"
	_ "github.com/mattn/go-sqlite3"
	_ "time"
	//"github.com/gorilla/sessions"
)

type User struct {
	Id   int64  `orm:"name(id);ai;"`
	Name string `orm:"name(name);`
}

func (c *User) TableName() string { return "User" }

func main() {
	db2, err := orm.NewDB("./orm.db", dialect.Sqlite3("sqlite3"))

db2.Create(&User{})

	fmt.Println(err)
	azer := &User{
		Name: "issue9",
	}

	ret, err := db2.Insert(azer)

	fmt.Println(ret, err)

	data := []*User{}
	sql := "select * from user where id >=?"
	rows, err := db2.Query(sql, []interface{}{1})  
	fmt.Println(rows, err)
	r, err := fetch.Object(true, rows, &data)
	fmt.Println(r, err)
	for k, v := range data {
		fmt.Println(k, v.Name)
	}
}

报错
sql: converting argument $1 type: unsupported type []interface {}, a slice of interface
panic: runtime error: invalid memory address or nil pointer dereference

	rows, err := db2.Query(sql, 1)   

返回有纪录 ,但是没有字段值

为bytes.Buffer建一个缓存池

目前需要通过频繁的创建bytes.Buffer来生成SQL语句。
通过sync.Pool管理bytes.Buffer,在性能上是否会有所提升!

添加对子对象的支持

比如查询以下语句:

select a.*, user.name as u.name, account.count as u.count from ...

导出到以下结构体

type Info struct {
    // 其它字段定义
    User *User `orm:"name(u)"`
}

type User struct {
    Name string `orm:"name(name)"`
    Account int `orm:"name(account)`
}

info := &info{}
sql.QueryObj(info)

查询出为的 info 对象,应该将查询语句的 u. 开头的字段自初始化 Info.User 中的对象。

添加对数组的支持

采用 JSON 字符串保存数组;或是以特殊符号分隔的字符串保存,符号由 struct tag 中指定。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.