golang generic orm, base on sqlx

  • By 刀刀
  • Last update: Nov 29, 2022
  • Comments: 1

ggm

golang generic orm, base on sqlx

install

go get github.com/daodao97/ggm

usage

Below is an example which shows some common use cases for ggm. Check model_test.go for more usage.

init db

We can initialize some db resources commonly used by programs, like this

// map[conn_name]db_config
ggm.Init(map[string]*ggm.Config{
    "default": {
        DSN: "[email protected](127.0.0.1:3306)/ggm_test?&parseTime=true",
    },
})

Of course, we can also instantiate some temporary DB resources, like this

m := ggm.NewConn(&ggm.Config{
	DSN: "[email protected](127.0.0.1:3306)/ggm_test?&parseTime=true" 
})

data model

For example, we have a table with the following structure

CREATE TABLE `user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `status` tinyint(4) NOT NULL DEFAULT '0',
  `profile` varchar(200) NOT NULL,
  `is_deleted` tinyint(3) unsigned NOT NULL DEFAULT '0',
  `ctime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `mtime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

struct define

The structure model for this table is

type User struct {
	Id      int             `db:"id,pk"   json:"id"`
	Name    string          `db:"name"    json:"name"`
	Profile string          `db:"profile" json:"profile"`
	CTime   time.Time       `db:"ctime"   json:"ctime"`
}

func (u User) Table() string {
	return "user"
}

interface: Table() string the struct must implement.

struct field must have db tag, value is db field name.

set conn

If you are using a db resource that is not the default

func (u User) Conn() string {
	return "conn_name"
}

fake delete

If you have a field to mark fake delete

func (u User) FakeDeleteKey() string {
	return "is_deleted"
}

then, the delete sql will converted to update ${fakeDeleteKey} = 1 when we delete data

select sql will auto add ${fackDeleteKey} = 0, to filter deleted data.

select

m := ggm.New[*User]() // or ggm.New[User]() 

m.Select(ggm.WhereEq("id", 1))

detail of where condition see where condition

insert

user := &User{Name: "Seiya"}

// single insert
m.Insert(user)

// batch insert
m.Insert(user, user2, ...)

update

use primary key update

user := &User{Id: 1, Name: "Seiya!!!"}
m.Update(user)

with where condition

user := &User{Name: "Seiya!!!"}
m.Update(user, ggm.WhereEq("id", 1))

delete

m.Delete(ggm.WhereEq("id", 1))

where condition

m.Select(
    WhereEq("id", 1),
    WhereGt("age", 20),
    WhereLike("name", "dd"),
    WhereGroup(
        WhereEq("sex", 1),
        WhereOrEq("class", 2),
        WhereGroup(
            WhereEq("sex1", 1),
            WhereEq("class2", 2),
        ),
    ),
    OrderBy("id", DESC),
)

more example, checkout sql_test.go

data type

Json

If the value of field user.profile is json_string like {"skill":"Pegasus Ryuseiken"}

type User struct {
	Id      int                 `db:"id,pk"   json:"id"`
	Name    string              `db:"name"    json:"name"`
	Profile *ggm.Json[*Profile] `db:"profile" json:"profile"`
	CTime   time.Time           `db:"ctime"   json:"ctime"`
}

type Profile struct {
    Skill string `json:"skill"`
}

Profile{Skill: "xxx"} <==> '{"skill":"xxx"}'

Data can be automatically converted into struct for use by programs.

Time

type User struct {
	Id      int                 `db:"id,pk"   json:"id"`
	Name    string              `db:"name"    json:"name"`
	Profile *ggm.Json[*Profile] `db:"profile" json:"profile"`
	CTime   ggm.Time            `db:"ctime"   json:"ctime"`
}

when api response or json.Marshal

ctime : 2022-03-19T11:52:19Z => 2022-03-19 11:52:19

define yourself data type

Implement the following interfaces

type DataType[T any] interface {
	Value() (driver.Value, error)
	Scan(value any) error
	MarshalJSON() ([]byte, error)
	UnmarshalJSON(b []byte) error
	Get() T
}

Linked data

hasOne

one to one

type User struct {
	Id      int             `db:"id,pk"   json:"id"`
	Name    string          `db:"name"    json:"name"`
	Profile *Json[*Profile] `db:"profile" json:"profile"`
	Score   int             `db:"score"   json:"score" hasOne:"user_score:uid"`
	Score2  int             `db:"score2"  json:"score2" hasOne:"user_score:uid"`
}

hasMany

one to N

type User struct {
	Id      int             `db:"id,pk"   json:"id"`
	Name    string          `db:"name"    json:"name"`
	Profile *Json[*Profile] `db:"profile" json:"profile"`
	Logs    []*Log          `json:"logs"  hasMany:"user_log:uid"`
}

type Log struct {
	Message string `db:"message" json:"message"`
}

hasOne or hasMany tag token:

[conn.][database.]table:[local_key->]foreign_key

m.Select() will auto query the linked data into the struct.

Check model_test.go for detail.

Download

ggm.zip

Comments(1)

  • 1

    change model to Model

    so you can add this in global and init late

    var UserModol *ggm.Model[*dao.Users]
    

    NewClient allow create master/slave instance:

    master := *ggm.Model[*dao.Users].NewClient(`master`)
    slave := *ggm.Model[*dao.Users].NewClient(`slave`)