• 【GORM】存取数组/自定义类型数据


    GORM存取数组类型数据

    GORM不支持数组类型变量数据的存取,可以通过GROM的自定义数据类型实现。

    一、GORM的自定义类型

    1. Scanner/Valuer接口

    GORM的自定义的数据类型必须实现Scanner/Valuer接口。

    (1) Scanner接口的Scan方法,是从数据库读取数据到Go变量时需要进行的解析处理,与解码的过程类型。

    (2) Valuer接口的Value方法,是将Go变量存到数据库时进行编码处理。

    2. 数组变量

    ​ 虽然GORM不支持数组变量,但是可以通过实现Scanner/Valuer接口,在数据库取/存时进行类似解码/编码的处理,使数组变量成为数据库可支持的变量类型。

    (1) 实现Sacnner接口

    • Scan函数,从数据库读取数据后,对其进行处理,然后获得Go类型的变量。
    type Strs []string
    
    func (m *Strs) Scan(val interface{}) error {
    	s := val.([]uint8)
    	ss := strings.Split(string(s), "|")
    	*m = ss
    	return nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 如上自定义Strs类型,底层类型未字符串数组。Scan函数将读取的val值,按照字符串处理,以"|"为分隔符进行切割,获得字符串数组类型,然后使用指针赋值。

    (2) 实现Valuer接口

    • Value函数,将数据存到数据库时,对数据进行处理,获得数据库支持的类型。
    func (m Strs) Value() (driver.Value, error) {
    	str := strings.Join(m, "|")
    	return str, nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 如上将Strs存入数据库前,将数组内的字符串用"|"拼接,获得数据库支持的string类型后再存入数据库。
    3. 测试
    • 定义如下函数进行测试

      (1) 模型变量

    type User struct {
    	ID   uint `gorm:"primary_key"`
    	Name string
    	Pics Strs `gorm:"type:longtext"`  // 自定义的数组类型,在数据库中存为长文本类型
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ​ (2) Service函数

    func SaveUser(user User) error {
    	var err error
    	err = db.Save(&user).Error
    	return err
    }
    
    func GetUser(name string) (User, error) {
    	var user User
    	err := db.First(&user, "name = ?", name).Error
    	return user, err
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    ​ (3) testService函数

    func TestSaveUser() {
       user := User{
          Name: "Jason",
          Pics: Strs{
             "123124",
             "gtsrbxrzsfcv",
          },
       }
       err := SaveUser(user)
       if err != nil {
          log.Panicln("保存失败!", err)
       } else {
          log.Println("保存成功!")
       }
    }
    
    func TestGetUser() {
       user, err := GetUser("Jason")
       if err != nil {
          log.Panicln("获取失败!", err)
       } else {
          log.Println("获取成功!", user)
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    ​ (4) 运行结果

    2022/07/30 18:18:41 保存成功!
    2022/07/30 18:18:41 获取成功! {3 Jason [123124 gtsrbxrzsfcv]}
    
    • 1
    • 2
    • 在数据库存储中体现为:
    mysql> select *from users;
    +----+-------+---------------------+
    | id | name  | pics                |
    +----+-------+---------------------+
    |  3 | Jason | 123124|gtsrbxrzsfcv |
    +----+-------+---------------------+
    1 row in set (0.00 sec)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    二、实际生产

    • 上面的例子中展示了Scanner/Valuer接口的简单使用,实际生产中将数组类型转换为json字符串进行保存,下面展示一个完整的例子,以一个User拥有一张包含信息的银行卡为例(当然可以通过一对一关联4444实现)。

      (1) 实现自定义类型

      type User struct {
         ID    uint `gorm:"primary_key"`
         Name  string
         Cards Card `gorm:"json"`
      }
      
      type Card struct {  // 指定json的Tag。
         Type     int    `json:"type"`
         Account  string `json:"account"`
         Password string `json:"password"`
      }
      
      // Scan 解码json字符串
      func (card *Card) Scan(val interface{}) error {
         b, _ := val.([]byte)
         return json.Unmarshal(b, card)
      }
      
      // Value 编码json
      func (card Card) Value() (value driver.Value, err error) {
      	return json.Marshal(card)
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22

    ​ (2) 数据表模型

    type User struct {
       ID    uint `gorm:"primary_key"`
       Name  string
       Cards Card `gorm:"json"`  // 指定为json类型
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

  • 相关阅读:
    软考 - 软件工程
    Java基本数据类型
    2 线程池-ThreadPoolExector分析
    使用postman做接口测试
    梯度下降、损失函数、神经网络的训练过程
    django-发送邮件
    GE IS420UCSCH2A-C-V0.1-A模拟量输入模块
    华为应用市场应用统计数据问题大揭秘
    MySql中事务详细理解学习(事务管理、事务隔离级别、事务传播机制)
    iperf3: error - unable to connect to server: No route to host 但嵌入式Linux设备
  • 原文地址:https://blog.csdn.net/js010111/article/details/126076320