不支持的扫描,将 driver.Value 类型 []uint8 存储到类型 *[]string 中。

huangapple go评论79阅读模式
英文:

unsupported Scan, storing driver.Value type []uint8 into type *[]string

问题

我使用golanggingorp实现了rest api

员工结构体如下:

type Employee struct {
	Id         int64     `db:"id" json:"id"`
	Firstname  string    `db:"firstname" json:"firstname"`
	Lastname   string    `db:"lastname" json:"lastname"`
	Dob        time.Time `db:"dob" json:"dob"`
	Skills     []string  `db:"skills" json:"skills"`
}

发送POST请求时:

func PostEmployee(c *gin.Context) {
	var emp Employee
	c.Bind(&emp)

	skills, _ := json.Marshal(emp.Skills)

	if emp.Firstname != "" && emp.Lastname != "" {
		if insert, _ := dbmap.Exec(`INSERT INTO employee (firstname, lastname, dob, skills) VALUES (?, ?, ?, ?)`, emp.Firstname, emp.Lastname, emp.Dob, skills); insert != nil {
			emp_id, err := insert.LastInsertId()
			// ...
		}
		// ...
	}
}

这段代码将数据保存到mysql数据库中,运行正常。

为了从数据库中检索数据,实现了GET请求:

func GetEmployees(c *gin.Context) {
	var emps []Employee
	_, err := dbmap.Select(&emps, "SELECT * FROM employee")
	log.Println(err)
	if err == nil {
		c.JSON(200, emps)
	} else {
		c.JSON(404, gin.H{"error": "no employee(s) into the table"})
	}
}

GET查询无法从数据库中获取任何数据,log.Println(err)的日志显示:

Scan error on column index 4: unsupported Scan, storing driver.Value type []uint8 into type *[]string

有什么想法吗?

英文:

I have implemented rest api using golang, gin and gorp

Employee structure:

type Employee struct {
  Id			int64  `db:"id" json:"id"`
  Firstname	string `db:"firstname" json:"firstname"`
  Lastname	string `db:"lastname" json:"lastname"`
  Dob			time.Time `db:"dob" json:"dob"`
  Skills		[]string `db:skills json:"skills"`
}

In POST sending request as:

func PostEmployee(c *gin.Context) {
  var emp Employee
  c.Bind(&emp)

  skills, _ := json.Marshal(emp.Skills)

  if emp.Firstname != "" && emp.Lastname != "" {

	if insert, _ := dbmap.Exec(`INSERT INTO employee (firstname, lastname, dob, skills) VALUES (?, ?, ?, ?)`, emp.Firstname, emp.Lastname, emp.Dob, skills); insert != nil {
		emp_id, err := insert.LastInsertId()
    .....
    }
  ......
  }

This save data to mysql database, works perfect.

For retrieving data from database implemented GET request

 func GetEmployees(c *gin.Context) {
   var emps []Employee
   _, err := dbmap.Select(&emps, "SELECT * FROM employee")
   log.Println(err)
   if err == nil {
	 c.JSON(200, emps)
 } else {
	 c.JSON(404, gin.H{"error": "no employee(s) into the table"})
 }

GET query doesn't gives any data from database and log.Println(err) log says:

 Scan error on column index 4: unsupported Scan, storing driver.Value type []uint8 into type *[]string

Any ideas?

答案1

得分: 13

两种方法:

1. 为自定义类型实现sql.Scanner和driver.Valuer接口

优点:

  • 存储和检索简单
  • 不需要从另一个表中查询/加载

注意事项:

  • 字符串大小受sql列定义限制(在这种情况下为255)。根据数据库架构,这可能会被截断或导致需要处理的错误。
  • 在数据库级别删除特定技能需要额外的操作。
  • 搜索需要使用包含操作符而不是等于操作符。
  • 未来修改技能结构将会困难。

2. 将技能移动到单独的表中,并引用员工

优点:

  • 扩展表定义
  • 搜索能力更强
  • 在数据库级别更容易删除某人/所有人的技能

注意事项:

  • 需要另一个查询/加载来获取技能
  • 增加了数据库模式定义的复杂性
英文:

Two Approaches:
1. Implement sql.Scanner and driver.Valuer interfaces for a custom type
Benefits:

  • Simple to store and retrieve
  • Don't have to query/load from another table

Caveats:

  • String Size Is Limited by sql column definition (i.e. in this case its 255).
    Depending on your database architecture this will either be truncated or will result in an error that needs handling.
  • Jump through hoops to delete specific skills for someone/everyone at the database level.
  • Searching needs to be done through contains instead of equal operator
  • Modifying the skills structure will be difficult in the future.
package tgorm

import (
	"database/sql/driver"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/sqlite"
	"github.com/stretchr/testify/assert"
	"strings"
	"testing"
	"time"
)

type Skills []string

func (s Skills) Value() (driver.Value, error) {
	if len(s) == 0 {
		return "[]", nil
	}
    return fmt.Sprintf(`["%s"]`, strings.Join(s, `","`)), nil
}

func (s *Skills) Scan(src interface{}) (err error) {
	var skills []string
	switch src.(type) {
	case string:
		err = json.Unmarshal([]byte(src.(string)), &skills)
	case []byte:
		err = json.Unmarshal(src.([]byte), &skills)
	default:
		return errors.New("Incompatible type for Skills")
	}
	if err != nil {
		return
	}
	*s = skills
	return nil
}

type Employee struct {
	Id        int64     `db:"id" json:"id"`
	Firstname string    `db:"firstname" json:"firstname"`
	Lastname  string    `db:"lastname" json:"lastname"`
	Dob       time.Time `db:"dob" json:"dob"`
	Skills    Skills    `gorm:"type:varchar(255);" db:"skills" json:"skills"`
}

func (e Employee) TableName() string {
	return "employee"
}


func getMemoryDataBase() *gorm.DB {
	db, err := gorm.Open("sqlite3", ":memory:")
	if err != nil {
		panic(err)
	}
	db = db.Debug()
	db.AutoMigrate(Employee{})
	return db
}

func TestSaveEmployee(t *testing.T) {
	db := getMemoryDataBase()
	emp := Employee{
		Id:        1,
		Firstname: "Fake",
		Lastname:  "Emp",
		Dob:       time.Time{},
		Skills:    []string{"C#", "GO", "C++"},
	}
	skills, _ := json.Marshal(emp.Skills)
	err := db.Exec(`INSERT INTO employee (firstname, lastname, dob, skills) VALUES (?, ?, ?, ?)`, emp.Firstname, emp.Lastname, emp.Dob, skills).Error
	assert.Nil(t, err)
	var emps []Employee
	err = db.Raw("SELECT * FROM employee").Scan(&emps).Error
	assert.Nil(t, err)
	assert.Equal(t, []Employee{emp}, emps)
}

2. Move Skills into a separate table, with a reference to the employee.

Benefits:

  • Extend Table Definition
  • Better searching capabilities
  • Easier to drop skills for someone/everyone at the database level

Caveats:

  • Needs another query/load for Skills
  • Increased Database Schema Definition
package subgrom

import (
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/sqlite"
	"github.com/stretchr/testify/assert"
	"testing"
	"time"
)

type Skill struct {
	Id          int64  `db:"id" json:"id"`
	Skill       string `db:"skill" json:"skill"`
	EmployeeRef int64
}

type Employee struct {
	Id        int64     `db:"id" json:"id"`
	Firstname string    `db:"firstname" json:"firstname"`
	Lastname  string    `db:"lastname" json:"lastname"`
	Dob       time.Time `db:"dob" json:"dob"`
	Skills    []Skill   `db:"skills" json:"skills" gorm:"foreignkey:EmployeeRef"`
}

func (e Employee) TableName() string {
	return "employee"
}

func getMemoryDataBase() *gorm.DB {
	db, err := gorm.Open("sqlite3", ":memory:")
	if err != nil {
		panic(err)
	}
	db = db.Debug()
	db.AutoMigrate(Employee{}, Skill{})
	return db
}

func TestSaveEmployee(t *testing.T) {
	db := getMemoryDataBase()
	emp := Employee{
		Id:        1,
		Firstname: "Fake",
		Lastname:  "Emp",
		Dob:       time.Time{},
		Skills:    []Skill{{Skill: "C#"}, {Skill: "GO"}, {Skill: "C++"}},
	}
	err := db.Create(&emp).Error
	assert.Nil(t, err)
	var emps []Employee
	err = db.Preload("Skills").Find(&emps).Error
	assert.Nil(t, err)
	assert.Equal(t, []Employee{emp}, emps)
}

答案2

得分: 0

遇到类似的问题,对我来说问题是字段"scope_t"的顺序。

选择组 = SELECT id, name, fully_qualified_name, parent_id, scopes, scope_t FROM groups

在插入数据时,我将"scope_t"放在一个随机的位置,所以SQL返回了上述错误,原因是该位置与不同的数据类型进行了映射。

如果错误 := r.db.QueryRowContext(ctx, createGroup, group.Name, group.FullyQualifiedName,
pq.Array(group.Scopes), group.ParentID, userID, scope_type).Scan(&id); 错误 != nil {
返回nil,错误
}

TL;DR

在插入之前,还要检查可能会交换值的位置,从而导致类型错误。

英文:

Faced similar issue, for me problem was sequencing of field "scope_t".

selectGroup = `SELECT 
id,
name,
fully_qualified_name,
parent_id,
scopes,
scope_t
FROM groups `

When inserting the data I placed "scope_t" at a random place so SQL return with the error mentioned above reason being that position was mapped with a different data type.

if err := r.db.QueryRowContext(ctx, createGroup, group.Name, group.FullyQualifiedName,
pq.Array(group.Scopes), group.ParentID, userID, scope_type).Scan(&id); err != nil {
return nil, err
}

TL;DR

Before inserting also check the places you might be interchanging the values due to which type error is caused.

huangapple
  • 本文由 发表于 2016年12月29日 16:09:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/41375563.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定