How to create an autoincrement ID field - go

I have a set of employees defined in a JSON-file:
type PrimitiveEmployee struct {
PrimitiveID string `xml:"ID,attr"`
ContractID []string `xml:"ContractID"`
}
Both ID and ContractID is letters, but they are not necessarily given continuous sequence of letters. For instance, there might exist an employee with PrimitiveID=A and another with PrimitiveID=C, but not an employee with PrimitiveID=B.
I want to convert this data into:
type Employee struct {
ID int
PrimitiveID string
Contracts []Contract
}
The thing is, I want the the employee-ID to start at 0 and increment with one for each time the struct is initialized. Sort of like a an autoincrement ID in a database or iota in a enum.
The employee with PrimitiveID=A will then automatically be created with ID=0, while the employee with PrimitiveID=C will get ID=1.
I just cant figure out how to solve this in a struct.
Greatly appreciate any help or pointers here.

Create a custom type to manage the unique incrementing ID:
type autoInc struct {
sync.Mutex // ensures autoInc is goroutine-safe
id int
}
func (a *autoInc) ID() (id int) {
a.Lock()
defer a.Unlock()
id = a.id
a.id++
return
}
So you can use it in targeted places or at the package level:
var ai autoInc // global instance
You can then create "constructor" functions to leverage this:
func NewEmployee() *Employee {
return &Employee{
ID: ai.ID(),
}
}
Marshaling JSON data to Employee can then be performed and the ID will be preserved - provided the JSON data does not container an ID tag.
https://play.golang.org/p/0iTaQSzTPZ_j

Related

GORM: unsupported Scan, storing driver.Value type int64 into type *models.Team

I have issue with my first GORM usage. I want to load Point with preloaded Team structure. Go throw error that int64 cannot be stored in models.Team. I suppose that I have to use GORM the wrong way, but I am not able to find out why. Database contain data, relation many2many on other structs works fine for me (users in Team struct).
type Point struct {
Id int `gorm:"primaryKey"`
TeamX Team `gorm:"column:team_id;foreignKey:id"` // <<< this struct I want to get
CreatedAt time.Time
Note string
}
func (Point) TableName() string {
return "points"
}
type Team struct {
Id int `gorm:"primaryKey"`
Name string
Users []User `gorm:"many2many:teams_users;"`
}
func (Team) TableName() string {
return "teams"
}
var obj models.Point
log.Println(getDatabase().Preload("Teams").First(&obj, 1).Error.Error())
fmt.Println(obj.Id) // <<< this one loads ok
fmt.Println(obj.TeamX.Id) // <<< this throw error
points table
id int(11), auto_increment
team_id int(11), foreign key teams(id)
createdAt datetime
note text
teams table
id int(11), auto_increment
name text
sql: Scan error on column index 1, name "team_id": unsupported Scan, storing driver.Value type int64 into type *models.Team

Accessing column in junction table in GORM

I have three tables users, todos and user_todos:
type User struct {
ID uint64
Email string
Todos []Todo `gorm:"many2many:user_todos"`
}
type Todo struct {
ID uint64
Title string
}
type UserTodos struct {
UserID uint64
TodoID uint64
Done bool
}
Now I want to get all users with all todos. db.Preload("Todos").Find(&users) does the trick for that.
But what if I want to get all todos and see if they are already done by the user?
I tried to create a SQL view checked_todos which joins the user_todos table and exchange the Todos attribute in the User struct with a CheckedTodo, which just extends Todo with a boolean field. Preloading never access the right table.
Couldn't find any hints in the GORM documentation how to access the columns in the junction table.
You might be able to achieve what you want with custom preloading. Two options you might want to try:
Option 1
db.Preload("Todos", func(grm *gorm.DB) *gorm.DB {
return grm.Where("user_todos.done = 1")
}).Find(&users)
Option 2
db.Preload("Todos", func(grm *gorm.DB) *gorm.DB {
return grm.Joins("INNER JOIN user_todos ON user_todos.todo_id = todos.id AND user_todos.done = 1")
}).Find(&users)

Foreign Key Constraint in gorm

I want to create a Booking-Class relationship, where every Booking can be assigned only one Class.
type Class struct {
Id int `json:"id"`
Name string `json:"name"`
}
type Booking struct {
Id int `json:"id"`
User string `json:"user"`
Members int `json:"members"`
ClassId int `json:"classid"`
}
I understand that it is similar to gorm's "belongs-to" relationship explained here https://gorm.io/docs/belongs_to.html but I was wondering if it's possible to achieve "foreign key constraint" without defining the field of type Class inside the Booking model (only ClassId int). To make sure that non-existent ClassId-s are used I defined functions:
func Find(slice []int, val int) bool {
for _, item := range slice {
if item == val {
return true
}
}
return false
}
func GetClassKeys(d *gorm.DB) []int {
var keys []int
rows, _ := d.Raw(`SELECT id
FROM classes`).Rows()
defer rows.Close()
for rows.Next() {
var key int
rows.Scan(&key)
keys = append(keys, key)
}
return keys
}
And perform this check before creating/updating a booking:
if !Find(GetClassKeys(db), booking.ClassId) {
log.Println("Wrong class id value")
return
}
But this doesn't handle the case of removed class id (which regular databases do automatically). I was wondering is there a way to achieve a normal database foreign key functionality with gorm by simply referencing the primary key of Class in a User model? Thanks in advance
I don't think the Migrator tool will help you because it assumes you're going to use the default Gorm patterns for defining relationships, and you've explicitly decided NOT to use these.
The only remaining option is to manage the constraint yourself, either through an SQL script or easier, some custom queries you run alongside your AutoMigrate call:
import "gorm.io/gorm/clause"
// somewhere you must be calling
db.AutoMigrate(&Class{}, &Booking{})
// then you'd have:
constraint := "fk_booking_classid"
var count int64
err := db.Raw(
"SELECT count(*) FROM INFORMATION_SCHEMA.table_constraints WHERE constraint_schema = ? AND table_name = ? AND constraint_name = ?",
db.Migrator().CurrentDatabase(),
"bookings",
constraint,
).Scan(&count).Error
// handle error
if (count == 0) {
err := db.Exec(
"ALTER TABLE bookings ADD CONSTRAINT ? FOREIGN KEY class_id REFERENCES classes(id)",
clause.Table{Name: constraint},
).Error
// handle error
}
Of course, this negates the advantage of having an automated migration (which means when things change like the field name, the constraint will be updated without changes to the migration code).
I'd be looking at why you don't want to define the foreign key as gorm expects, as this smells of you trying to use the database model directly as your JSON API Request/Response model, and that usually doesn't lead to a good end :)

gorm golang one2many same table

I'm trying to create a self-reference in a (my)sql table using golang gorm. At the moment my code looks like this:
type Person struct {
gorm.Model
Name string
Children []*Person `gorm:"ForeignKey:ParentID"`
ParentID uint
}
func main() {
/* code to get database connection omitted */
p := &Person{Name:"Sally"}
db.Create(p)
children := []*Person{ {Name:"Jane", ParentID:p.ID},
{Name:"Tom", ParentID:p.ID}}
for _, child := range children {
db.Create(child)
}
var children2 []*Person
db.Model(p).Related(children2, "ParentID")
}
The code is failing with an error "reflect.Value.Set using unaddressable value".
Does anybody know how to get this relationship working using go gorm?
Many thanks in advance :)
Fortunately gorm have added lately this feature (reference: here).
In your case should be like this:
type Person struct {
gorm.Model
Name string
Children []*Person `gorm:"many2many: children;association_jointable_foreignkey:children_id"`
}

How to populate and embedded array with gorm?

I have 2 structs with data like this:
type User struct {
Pics Pic[]
}
type Pic struct {
Id int
UserId int64
}
Although everytime I insert an User, Each of the pics are inserted on their table everytime I find the users, pics are not populated:
var users []User
db.Limit(pagesize).Where("updated_at > ?", date).Find(&users)
Am I doing something wrong?
Your models (the structs) don't really make sense because User have a Pic array indicates a 'one to many' user to pics relationship however your user has no id property itself and there for cannot be related to items on the Pic table.
User should have a property Id which will be it's primary key and UserId is a foreign key on Pic that relates to it. Without the 'relation' between these two tables/entities there's no way you're going to return pics by querying users.
I'm not sure what all you need to do to make your code work since the example is incomplete but the first thing you need is an Id property which you should designate as a Primarykey with gorm annotations. You also should have annotations on the Pic struct saying UserId is a foreign key and Id is it's primary key.
Also, just fyi your array is not embedded. Embedding is a language feature which you're not using, if you embed the property it has no name and it's properties can be accessed directly from an instance of the embedding type.
I had these issues once. Then I used Join function.
See my example that works just fine:
type FileType struct {
Id int
}
type File struct {
Id int
FileType `xorm:"extends"`
}
file := File{Id: id}
has, err := eng.
Join("INNER", "FileType", "FileType.IdFileType = File.IdFileType").
Get(&file)
You probably know by now. You got to think as if you are creating a SQL table with 1-to-many relationship. Here is an example:
type Entry struct {
ID int
Name string
...
ContainerID int
}
type Container struct {
ID int
Tag int
Name string
...
Entries []Entry `gorm:"foreignkey:ContainerID"`
}
The trick is to populate it. I am yet to find how to make it in one try. For every such dependency, you got to run something like:
c := getContainerFromDB(...)
if err := getROConn().Model(c).Related(&c.Entries, "Entries").Error; err != nil {
return errors.Wrap(err, "error getting container field")
}
Try Preload
db.Limit(pagesize).Where("updated_at > ?", date).Preload("Pics").Find(&users)

Resources