orm

package module
v1.0.0-beta2 Latest Latest
Warning

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

Go to latest
Published: Mar 9, 2022 License: MIT Imports: 10 Imported by: 0

README

GoDoc

Go Report Card Coverage Status

GolobbyORM

GoLobbyORM is a simple yet powerfull, fast, safe, customizable, type-safe database toolkit for Golang.

Table Of Contents

Features

  • Simple, type safe, elegant API with help of Generics
  • Minimum reflection usage, mostly at startup of application
  • No code generation
  • Query builder for various query types
  • Binding query results to entities
  • Supports relationship/Association types
    • HasMany
    • HasOne
    • BelongsTo
    • BelongsToMany (ManyToMany)
Introduction

GolobbyORM an object-relational mapper (ORM) that makes it enjoyable to interact with your database. When using GolobbyORM, each database table has a corresponding "Entity" that is used to interact with that table. In addition to retrieving records from the database table, GolobbyORM entities allow you to insert, update, and delete records from the table as well.

Creating a new Entity

Lets create a new Entity to represent User in our application.

package main

import "github.com/golobby/orm"

type User struct {
  ID       int64
  Name     string
  LastName string
  Email    string
}

func (u User) ConfigureEntity(e *orm.EntityConfigurator) {
    e.Table("users").Connection("default") // You can omit .Connection if you have only on connection.
}

func (u User) ConfigureRelations(r *orm.RelationConfigurator) {
	// we talk about relationships later
}

as you see our user entity is nothing else than a simple struct and two methods. Entities in GolobbyORM are implementations of Entity interface which defines two methods:

  • ConfigureEntity: configures table and database connection.
  • ConfigureRelations: configures relations that Entity has with other relations.
Conventions
Column names

GolobbyORM for each struct field(except slice, arrays, maps and other nested structs) assumes a respective column named using snake case syntax. if you want to have a custom column name you should specify it in entity struct.

package main
type User struct {
	Name string `orm:"column=username"` // now this field will be mapped to `username` column in sql database. 
}
Primary Key

GolobbyORM assumes that each entity has primary key named id, if you want to have a custom named primary key you need to specify it in entity struct.

package main
type User struct {
	PK int64 `orm:"pk=true"`
}
Initializing ORM

after creating our entities we need to initialize GolobbyORM.

package main

import "github.com/golobby/orm"

func main() {
  orm.Initialize(orm.ConnectionConfig{
    Name:             "default",
    Driver:           "sqlite3",
    ConnectionString: ":memory:",
    Entities:         []orm.Entity{&User{}},
  })
}

After this step we can start using ORM.

Fetching an entity from database

GolobbyORM makes it trivial to fetch entity from database using its primary key.

user, err := orm.Find[User](1)

orm.Find is a generic function that takes a generic parameter that specifies the type of Entity we want to query and it's primary key value. You can also use custom queries to get entities from database.


user, err := orm.Query[User]().Where("id", 1).One()
// also you can use `WherePK` which return
user, err := orm.Query[User]().WherePK(1).One()

GolobbyORM contains a powerful query builder which you can use to build Select, Update and Delete queries, but if you want to write a raw sql query you can.

users, err := orm.QueryRaw[User](`SELECT * FROM users`)
Saving entities or Insert/Update

GolobbyORM makes it easy to persist an Entity to the database using Save method, it's an UPSERT method, if the primary key field is not zero inside entity it will go for update query, otherwise it goes for insert.

// this will insert entity into the table
err := orm.Save(&User{Name: "Amirreza"}) // INSERT INTO users (name) VALUES (?) , "Amirreza"
//this will update entity with id = 1
orm.Save(&User{ID: 1, Name: "Amirreza2"}) // UPDATE users SET name=? WHERE id=?, "Amirreza2", 1

also you can do custom update queries using again query builder or raw sql as well.

res, err := orm.Query[User]().Set("name", "amirreza2").Where("id", 1).Update()

using raw sql

_, affected, err := orm.ExecRaw[User](`UPDATE users SET name=? WHERE id=?`, "amirreza", 1)
Deleting entities

It is also easy to delete entities from database.

err := orm.Delete(user)

you can also use query builder or raw sql.

_, affected, err := orm.Query[Post]().WherePK(1).Delete()

_, affected, err := orm.Query[Post]().Where("id", 1).Delete()

_, affected, err := orm.ExecRaw[Post](`DELETE FROM posts WHERE id=?`, 1)
Relationships

GolobbyORM makes it easy to have entities that have relationships with each other. As you have already seen in entity definition you have a ConfigureRelations method which let's you define relations of an Entity.

HasMany
type Post struct {}

func (p Post) ConfigureRelations(r *orm.RelationConfigurator) {
    r.HasMany(&Comment{}, orm.HasManyConfig{})
}

As you can see we are defining a Post entity which has a HasMany relation with Comment. You can configure how GolobbyORM queries HasMany relation with orm.HasManyConfig object, by default it will infer all fields for you. now you can use this relationship anywhere in your code.

comments, err := orm.HasMany[Comment](post)
HasOne
type Post struct {}

func (p Post) ConfigureRelations(r *orm.RelationConfigurator) {
    r.HasOne(&HeaderPicture{}, orm.HasOneConfig{})
}

As you can see we are defining a Post entity which has a HasOne relation with HeaderPicture. You can configure how GolobbyORM queries HasOne relation with orm.HasOneConfig object, by default it will infer all fields for you. now you can use this relationship anywhere in your code.

picture, err := orm.HasOne[HeaderPicture](post)
BelongsTo
type Comment struct {}

func (c Comment) ConfigureRelations(r *orm.RelationConfigurator) {
    r.BelongsTo(&Post{}, orm.BelongsToConfig{})
}

As you can see we are defining a Comment entity which has a BelongsTo relation with Post that we saw earlier. You can configure how GolobbyORM queries BelongsTo relation with orm.BelongsToConfig object, by default it will infer all fields for you. now you can use this relationship anywhere in your code.

post, err := orm.BelongsTo[Post](comment)
BelongsToMany
type Post struct {}

func (p Post) ConfigureRelations(r *orm.RelationConfigurator) {
    r.BelongsToMany(&Category{}, orm.BelongsToManyConfig{IntermediateTable: "post_categories"})
}

type Category struct{}
func(c Category) ConfigureRelations(r *orm.RelationConfigurator) {
    r.BelongsToMany(&Post{}, orm.BelongsToManyConfig{IntermediateTable: "post_categories"})
}

we are defining a Post entity and also a Category entity which have a many2many relationship, as you can see it's mandatory for us to configure IntermediateTable name which GolobbyORM cannot infer by itself now. now you can use this relationship anywhere in your code.

categories, err := orm.BelongsToMany[Category](post)
Saving with relation

You may need to save an entity which has some kind of relation with another entity, in that case you can use Add method.

orm.Add(post, comments...) // inserts all comments passed in and also sets all post_id to the primary key of the given post.

License

GoLobby ORM is released under the MIT License.

Documentation

Index

Constants

View Source
const (
	DialectMySQL = iota + 1
	DialectPostgres
	DialectSQLite
)
View Source
const (
	OrderByASC  = "ASC"
	OrderByDesc = "DESC"
)
View Source
const (
	JoinTypeInner = "INNER"
	JoinTypeLeft  = "LEFT"
	JoinTypeRight = "RIGHT"
	JoinTypeFull  = "FULL"
	JoinTypeSelf  = "SELF"
)
View Source
const (
	Eq      = "="
	GT      = ">"
	LT      = "<"
	GE      = ">="
	LE      = "<="
	NE      = "!="
	Between = "BETWEEN"
	Like    = "LIKE"
	In      = "IN"
)

Variables

View Source
var Dialects = &struct {
	MySQL      *Dialect
	PostgreSQL *Dialect
	SQLite3    *Dialect
}{
	MySQL: &Dialect{
		DriverName:                  "mysql",
		PlaceholderChar:             "?",
		IncludeIndexInPlaceholder:   false,
		AddTableNameInSelectColumns: true,
		PlaceHolderGenerator:        mySQLPlaceHolder,
	},
	PostgreSQL: &Dialect{
		DriverName:                  "postgres",
		PlaceholderChar:             "$",
		IncludeIndexInPlaceholder:   true,
		AddTableNameInSelectColumns: true,
		PlaceHolderGenerator:        postgresPlaceholder,
	},
	SQLite3: &Dialect{
		DriverName:                  "sqlite3",
		PlaceholderChar:             "?",
		IncludeIndexInPlaceholder:   false,
		AddTableNameInSelectColumns: false,
		PlaceHolderGenerator:        mySQLPlaceHolder,
	},
}

Functions

func Add added in v1.2.0

func Add(to Entity, items ...Entity) error

Add adds `items` to `to` using relations defined between items and to in ConfigureRelations method of `to`.

func BelongsTo added in v1.2.0

func BelongsTo[OWNER Entity](property Entity) (OWNER, error)

func BelongsToMany added in v1.2.0

func BelongsToMany[OWNER Entity](property Entity) ([]OWNER, error)

BelongsToMany

func Delete added in v1.2.0

func Delete(obj Entity) error

Delete given Entity from database

func Exec

func Exec[E Entity](stmt *QueryBuilder[E]) (lastInsertedId int64, rowsAffected int64, err error)

func ExecRaw added in v1.2.0

func ExecRaw[E Entity](q string, args ...interface{}) (int64, int64, error)

func Find added in v1.2.0

func Find[T Entity](id interface{}) (T, error)

Find finds the Entity you want based on Entity generic type and primary key you passed.

func HasMany added in v1.2.0

func HasMany[OUT Entity](owner Entity) ([]OUT, error)

func HasOne added in v1.2.0

func HasOne[PROPERTY Entity](owner Entity) (PROPERTY, error)

func Initialize added in v1.2.0

func Initialize(confs ...ConnectionConfig) error

func Insert added in v1.2.0

func Insert(objs ...Entity) error

insertStmt given Entity

func QueryRaw added in v1.2.0

func QueryRaw[OUTPUT Entity](q string, args ...interface{}) ([]OUTPUT, error)

func Save added in v1.2.0

func Save(obj Entity) error

Save upserts given entity.

func Schematic added in v1.2.0

func Schematic()

func Update added in v1.2.0

func Update(obj Entity) error

Update given Entity in database

Types

type BelongsToConfig added in v1.2.0

type BelongsToConfig struct {
	OwnerTable        string
	LocalForeignKey   string
	ForeignColumnName string
}

type BelongsToManyConfig added in v1.2.0

type BelongsToManyConfig struct {
	IntermediateTable      string
	IntermediatePropertyID string
	IntermediateOwnerID    string
	ForeignTable           string
	ForeignLookupColumn    string
}

type Cond

type Cond struct {
	PlaceHolderGenerator func(n int) []string

	Lhs string
	Op  binaryOp
	Rhs interface{}
}

func (Cond) ToSql

func (b Cond) ToSql() (string, []interface{})

type Connection

type Connection struct {
	Name       string
	Dialect    *Dialect
	Connection *sql.DB
	Schemas    map[string]*schema
}

func GetConnection added in v1.2.0

func GetConnection(name string) *Connection

func (*Connection) Schematic

func (c *Connection) Schematic()

type ConnectionConfig added in v1.2.0

type ConnectionConfig struct {
	Name             string
	Driver           string
	ConnectionString string
	DB               *sql.DB
	Dialect          *Dialect
	Entities         []Entity
}

type Dialect

type Dialect struct {
	DriverName                  string
	PlaceholderChar             string
	IncludeIndexInPlaceholder   bool
	AddTableNameInSelectColumns bool
	PlaceHolderGenerator        func(n int) []string
}

type Entity

type Entity interface {
	ConfigureEntity(e *EntityConfigurator)
	ConfigureRelations(r *RelationConfigurator)
}

type EntityConfigurator added in v1.2.0

type EntityConfigurator struct {
	// contains filtered or unexported fields
}

func (*EntityConfigurator) Connection added in v1.2.0

func (e *EntityConfigurator) Connection(name string) *EntityConfigurator

func (*EntityConfigurator) Table added in v1.2.0

type GroupBy added in v1.2.0

type GroupBy struct {
	Columns []string
}

func (GroupBy) String added in v1.2.0

func (g GroupBy) String() string

type HasManyConfig added in v1.2.0

type HasManyConfig struct {
	PropertyTable      string
	PropertyForeignKey string
}

type HasOneConfig added in v1.2.0

type HasOneConfig struct {
	PropertyTable      string
	PropertyForeignKey string
}

type Having

type Having struct {
	PlaceHolderGenerator func(n int) []string
	Cond                 Cond
}

func (Having) ToSql

func (h Having) ToSql() (string, []interface{})

type Join added in v1.2.0

type Join struct {
	Type  joinType
	Table string
	On    JoinOn
}

func (Join) String added in v1.2.0

func (j Join) String() string

type JoinOn added in v1.2.0

type JoinOn struct {
	Lhs string
	Rhs string
}

func (JoinOn) String added in v1.2.0

func (j JoinOn) String() string

type Limit added in v1.2.0

type Limit struct {
	N int
}

func (Limit) String added in v1.2.0

func (l Limit) String() string

type Offset added in v1.2.0

type Offset struct {
	N int
}

func (Offset) String added in v1.2.0

func (o Offset) String() string

type QueryBuilder added in v1.2.0

type QueryBuilder[E Entity] struct {
	// contains filtered or unexported fields
}

func NewQueryBuilder added in v1.2.0

func NewQueryBuilder[E Entity]() *QueryBuilder[E]

func Query added in v1.2.0

func Query[E Entity]() *QueryBuilder[E]

func (*QueryBuilder[E]) All added in v1.2.0

func (q *QueryBuilder[E]) All() ([]E, error)

func (*QueryBuilder[E]) AndWhere added in v1.2.0

func (q *QueryBuilder[E]) AndWhere(parts ...interface{}) *QueryBuilder[E]

func (*QueryBuilder[E]) Count added in v1.2.0

func (q *QueryBuilder[E]) Count() (int64, error)

func (*QueryBuilder[E]) Delete added in v1.2.0

func (q *QueryBuilder[E]) Delete() (sql.Result, error)

func (*QueryBuilder[E]) Execute added in v1.2.0

func (q *QueryBuilder[E]) Execute() (sql.Result, error)

func (*QueryBuilder[E]) First added in v1.2.0

func (q *QueryBuilder[E]) First() (E, error)

func (*QueryBuilder[E]) FromQuery added in v1.2.0

func (q *QueryBuilder[E]) FromQuery(subQuery *QueryBuilder[E]) *QueryBuilder[E]

func (*QueryBuilder[E]) FullOuterJoin added in v1.2.0

func (q *QueryBuilder[E]) FullOuterJoin(table string, onLhs string, onRhs string) *QueryBuilder[E]

func (*QueryBuilder[E]) GroupBy added in v1.2.0

func (q *QueryBuilder[E]) GroupBy(columns ...string) *QueryBuilder[E]

func (*QueryBuilder[E]) InnerJoin added in v1.2.0

func (q *QueryBuilder[E]) InnerJoin(table string, onLhs string, onRhs string) *QueryBuilder[E]

func (*QueryBuilder[E]) Latest added in v1.2.0

func (q *QueryBuilder[E]) Latest() (E, error)

func (*QueryBuilder[E]) LeftJoin added in v1.2.0

func (q *QueryBuilder[E]) LeftJoin(table string, onLhs string, onRhs string) *QueryBuilder[E]

func (*QueryBuilder[E]) Limit added in v1.2.0

func (q *QueryBuilder[E]) Limit(n int) *QueryBuilder[E]

func (*QueryBuilder[E]) Offset added in v1.2.0

func (q *QueryBuilder[E]) Offset(n int) *QueryBuilder[E]

func (*QueryBuilder[E]) One added in v1.2.0

func (q *QueryBuilder[E]) One() (E, error)

func (*QueryBuilder[E]) OrWhere added in v1.2.0

func (q *QueryBuilder[E]) OrWhere(parts ...interface{}) *QueryBuilder[E]

func (*QueryBuilder[E]) OrderBy added in v1.2.0

func (q *QueryBuilder[E]) OrderBy(column string, how string) *QueryBuilder[E]

func (*QueryBuilder[E]) RightJoin added in v1.2.0

func (q *QueryBuilder[E]) RightJoin(table string, onLhs string, onRhs string) *QueryBuilder[E]

func (*QueryBuilder[E]) Select added in v1.2.0

func (q *QueryBuilder[E]) Select(columns ...string) *QueryBuilder[E]

func (*QueryBuilder[E]) Set added in v1.2.0

func (q *QueryBuilder[E]) Set(name string, value interface{}) *QueryBuilder[E]

func (*QueryBuilder[E]) SetDelete added in v1.2.0

func (q *QueryBuilder[E]) SetDelete() *QueryBuilder[E]

func (*QueryBuilder[E]) SetDialect added in v1.2.0

func (q *QueryBuilder[E]) SetDialect(dialect *Dialect) *QueryBuilder[E]

func (*QueryBuilder[E]) SetSelect added in v1.2.0

func (q *QueryBuilder[E]) SetSelect() *QueryBuilder[E]

func (*QueryBuilder[E]) SetUpdate added in v1.2.0

func (q *QueryBuilder[E]) SetUpdate() *QueryBuilder[E]

func (*QueryBuilder[E]) Sets added in v1.2.0

func (q *QueryBuilder[E]) Sets(tuples ...[2]interface{}) *QueryBuilder[E]

func (*QueryBuilder[E]) Table added in v1.2.0

func (q *QueryBuilder[E]) Table(t string) *QueryBuilder[E]

func (*QueryBuilder[E]) ToSql added in v1.2.0

func (q *QueryBuilder[E]) ToSql() (string, []interface{}, error)

func (*QueryBuilder[E]) Update added in v1.2.0

func (q *QueryBuilder[E]) Update() (sql.Result, error)

func (*QueryBuilder[E]) Where added in v1.2.0

func (q *QueryBuilder[E]) Where(parts ...interface{}) *QueryBuilder[E]

func (*QueryBuilder[E]) WhereIn added in v1.2.0

func (q *QueryBuilder[E]) WhereIn(column string, values ...interface{}) *QueryBuilder[E]

func (*QueryBuilder[E]) WherePK added in v1.2.0

func (q *QueryBuilder[E]) WherePK(value interface{}) *QueryBuilder[E]

type RelationConfigurator

type RelationConfigurator struct {
	// contains filtered or unexported fields
}

func (*RelationConfigurator) BelongsTo

func (r *RelationConfigurator) BelongsTo(owner Entity, config BelongsToConfig) *RelationConfigurator

func (*RelationConfigurator) BelongsToMany

func (r *RelationConfigurator) BelongsToMany(owner Entity, config BelongsToManyConfig) *RelationConfigurator

func (*RelationConfigurator) HasMany

func (r *RelationConfigurator) HasMany(property Entity, config HasManyConfig) *RelationConfigurator

func (*RelationConfigurator) HasOne

func (r *RelationConfigurator) HasOne(property Entity, config HasOneConfig) *RelationConfigurator

Jump to

Keyboard shortcuts

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