I've been trying to use the Associations feature in golang orm (https://github.com/jinzhu/gorm/), and am unable to create a pretty simple association.
In the example below, the user table contains data, but email table does not.
I've tried a bunch of things and I'm probably missing something basic, but have been unable to find the right answer in github/stackoverflow.
Code :
package main
import (
"database/sql"
"log"
"github.com/jinzhu/gorm"
"github.com/mattn/go-sqlite3"
)
var db gorm.DB
type User struct {
Name string
Mail Email
}
type Email struct {
Address string
}
//Initialize DB .
func InitDB() {
var DB_DRIVER string
sql.Register(DB_DRIVER, &sqlite3.SQLiteDriver{})
log.Printf("Initializing Database with ", DB_DRIVER)
dbSql, _ := sql.Open(DB_DRIVER, "simple-sqlite")
var err error
db, err = gorm.Open("sqlite3", dbSql)
if err != nil {
log.Fatalf("Got error when connecting to the database, the error is '%v'", err)
}
db.LogMode(true)
// Then you could invoke `*sql.DB`'s functions with it
db.DB().Ping()
db.DB().SetMaxIdleConns(10)
db.DB().SetMaxOpenConns(100)
// Disable table name's pluralization
db.SingularTable(true)
}
func InitSchema() {
db.CreateTable(&User{}, &Email{})
}
func DoStuff() {
user := User{Name: "Jinzhu", Mail: Email{Address: "hello#hello.com"}}
db.Create(&user)
}
func main() {
InitDB()
InitSchema()
DoStuff()
}
go run main.go prints the following output
2015/09/30 17:25:04 Initializing Database with %!(EXTRA string=)
[2015-09-30 17:25:04] [3.21ms] CREATE TABLE "user" ("name" varchar(255))
[2015-09-30 17:25:04] [4.01ms] CREATE TABLE "email" ("address" varchar(255) )
[2015-09-30 17:25:04] [0.54ms] INSERT INTO "user" ("name") VALUES ('Jinzhu')
Not sure what I'm missing here - appreciate any response!
You're missing your primary/foreign key references for each of your models, here's the updated code:
package main
import (
"database/sql"
"log"
"github.com/jinzhu/gorm"
"github.com/mattn/go-sqlite3"
)
var db gorm.DB
type User struct {
ID uint `gorm:"primary_key"`
Name string
Mail Email
MailID sql.NullInt64
}
type Email struct {
ID uint `gorm:"primary_key"`
Address string
}
//Initialize DB .
func InitDB() {
var DB_DRIVER string
sql.Register(DB_DRIVER, &sqlite3.SQLiteDriver{})
log.Printf("Initializing Database with ", DB_DRIVER)
dbSql, _ := sql.Open(DB_DRIVER, "simple-sqlite")
var err error
db, err = gorm.Open("sqlite3", dbSql)
if err != nil {
log.Fatalf("Got error when connecting to the database, the error is '%v'", err)
}
db.LogMode(true)
// Then you could invoke `*sql.DB`'s functions with it
db.DB().Ping()
db.DB().SetMaxIdleConns(10)
db.DB().SetMaxOpenConns(100)
// Disable table name's pluralization
db.SingularTable(true)
}
func InitSchema() {
db.CreateTable(&User{}, &Email{})
}
func DoStuff() {
user := User{Name: "Jinzhu", Mail: Email{Address: "hello#hello.com"}}
db.Create(&user)
}
func main() {
InitDB()
InitSchema()
DoStuff()
}
notice the primary keys on the User and Email structs as well as the foreign key reference on User
Related
I am trying to save an hederea contract ID of type *hedera.ContractID into a Gorm field but i get the error "invalid field found for struct github.com/hashgraph/hedera-sdk-go/v2.AccountID's field AliasKey: define a valid foreign key for relations or implement the Valuer interface"
package contract
import (
"fmt"
"github.com/.../scanner/controllers/blockchain"
database "github.com/.../scanner/db"
model "github.com/.../scanner/models"
"github.com/rs/xid"
"gorm.io/gorm"
)
func DeployContract() *gorm.DB {
//connect to database
db, err := database.ConnectToDB()
//if db connection fails
if err != nil {
panic(err)
}
//init model
var modelContract model.Contract
//check if a contract has been deployed
if err := db.First(&modelContract); err.Error != nil {
//no deployment found
//Migrate the schema
db.AutoMigrate(&model.Contract{})
//deploy contract
contract, _ := blockchain.DeployContract()
//create record
// generate random id
id := xid.New()
// Create
db.Create(&model.Contract{
Id: id.String(),
ContractId: contract.Receipt.ContractID,
GasUsed: contract.CallResult.GasUsed,
TransactionId: fmt.Sprint(contract.TransactionID),
Timestamp: contract.ConsensusTimestamp,
ChargeFee: fmt.Sprint(contract.TransactionFee),
PayerAccount: fmt.Sprint(contract.TransactionID.AccountID),
Status: fmt.Sprint(contract.Receipt.Status),
})
}
return db
}
Gorm Model
package models
import (
"time"
"github.com/hashgraph/hedera-sdk-go/v2"
"gorm.io/gorm"
)
type Contract struct {
gorm.Model
Id string
ContractId *hedera.ContractID
GasUsed uint64
TransactionId string
Timestamp time.Time
ChargeFee string
PayerAccount string
Status string
}
For custom data types, you need to specify how the value will be stored and retrieved from your database. This is done by implementing the Scanner and Valuer interfaces.
However, since hedera.ContractID is defined in another package, you will need to create your own ContractID and implement these interfaces. Something like this:
type ContractID hedera.ContractID
type Contract struct {
gorm.Model
Id string
ContractId *ContractID
GasUsed uint64
TransactionId string
Timestamp time.Time
ChargeFee string
PayerAccount string
Status string
}
func (c *ContractID) Scan(value interface{}) error {
bytes, ok := value.([]byte)
if !ok {
return errors.New(fmt.Sprint("Failed to unmarshal ContractID value:", value))
}
return json.Unmarshal(bytes, c)
}
func (c ContractID) Value() (driver.Value, error) {
return json.Marshal(c)
}
Additionally, cast hedera.ContractID into model.ContractID wherever it is used. For example:
cID := model.ContractID(*contract.Receipt.ContractID)
// Create
db.Create(&model.Contract{
Id: id.String(),
ContractId: &cID,
GasUsed: contract.CallResult.GasUsed,
TransactionId: fmt.Sprint(contract.TransactionID),
Timestamp: contract.ConsensusTimestamp,
ChargeFee: fmt.Sprint(contract.TransactionFee),
PayerAccount: fmt.Sprint(contract.TransactionID.AccountID),
Status: fmt.Sprint(contract.Receipt.Status),
})
Have this struct of User and Post and I try to make Name from User to be included within Post Struct when a user create a new post.
type User struct {
ID int
Name string
Created time.Time
}
type Post struct {
ID int
PostTitle string
PostDesc string
Created time.Time
}
How can I create something connected between this two struct such as Author of the Post ?
The goal is try to get the name of the post author which from User struct with the code below:
post, err := app.Models.Posts.GetPost(id)
GetPost() just run SELECT query and scan row
This approach is without any ORM.
It's a simple query that can return multiple rows. You've to scan the whole resultset and map each column on the struct's fields.
Keep in mind to always check for errors.
Below you can find the solution:
package main
import (
"database/sql"
"fmt"
"time"
_ "github.com/lib/pq"
)
type Post struct {
ID int
PostTitle string
PostDesc string
Created time.Time
UserID int
User User
}
type User struct {
ID int
Name string
Created time.Time
}
func main() {
conn, err := sql.Open("postgres", "host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable")
if err != nil {
panic(err)
}
defer conn.Close()
// get MAX id
var id int
conn.QueryRow(`SELECT MAX(id) FROM posts`).Scan(&id)
// insert
sqlInsertStmt := `INSERT INTO posts (id, post_title, post_desc, created, user_id) VALUES ($1,$2,$3,$4,$5)`
if _, err = conn.Exec(sqlInsertStmt, id+1, "TDD", "Introduction to Test-Driven-Development", time.Now(), 1); err != nil {
panic(err)
}
// read
rows, err := conn.Query(`SELECT posts.id, post_title, post_desc, posts.created, users.id, users.name, users.created FROM posts INNER JOIN users ON posts.user_id=users.id`)
if err != nil {
panic(err)
}
var posts []Post
for rows.Next() {
var post Post
if err = rows.Scan(&post.ID, &post.PostTitle, &post.PostDesc, &post.Created, &post.User.ID, &post.User.Name, &post.User.Created); err != nil {
panic(err)
}
posts = append(posts, post)
}
if err = rows.Err(); err != nil {
panic(err)
}
for _, v := range posts {
fmt.Printf("author name: %q\n", v.User.Name)
}
}
Let me know if this helps.
Edit
I've also included an example of INSERT in Postgres. To achieve it, we've to use the db.Exec() function, and provide the parameters.
Pay attention to how you construct the query as you can get a SQL-Injection vulnerability.
Lastly, in a real-world scenario, you shouldn't lookup for the MAX id in the posts table but should be automatically generated.
Give it a try to this solution, maybe it resolves your issue.
First, I defined the structs in this way:
// "Post" belongs to "User", "UserID" is the foreign key
type Post struct {
gorm.Model
ID int
PostTitle string
PostDesc string
Created time.Time
UserID int
User User
}
type User struct {
ID int
Name string
Created time.Time
}
In this way, you can say that Post belongs to User and access the User's information within the Post struct.
To query the records, you've to use Preload("User") to be sure to eager load the User records from the separate table.
Keep attention to the name you pass in as the argument in Preload, as it can be tricky.
Lastly, you'll be able to access data in the embedded struct (with the dot notation).
Below, you can find a complete working example (implemented with the use of Docker):
package main
import (
"fmt"
"time"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
// "Post" belongs to "User", "UserID" is the foreign key
type Post struct {
gorm.Model
ID int
PostTitle string
PostDesc string
Created time.Time
UserID int
User User
}
type User struct {
ID int
Name string
Created time.Time
}
func main() {
dsn := "host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
db.AutoMigrate(&Post{})
newPost := &Post{ID: 1, PostTitle: "Golang", PostDesc: "Introduction to Golang", Created: time.Now(), UserID: 1, User: User{ID: 1, Name: "John Doe", Created: time.Now()}}
db.Create(newPost)
var post Post
db.Preload("User").Find(&post, 1)
fmt.Printf("author name: %q\n", post.User.Name)
}
Let me know if I answered your question!
I'd like to use GORM's 'belongs to' association in a way similar to Django's one-to-one relationships. Consider the following example in which each User is associated with one Profile:
package main
import (
"fmt"
"os"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
"github.com/sirupsen/logrus"
)
type User struct {
gorm.Model
Name string
}
func (user User) String() string {
return fmt.Sprintf("User(Name=%s)", user.Name)
}
type Profile struct {
gorm.Model
UserID uint
User User
Name string
}
func (profile Profile) String() string {
return fmt.Sprintf("Profile(Name=%s, User=%d)", profile.Name, profile.UserID)
}
func (user *User) AfterCreate(scope *gorm.Scope) error {
profile := Profile{
UserID: user.ID,
Name: user.Name,
}
return scope.DB().Create(&profile).Error
}
const dbName = "examplegorm.db"
func main() {
db, err := gorm.Open("sqlite3", dbName)
if err != nil {
logrus.Fatalf("open db: %v", err)
}
defer func() {
db.Close()
os.Remove(dbName)
}()
db.LogMode(true)
db.AutoMigrate(&User{})
db.AutoMigrate(&Profile{})
user := User{Name: "jinzhu"}
if err := db.Create(&user).Error; err != nil {
logrus.Fatalf("create user: %v", err)
}
var profile Profile
if err := db.Where(Profile{UserID: user.ID}).Preload("User").First(&profile).Error; err != nil {
logrus.Fatalf("get profile: %v", err)
}
logrus.Infof("profile: %v", profile)
logrus.Infof("user: %v", profile.User)
}
In this example, I query for a Profile and preload its User. I would actually like to do this the other way, however: query a User and preload its Profile.
As I understand it, in Django you would be able to access both the profile.user and the user.profile, but if I try to add Profile and ProfileID fields to the User model,
type User struct {
gorm.Model
Name string
Profile
ProfileID uint
}
I get an 'invalid recursive type' error:
# command-line-arguments
./gorm_belongs_to.go:23:6: invalid recursive type Profile
Is there any way to get a user.Profile in this GORM example?
I think that the problem in your case is using the name Profile which is the same as the type.
If your User struct will look something like this it should work:
type User struct {
gorm.Model
Name string
UserProfile Profile
UserProfileID uint
}
I currently develop golang projects with xorm.
I want to use a cache to manage the result of sql queries called once. I expected that sql requests would be called once, and would not be called again, but they do get called again.
Also, Redis keys that get created do not appear in redis-cli(keys *).
Why are my sql queries getting called more than once?
package main
import (
"github.com/go-xorm/xorm"
_ "github.com/go-sql-driver/mysql"
xrc "github.com/go-xorm/xorm-redis-cache"
)
type User struct {
Id int
Name string
}
func main() {
engine, err := xorm.NewEngine("mysql", "root:#/xorm_test_db")
if nil != err {
log.Fatal(err)
}
engine.ShowSQL(true)
cacher := xrc.NewRedisCacher("localhost:6379", "", xrc.DEFAULT_EXPIRATION, engine.Logger())
engine.SetDefaultCacher(cacher)
engine.Get(User{Id: 1})
engine.Get(User{Id: 1})
}
Two things has to be addressed for the caching to work properly:
Table must have a Primary Key for it to be cached. So Id can be
made as Primary Key as follows:
type User struct {
Id int `xorm:"pk"`
Name string
}
The type User must be registered using Golang's encoding/gob package:
gob.Register(new(User))
Don't forget to drop the existing table and sync the new User structure.
// Drop the existing table
DROP TABLE user;
// Sync the User struct to table
engine.Sync(new(User))
// Create a sample user
engine.Insert(&User{Id: 1, Name: "user1"})
The corrected code would look something as follows:
package main
import (
"github.com/go-xorm/xorm"
_ "github.com/go-sql-driver/mysql"
xrc "github.com/go-xorm/xorm-redis-cache"
"encoding/gob"
)
type User struct {
Id int `xorm:"pk"`
Name string
}
func main() {
gob.Register(new(User))
engine, err := xorm.NewEngine("mysql", "root:#/xorm_test_db")
if nil != err {
log.Fatal(err)
}
engine.ShowSQL(true)
cacher := xrc.NewRedisCacher("localhost:6379", "", xrc.DEFAULT_EXPIRATION, engine.Logger())
engine.SetDefaultCacher(cacher)
engine.Get(User{Id: 1})
engine.Get(User{Id: 1})
}
I resolved that!
package main
import (
"github.com/go-xorm/xorm"
_ "github.com/go-sql-driver/mysql"
xrc "github.com/go-xorm/xorm-redis-cache"
"encoding/gob"
"log"
)
type User struct {
Id int `xorm:"pk"`
Name string `xorm:"'name'"`
}
func main() {
gob.Register(new(User))
engine, err := xorm.NewEngine("mysql", "root:#/xorm_test2?charset=utf8")
if nil != err {
log.Fatal(err)
}
engine.Sync(new(User))
engine.Insert(&User{Id: 1, Name: "user1"})
engine.ShowSQL(true)
cacher := xrc.NewRedisCacher("localhost:6379", "", xrc.DEFAULT_EXPIRATION, engine.Logger())
engine.SetDefaultCacher(cacher)
engine.MapCacher(&User{}, cacher)
engine.Get(&User{Id: 1})
engine.Get(&User{Id: 1})
}
I'm trying to return a instance from the gorm.Open() return it i'm getting following error
controllers/db.go:34: cannot assign *gorm.DB to dc.DB (type gorm.DB) in multiple assignment
This is the db.go controller
package controllers
import (
//"fmt"
_ "github.com/go-sql-driver/mysql"
//v "github.com/spf13/viper"
"github.com/jinzhu/gorm"
)
type DBController struct {
DB gorm.DB
}
func (dc *DBController) InitDB() {
var err error
dc.DB, err = gorm.Open("mysql","root:12345#tcp(localhost:3306)/api")
if err != nil {
log.Fatalf("Error when connect database, the error is '%v'", err)
}
dc.DB.LogMode(true)
}
func (dc *DBController) GetDB() gorm.DB {
return dc.DB
}
What is reason for this error and how can i fix this?
You need and most probably you want to have a pointer in the structure of controller. Passing structure with a pointer to the database object (gorm.DB) will prevent Go from making a copy of this object (gorm.DB).
Do the following:
type DBController struct {
DB *gorm.DB
}
Now it should work fine.