main.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // demo: gormvrtx (TsDB2 GORM Dialector) 完整走查。
  2. //
  3. // 覆盖: AutoMigrate (建表/加列/索引) → CRUD → 事务 → Preload 关联 (独立第二条查询,
  4. // 不走 JOIN)。每步断言, 失败打印并 os.Exit(1)。
  5. //
  6. // 运行: cd gormvrtx/demo && go run main.go
  7. // 依赖: 主模块 git.ycl.pub/yl32940/TsDB2 (go.mod replace 到 ../..)
  8. package main
  9. import (
  10. "errors"
  11. "fmt"
  12. "os"
  13. "path/filepath"
  14. "time"
  15. "gorm.io/gorm"
  16. "git.ycl.pub/yl32940/TsDB2/gormvrtx"
  17. )
  18. // User 与 Order: has-many 关联, Preload 时 GORM 会单独发一条 IN 查询 (不 JOIN)。
  19. type User struct {
  20. ID uint `gorm:"primaryKey"`
  21. Name string `gorm:"size:32;not null"`
  22. Age int
  23. Email string `gorm:"size:64;uniqueIndex"`
  24. Orders []Order `gorm:"foreignKey:UserID"`
  25. }
  26. // UserWithAddr 迁移演进的下一步: 加一列。
  27. type UserWithAddr struct {
  28. ID uint `gorm:"primaryKey"`
  29. Name string `gorm:"size:32;not null"`
  30. Age int
  31. Email string `gorm:"size:64;uniqueIndex"`
  32. Addr string `gorm:"size:128"`
  33. }
  34. func (UserWithAddr) TableName() string { return "users" }
  35. type Order struct {
  36. ID uint `gorm:"primaryKey"`
  37. UserID uint `gorm:"index"`
  38. Amount float64
  39. }
  40. var failed bool
  41. // check 断言: 失败即标记并打印。
  42. func check(ok bool, format string, args ...interface{}) {
  43. if ok {
  44. fmt.Printf(" ok: %s\n", fmt.Sprintf(format, args...))
  45. return
  46. }
  47. failed = true
  48. fmt.Printf(" FAIL: %s\n", fmt.Sprintf(format, args...))
  49. }
  50. func main() {
  51. dir, err := os.MkdirTemp("", "gormvrtx_demo")
  52. if err != nil {
  53. panic(err)
  54. }
  55. defer os.RemoveAll(dir)
  56. dsn := filepath.Join(dir, "demo.db")
  57. db, err := gorm.Open(gormvrtx.Open(dsn), &gorm.Config{})
  58. if err != nil {
  59. fmt.Println("打开失败:", err)
  60. os.Exit(1)
  61. }
  62. fmt.Println("[1] AutoMigrate 建表 (主键/普通字段/uniqueIndex)")
  63. if err := db.AutoMigrate(&User{}, &Order{}); err != nil {
  64. fmt.Println(" FAIL: AutoMigrate:", err)
  65. os.Exit(1)
  66. }
  67. check(db.Migrator().HasTable(&User{}), "users 表存在")
  68. check(db.Migrator().HasTable(&Order{}), "orders 表存在")
  69. check(db.Migrator().HasIndex(&User{}, "pk_users"), "主键索引 pk_users")
  70. check(db.Migrator().HasIndex(&User{}, "idx_users_email"), "唯一索引 idx_users_email")
  71. check(db.Migrator().HasIndex(&Order{}, "idx_orders_user_id"), "普通索引 idx_orders_user_id")
  72. fmt.Println("[2] AutoMigrate 加列 (AddField 路径)")
  73. if err := db.AutoMigrate(&UserWithAddr{}); err != nil {
  74. fmt.Println(" FAIL: AutoMigrate 加列:", err)
  75. os.Exit(1)
  76. }
  77. check(db.Migrator().HasColumn(&UserWithAddr{}, "addr"), "addr 列出现")
  78. fmt.Println("[3] CRUD")
  79. u := &UserWithAddr{Name: "alice", Age: 30, Email: "alice@example.com", Addr: "Beijing"}
  80. if err := db.Create(u).Error; err != nil {
  81. fmt.Println(" FAIL: Create:", err)
  82. os.Exit(1)
  83. }
  84. check(u.ID > 0, "Create 回填主键 ID=%d", u.ID)
  85. var got UserWithAddr
  86. if err := db.First(&got, u.ID).Error; err != nil {
  87. fmt.Println(" FAIL: First:", err)
  88. os.Exit(1)
  89. }
  90. check(got.Name == "alice" && got.Age == 30 && got.Addr == "Beijing",
  91. "First 读回 name=%s age=%d addr=%s", got.Name, got.Age, got.Addr)
  92. for i := 0; i < 3; i++ {
  93. if err := db.Create(&UserWithAddr{Name: fmt.Sprintf("u%d", i), Age: 20 + i,
  94. Email: fmt.Sprintf("u%d@example.com", i)}).Error; err != nil {
  95. fmt.Println(" FAIL: Create:", err)
  96. os.Exit(1)
  97. }
  98. }
  99. var list []UserWithAddr
  100. if err := db.Where("age >= ?", 20).Order("age desc").Limit(2).Find(&list).Error; err != nil {
  101. fmt.Println(" FAIL: Find:", err)
  102. os.Exit(1)
  103. }
  104. // alice(30) u2(22) u1(21) u0(20) 满足 age>=20, desc 前 2 = 30,22
  105. age0, age1 := 0, 0
  106. if len(list) >= 2 {
  107. age0, age1 = list[0].Age, list[1].Age
  108. }
  109. check(len(list) == 2 && age0 == 30 && age1 == 22,
  110. "Find+Where+Order+Limit 取回 %d 行 (ages %d,%d)", len(list), age0, age1)
  111. if err := db.Model(&UserWithAddr{}).Where("id = ?", u.ID).
  112. Updates(map[string]interface{}{"age": 31}).Error; err != nil {
  113. fmt.Println(" FAIL: Updates:", err)
  114. os.Exit(1)
  115. }
  116. var after UserWithAddr
  117. _ = db.First(&after, u.ID)
  118. check(after.Age == 31, "Updates 生效 age=31")
  119. var n int64
  120. _ = db.Model(&UserWithAddr{}).Count(&n)
  121. check(n == 4, "Count=%d (期望 4)", n)
  122. if err := db.Where("email = ?", "alice@example.com").Delete(&UserWithAddr{}).Error; err != nil {
  123. fmt.Println(" FAIL: Delete:", err)
  124. os.Exit(1)
  125. }
  126. _ = db.Model(&UserWithAddr{}).Count(&n)
  127. check(n == 3, "Delete 后 Count=%d (期望 3)", n)
  128. fmt.Println("[4] 事务 (回滚 / 提交)")
  129. boom := errors.New("rollback please")
  130. err = db.Transaction(func(tx *gorm.DB) error {
  131. _ = tx.Create(&UserWithAddr{Name: "ghost", Email: "ghost@example.com"})
  132. return boom
  133. })
  134. check(errors.Is(err, boom), "回调错误透出, 事务回滚")
  135. _ = db.Model(&UserWithAddr{}).Count(&n)
  136. check(n == 3, "回滚后仍 %d 行", n)
  137. err = db.Transaction(func(tx *gorm.DB) error {
  138. return tx.Create(&UserWithAddr{Name: "carol", Age: 41, Email: "carol@example.com"}).Error
  139. })
  140. check(err == nil, "事务提交成功")
  141. _ = db.Model(&UserWithAddr{}).Count(&n)
  142. check(n == 4, "提交后 %d 行", n)
  143. fmt.Println("[5] Preload 关联 (独立查询, 不 JOIN)")
  144. // carol 的 ID
  145. var carol UserWithAddr
  146. _ = db.Where("email = ?", "carol@example.com").First(&carol).Error
  147. for i := 1; i <= 2; i++ {
  148. if err := db.Create(&Order{UserID: carol.ID, Amount: float64(100 * i)}).Error; err != nil {
  149. fmt.Println(" FAIL: Create Order:", err)
  150. os.Exit(1)
  151. }
  152. }
  153. var users []User
  154. if err := db.Preload("Orders").Where("email = ?", "carol@example.com").Find(&users).Error; err != nil {
  155. fmt.Println(" FAIL: Preload:", err)
  156. os.Exit(1)
  157. }
  158. check(len(users) == 1 && len(users[0].Orders) == 2,
  159. "Preload Orders: %d 个用户 / %d 张订单", len(users), ordersOf(users))
  160. if failed {
  161. fmt.Println("\n结果: 存在失败项")
  162. os.Exit(1)
  163. }
  164. fmt.Println("\n结果: 全部通过")
  165. }
  166. func ordersOf(users []User) int {
  167. if len(users) >= 1 {
  168. return len(users[0].Orders)
  169. }
  170. return 0
  171. }
  172. // time 保留引用 (模型扩展 DATETIME 列时可用的提示)。
  173. var _ = time.Time{}