goooqo

package module
v0.3.1-pre Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 12, 2026 License: BSD-3-Clause-Clear Imports: 4 Imported by: 1

README

Sonar Stats Code Lines Coverage Status CI Go Report Card Go Reference

GoooQo

Introduction

GoooQo is a database access framework implemented in Go, based on OQM techniques. It relies entirely on objects to construct various database query statements, eliminating boilerplate code associated with traditional ORM frameworks, and assists developers in achieving automated database access operations.

The first three Os in the name GoooQo stands for the three major object concepts in the OQM technique:

  • Entity Object is used to map the static part in the CRUD statements, e.g. table name and column names;
  • Query Object is used to map the dynamic part in the CRUD statements, e.g. filter conditions, pagination and sorting clause;
  • View Object is used to map the static part in the complex query statements, e.g. table names, column names, group-by clause and joins.

Check this article for more details.

Check this demo to take a quick tour.

Product documentation: https://goooqo.docs.doyto.win/

Quick start

Init project

Use go mod init to init the project and add GoooQo dependency:

go get -u github.com/doytowin/goooqo/rdb

Init a database connection db and a transaction manager tm:

package main

import (
	"database/sql"
	"github.com/doytowin/goooqo/rdb"
	_ "github.com/mattn/go-sqlite3"
)

func main() {
	db, _ := sql.Open("sqlite3", "./test.db")
	tm := rdb.NewTransactionManager(db)
	//...
}

Create a data access interface

Suppose we have the following user table in test.db:

id name score memo deleted
1 Alley 80 Good false
2 Dave 75 Well false
3 Bob 60 false
4 Tim 92 Great true
5 Emy 100 Great false

We define an entity object, a query object and a database access interface for this table:

package main

import . "github.com/doytowin/goooqo/core"

type UserEntity struct {
	Int64Id
	Name    *string `json:"name"`
	Score   *int    `json:"score"`
	Memo    *string `json:"memo"`
	Deleted *bool   `json:"deleted"`
}

type UserQuery struct {
	PageQuery
	IdGt     *int64
	IdIn     *[]int64
	ScoreLt  *int
	MemoNull *bool
	MemoLike *string
	Deleted  *bool
	UserOr   *[]UserQuery

	ScoreLtAvg *UserQuery `subquery:"select avg(score) from t_user"`
	ScoreLtAny *UserQuery `subquery:"SELECT score FROM t_user"`
	ScoreLtAll *UserQuery `subquery:"SELECT score FROM t_user"`
	ScoreGtAvg *UserQuery `select:"avg(score)" from:"t_user"`

	Role *RoleQuery `entitypath:"user,role"`
	Perm *PermQuery `entitypath:"user,role,perm"`
}

var UserDataAccess TxDataAccess[UserEntity]

After establishing a database connection and creating the transaction manager tm, initialize the UserDataAccess interface to perform CRUD operations:

UserDataAccess = rdb.NewTxDataAccess[UserEntity](tm)

Query examples:

userQuery := UserQuery{ScoreLt: P(80)}
users, err := UserDataAccess.Query(ctx, userQuery)
// SQL="SELECT id, name, score, memo, deleted FROM t_user WHERE score < ?" args="[80]"

userQuery := UserQuery{PageQuery: PageQuery{Size: 20, Sort: "id,desc;score"}, MemoLike: P("Great")}
users, err := UserDataAccess.Query(ctx, userQuery)
// SQL="SELECT id, name, score, memo, deleted FROM t_user WHERE memo LIKE ? ORDER BY id DESC, score LIMIT 20 OFFSET 0" args="[Great]"

userQuery := UserQuery{IdIn: &[]int64{1, 4, 12}, Deleted: P(true)}
users, err := UserDataAccess.Query(ctx, userQuery)
// SQL="SELECT id, name, score, memo, deleted FROM t_user WHERE id IN (?, ?, ?) AND deleted = ?" args="[1 4 12 true]"

userQuery := UserQuery{UserOr: &[]UserQuery{{IdGt: P(int64(10)), MemoNull: P(true)}, {ScoreLt: P(80), MemoLike: P("Good")}}}
users, err := UserDataAccess.Query(ctx, userQuery)
// SQL="SELECT id, name, score, memo, deleted FROM t_user WHERE (id > ? AND memo IS NULL OR score < ? AND memo LIKE ?)" args="[10 80 Good]"

userQuery := UserQuery{ScoreGtAvg: &UserQuery{Deleted: P(true)}, ScoreLtAny: &UserQuery{}}
users, err := UserDataAccess.Query(ctx, userQuery)
// SQL="SELECT id, name, score, memo, deleted FROM t_user WHERE score > (SELECT avg(score) FROM t_user WHERE deleted = ?) AND score < ANY(SELECT score FROM t_user)" args="[true]"

For more CRUD examples, please refer to: https://goooqo.docs.doyto.win/api/crud

Code Generation

GoooQo provides a code generation tool, gooogen, which supports automatically generating dynamic query construction methods for query objects.

Install gooogen

Install the gooogen tool using the following command:

go install github.com/doytowin/goooqo/gooogen@latest
Add Generate Directive

Add the go:generate gooogen directive to the query object definition. For example:

//go:generate gooogen -type sql -o user_query_builder.go
type UserQuery struct {
    // ...
}
  • -type: (Optional) Specifies the type of query language to generate, e.g. sql.
  • -f: (Optional) Defines the name of the input file which contains a query object, e.g. user.go.
  • -o: (Optional) Defines the name of the generated file, e.g. user_query_builder.go.
Generate Code

Run the go generate command to generate the corresponding query construction methods in the specified file.

Transaction Examples

Use TransactionManager#StartTransaction to start a transaction, then manually commit or rollback the transaction:

tc, err := UserDataAccess.StartTransaction(ctx)
userQuery := UserQuery{ScoreLt: P(80)}
cnt, err := UserDataAccess.DeleteByQuery(tc, userQuery)
if err != nil {
	err = tc.Rollback()
	return 0, err
}
err = tc.Commit()
return cnt, err

Or use TransactionManager#SubmitTransaction to commit the transaction via callback function:

err := tm.SubmitTransaction(ctx, func(tc TransactionContext) (err error) {
	// transaction body
	return
})

License

This project is under the BSD 3-Clause Clear License.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Config = core.Config
View Source
var Connect = rdb.Connect
View Source
var Disconnect = rdb.Disconnect
View Source
var NewInt64Id = core.NewInt64Id
View Source
var NewIntId = core.NewIntId
View Source
var NewRdbAssociationService = rdb.NewRdbAssociationService
View Source
var NewTransactionManager = rdb.NewTransactionManager
View Source
var RegisterConverter = web.RegisterConverter

Functions

func BuildRestService

func BuildRestService[E Entity, Q Query](prefix string, dataAccess DataAccess[E])

func P added in v0.2.0

func P[T any](t T) *T

Types

type DataAccess

type DataAccess[E Entity] core.DataAccess[E]

func NewDataAccess

func NewDataAccess[E Entity](db rdb.Connection) DataAccess[E]

func NewTxDataAccess

func NewTxDataAccess[E Entity](tm core.TransactionManager) DataAccess[E]

type Entity

type Entity = core.Entity

type Int64Id

type Int64Id = core.Int64Id

type IntId

type IntId = core.IntId

type PageQuery

type PageQuery = core.PageQuery

type Query

type Query = core.Query

type TransactionManager

type TransactionManager = core.TransactionManager

Directories

Path Synopsis
core module
gen module
gooogen module
main module
mongodb module
rdb module
test module
web module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL