Skip to content

Instantly share code, notes, and snippets.

@wangrenjun
Last active February 3, 2024 13:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save wangrenjun/48f03d2980c8f953f1e9ca5d7e09de5c to your computer and use it in GitHub Desktop.
Save wangrenjun/48f03d2980c8f953f1e9ca5d7e09de5c to your computer and use it in GitHub Desktop.
Example of GORM using join
package main
import (
"encoding/json"
"fmt"
"time"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
var dsn = "host=127.0.0.1 user=postgres dbname=gormexample port=5432 sslmode=disable password=XXX"
func main() {
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
sqldb, err := db.DB()
if err != nil {
panic(err)
}
sqldb.SetMaxIdleConns(10) // 设置连接池中空闲连接的最大数量
sqldb.SetMaxOpenConns(100) // 设置打开数据库连接的最大数量
sqldb.SetConnMaxLifetime(time.Second * 30) // 设置连接可复用的最大时间
AutoMigrate(db)
CreateSchools(db)
CreateClasses(db)
CreateStudents(db)
listAllStudentInfo(db)
listAllStudentInfoUnderGrade(db, 4)
}
func AutoMigrate(db *gorm.DB) {
db.AutoMigrate(&Student{})
db.AutoMigrate(&Class{})
db.AutoMigrate(&School{})
}
func CreateSchools(db *gorm.DB) {
schools := []School{
{
Name: "SCHOOL.NAME.1",
Address: "SCHOOL.ADDRESS.1",
},
{
Name: "SCHOOL.NAME.2",
Address: "SCHOOL.ADDRESS.2",
},
{
Name: "SCHOOL.NAME.3",
Address: "SCHOOL.ADDRESS.3",
},
}
res := db.Create(&schools)
if res.Error != nil {
panic(res.Error)
}
for _, school := range schools {
fmt.Printf("school.ID: %d\n", school.ID)
}
}
func CreateClasses(db *gorm.DB) {
classes := []Class{
{
Grade: 1,
Class: 1,
SchoolId: 1,
Address: "CLASS.ADDRESS.1.1.1",
},
{
Grade: 1,
Class: 2,
SchoolId: 1,
Address: "CLASS.ADDRESS.1.2.1",
},
{
Grade: 2,
Class: 1,
SchoolId: 1,
Address: "CLASS.ADDRESS.2.1.1",
},
{
Grade: 2,
Class: 2,
SchoolId: 1,
Address: "CLASS.ADDRESS.2.2.1",
},
{
Grade: 4,
Class: 5,
SchoolId: 3,
Address: "CLASS.ADDRESS.4.5.3",
},
}
res := db.Create(&classes)
if res.Error != nil {
panic(res.Error)
}
for _, class := range classes {
fmt.Printf("class.ID: %d\n", class.ID)
}
}
func CreateStudents(db *gorm.DB) {
students := []Student{
{
SchoolId: 1,
Grade: 1,
Class: 1,
Name: "STUDENT.AAA",
},
{
SchoolId: 1,
Grade: 1,
Class: 1,
Name: "STUDENT.BBB",
},
{
SchoolId: 1,
Grade: 1,
Class: 2,
Name: "STUDENT.CCC",
},
{
SchoolId: 1,
Grade: 1,
Class: 2,
Name: "STUDENT.DDD",
},
{
SchoolId: 3,
Grade: 4,
Class: 5,
Name: "STUDENT.EEE",
},
}
res := db.Create(&students)
if res.Error != nil {
panic(res.Error)
}
for _, student := range students {
fmt.Printf("student.ID: %d\n", student.ID)
}
}
type StudentInfo struct {
Student `gorm:"embedded;embeddedPrefix:student_"`
Class `gorm:"embedded;embeddedPrefix:class_"`
School `gorm:"embedded;embeddedPrefix:school_"`
}
// select students.id, students.created_at, students.updated_at, students.grade,
// students.class, students.name, classes.address, schools.name, schools.address
// from (students inner join classes on students.class_id = classes.id)
// inner join schools on classes.school_id = schools.id
func listAllStudentInfo(db *gorm.DB) {
var results []StudentInfo
fields := `
students.id AS student_id,
students.created_at AS student_created_at,
students.updated_at AS student_updated_at,
students.grade AS student_grade,
students.class AS student_class,
students.name AS student_name,
classes.address AS class_address,
schools.name AS school_name,
schools.address AS school_address
`
err := db.Model(&Student{}).Select(fields).
Joins("INNER JOIN classes ON students.class_id = classes.id").
Joins("INNER JOIN schools ON classes.school_id= schools.id").
Scan(&results).Error
if err != nil {
panic(err)
}
fmt.Print("listAllStudentInfo: ")
for _, r := range results {
fmt.Printf("%s\n", marshalIndent(struct {
Student Student
Class Class
School School
}{Student: r.Student, Class: r.Class, School: r.School}))
}
}
func listAllStudentInfoUnderGrade(db *gorm.DB, grade uint8) {
var results []StudentInfo
fields := `
students.id AS student_id,
students.created_at AS student_created_at,
students.updated_at AS student_updated_at,
students.grade AS student_grade,
students.class AS student_class,
students.name AS student_name,
classes.address AS class_address,
schools.name AS school_name,
schools.address AS school_address
`
err := db.Model(&Student{}).Select(fields).
Joins("INNER JOIN classes ON students.class_id = classes.id").
Joins("INNER JOIN schools ON classes.school_id= schools.id").
Where("classes.grade = ?", grade).
Scan(&results).Error
if err != nil {
panic(err)
}
fmt.Print("listAllStudentInfoUnderGrade: ")
for _, r := range results {
fmt.Printf("%s\n", marshalIndent(struct {
Student Student
Class Class
School School
}{Student: r.Student, Class: r.Class, School: r.School}))
}
}
func marshalIndent(val interface{}) string {
pretty, _ := json.MarshalIndent(val, "", "\t")
return string(pretty)
}
package main
import (
"errors"
"fmt"
"strings"
"time"
"gorm.io/gorm"
)
type School struct {
ID uint64 `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Name string `gorm:"size:50;not null;index"`
Address string `gorm:"size:200;not null;default:''"`
}
func (s *School) BeforeSave(tx *gorm.DB) (err error) {
fmt.Printf("School BeforeSave: %+v\n", s)
return
}
func (s *School) AfterSave(tx *gorm.DB) (err error) {
fmt.Printf("School AfterSave: %+v\n", s)
return
}
func (s *School) validate() (err error) {
s.Name = strings.TrimSpace(s.Name)
s.Address = strings.TrimSpace(s.Address)
if s.Name == "" || s.Address == "" {
return errors.New("Invalid arguments")
}
return
}
func (s *School) BeforeCreate(tx *gorm.DB) (err error) {
fmt.Printf("School BeforeCreate: %+v\n", s)
return s.validate()
}
func (s *School) AfterCreate(tx *gorm.DB) (err error) {
fmt.Printf("School AfterCreate: %+v\n", s)
return
}
func (s *School) BeforeUpdate(tx *gorm.DB) (err error) {
fmt.Printf("School BeforeUpdate: %+v\n", s)
return s.validate()
}
func (s *School) AfterUpdate(tx *gorm.DB) (err error) {
fmt.Printf("School AfterUpdate: %+v\n", s)
return
}
func (s *School) BeforeDelete(tx *gorm.DB) (err error) {
fmt.Printf("School BeforeDelete: %+v\n", s)
return
}
func (s *School) AfterDelete(tx *gorm.DB) (err error) {
fmt.Printf("School AfterDelete: %+v\n", s)
return
}
func (s *School) AfterFind(tx *gorm.DB) (err error) {
fmt.Printf("School AfterFind: %+v\n", s)
return
}
type Class struct {
ID uint64 `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Grade uint8 `gorm:"uniqueIndex:idx_grade_class_schoolid,priority:1"` //年 grade, class, school_id
Class uint8 `gorm:"uniqueIndex:idx_grade_class_schoolid,priority:2"` //班
SchoolId uint64 `gorm:"uniqueIndex:idx_grade_class_schoolid,priority:3;index"`
Address string `gorm:"size:200;not null;default:''"`
}
func (c *Class) BeforeCreate(tx *gorm.DB) (err error) {
fmt.Printf("Class BeforeCreate: %+v\n", c)
var school School
err = tx.First(&school, c.SchoolId).Error
return
}
type Student struct {
ID uint64 `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
ClassId uint64 `gorm:"index:idx_classid_schoolid,priority:1"` // class_id, school_id
SchoolId uint64 `gorm:"index:idx_classid_schoolid,priority:2;index"`
Grade uint8 `gorm:"index:idx_grade_class,priority:1"` //年 grade, class, school_id
Class uint8 `gorm:"index:idx_grade_class,priority:2"` //班
Name string `gorm:"index;size:50;not null"`
}
func (s *Student) BeforeCreate(tx *gorm.DB) (err error) {
fmt.Printf("Student BeforeCreate: %+v\n", s)
var class Class
ret := tx.Model(&Class{}).Where("grade = ? AND class = ? AND school_id = ?", s.Grade, s.Class, s.SchoolId).Select("id").Find(&class)
if ret.Error != nil {
return ret.Error
}
if ret.RowsAffected == 0 {
return gorm.ErrRecordNotFound
}
s.ClassId = class.ID
return
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment