Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/meowmeowcode/hohin
Database toolkit for Go
https://github.com/meowmeowcode/hohin
Last synced: 19 days ago
JSON representation
Database toolkit for Go
- Host: GitHub
- URL: https://github.com/meowmeowcode/hohin
- Owner: meowmeowcode
- License: mit
- Created: 2023-07-13T18:48:12.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2023-09-20T19:27:38.000Z (over 1 year ago)
- Last Synced: 2024-11-08T19:23:19.954Z (2 months ago)
- Language: Go
- Homepage:
- Size: 176 KB
- Stars: 4
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Hōhin
Hohin is a database toolkit that contains generic implementations of the Repository pattern.
## Supported database systems
At the moment, ClickHouse, MySQL, PostgreSQL, and SQLite3 are supported.
## Documentation
https://pkg.go.dev/github.com/meowmeowcode/hohin
## Usage example
```go
package exampleimport (
"context"
"database/sql"
"encoding/json"
"fmt"
"github.com/google/uuid"
_ "github.com/mattn/go-sqlite3"
"github.com/meowmeowcode/hohin"
"github.com/meowmeowcode/hohin/sqldb"
"github.com/meowmeowcode/hohin/sqlite3"
)func Example() {
// Suppose we have this entity in our application:
type User struct {
Id uuid.UUID
Name string
}// We need to connect to a database and create a table for this entity:
pool, err := sql.Open("sqlite3", ":memory:")
if err != nil {
panic(err)
}
defer pool.Close()_, err = pool.Exec(`
CREATE TABLE users (
Id uuid PRIMARY KEY,
Name text NOT NULL
)
`)
if err != nil {
panic(err)
}// Everything is set up. Let's see what we can do now.
// Creating a repository:
usersRepo := sqlite3.NewRepo(sqlite3.Conf[User]{Table: "users"}).Simple()// Saving an entity:
db := sqlite3.NewDB(pool).Simple()
alice := User{Id: uuid.New(), Name: "Alice"}
err = usersRepo.Add(db, alice)
if err != nil {
panic(err)
}// Saving several entities:
bob := User{Id: uuid.New(), Name: "Bob"}
eve := User{Id: uuid.New(), Name: "Eve"}
err = usersRepo.AddMany(db, []User{bob, eve})
if err != nil {
panic(err)
}// Loading an entity:
user, err := usersRepo.Get(db, hohin.Eq("Name", "Alice"))
if err != nil {
panic(err)
}
fmt.Println(user == alice)user, err = usersRepo.Get(db, hohin.Contains("Name", "o"))
if err != nil {
panic(err)
}
fmt.Println(user == bob)user, err = usersRepo.Get(
db,
hohin.And(hohin.HasSuffix("Name", "e"), hohin.HasPrefix("Name", "E")),
)
if err != nil {
panic(err)
}
fmt.Println(user == eve)// Loading several entities:
users, err := usersRepo.GetMany(
db,
hohin.Query{Filter: hohin.HasSuffix("Name", "e")}.OrderBy(hohin.Asc("Name")),
)
if err != nil {
panic(err)
}
fmt.Println(len(users) == 2)
fmt.Println(users[0] == alice)
fmt.Println(users[1] == eve)// Updating an entity:
bob.Name = "Robert"
err = usersRepo.Update(db, hohin.Eq("Id", bob.Id), bob)
if err != nil {
panic(err)
}
user, err = usersRepo.Get(db, hohin.Eq("Id", bob.Id))
if err != nil {
panic(err)
}
fmt.Println(user.Name == "Robert")// Removing an entity:
err = usersRepo.Delete(db, hohin.Eq("Name", "Robert"))
if err != nil {
panic(err)
}// Using transactions:
err = db.Transaction(func(db hohin.SimpleDB) error {
alice, err := usersRepo.GetForUpdate(db, hohin.Eq("Name", "Alice"))
if err != nil {
return err
}
eve, err := usersRepo.GetForUpdate(db, hohin.Eq("Name", "Eve"))
if err != nil {
return err
}
alice.Name = "Eve"
eve.Name = "Alice"
err = usersRepo.Update(db, hohin.Eq("Id", alice.Id), alice)
if err != nil {
return err
}
err = usersRepo.Update(db, hohin.Eq("Id", eve.Id), eve)
if err != nil {
return err
}
return nil
})
if err != nil {
panic(err)
}
user, err = usersRepo.Get(db, hohin.Eq("Id", alice.Id))
if err != nil {
panic(err)
}
fmt.Println(user.Name == "Eve")// Using a context:
usersRepo2 := sqlite3.NewRepo(sqlite3.Conf[User]{Table: "users"})
db2 := sqlite3.NewDB(pool)
user, err = usersRepo2.Get(context.Background(), db2, hohin.Eq("Name", "Alice"))
if err != nil {
panic(err)
}// Configuring a repository:
//
// For this example we need another entity and a couple of tables:
type Contact struct {
Id uuid.UUID
Name string
Emails []string
}_, err = pool.Exec(`
CREATE TABLE contacts (
pk uuid PRIMARY KEY,
name text NOT NULL
)
`)
if err != nil {
panic(err)
}_, err = pool.Exec(`
CREATE TABLE emails (
pk uuid PRIMARY KEY,
email text NOT NULL,
contact_pk text NOT NULL
)
`)
if err != nil {
panic(err)
}contactsRepo := sqlite3.NewRepo(sqlite3.Conf[Contact]{
Table: "contacts",
Mapping: map[string]string{
"Id": "pk",
"Name": "name",
},
Query: `
SELECT * FROM (
SELECT contacts.pk, contacts.name, json_group_array(emails.email) AS emails
FROM contacts
LEFT JOIN emails ON emails.contact_pk = contacts.pk
GROUP BY contacts.pk, contacts.name
) AS query
`,
Load: func(s sqlite3.Scanner) (Contact, error) {
var entity Contact
var emailsData string
err := s.Scan(&entity.Id, &entity.Name, &emailsData)
err = json.Unmarshal([]byte(emailsData), &entity.Emails)
return entity, err
},
AfterAdd: func(c Contact) []*sqldb.SQL {
var qs []*sqldb.SQL
for _, e := range c.Emails {
q := sqlite3.NewSQL("INSERT INTO emails (pk, email, contact_pk) VALUES (").
JoinParams(", ", uuid.New(), e, c.Id).
Add(")")
qs = append(qs, q)
}
return qs
},
AfterUpdate: func(c Contact) []*sqldb.SQL {
var qs []*sqldb.SQL
qs = append(qs, sqlite3.NewSQL("DELETE FROM emails WHERE contact_pk = ").Param(c.Id))for _, e := range c.Emails {
q := sqlite3.NewSQL("INSERT INTO emails (id, email, contact_pk) VALUES (").
JoinParams(", ", uuid.New(), e, c.Id).
Add(")")
qs = append(qs, q)
}
return qs
},
}).Simple()contact := Contact{Id: uuid.New(), Name: "Bob", Emails: []string{"[email protected]", "[email protected]"}}
err = contactsRepo.Add(db, contact)
if err != nil {
panic(err)
}contact = Contact{Id: uuid.New(), Name: "Alice", Emails: []string{"[email protected]"}}
err = contactsRepo.Add(db, contact)
if err != nil {
panic(err)
}contact, err = contactsRepo.Get(db, hohin.Eq("Name", "Bob"))
if err != nil {
panic(err)
}
fmt.Println(contact.Name, contact.Emails)contact, err = contactsRepo.Get(db, hohin.Eq("Name", "Alice"))
if err != nil {
panic(err)
}
fmt.Println(contact.Name, contact.Emails)// Output:
// true
// true
// true
// true
// true
// true
// true
// true
// Bob [[email protected] [email protected]]
// Alice [[email protected]]
}
```