golang working effectively with Null* types

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

golang working effectively with Null* types

问题

如果你正在使用数据库,Null* 类型在大多数情况下都很有用,因为通常不希望传递一个“零”值,而是希望使用 NOT NULL 约束等来提醒你没有传递所有必要的数据。

所以你可以创建以下结构:

type Role struct {
    Id   sql.NullInt64
    Code sql.NullString
}

这很好,但是现在你无法直接访问属性,而必须使用 Role.Id.Value 来获取和设置值,在一个大型应用程序中,每次访问属性时都需要进行额外的步骤,这会变得非常繁琐。

如果你可以直接赋值,例如 Role.Code = "Fsfs",并且在需要进行空值检查时可以使用 Role.Code.IsNull,那将非常方便。这种情况是否可能?

英文:

If one is working with a database, a Null* type is useful for most scenarios as one typically does not want a "zero" value going through, you want the NOT NULL constraints etc. to kick up and remind you that you haven't passed in all the data necessary.

So you create a structure like the following:

type Role struct {
	Id   sql.NullInt64
	Code sql.NullString
}

Thats great, but now you cannot get direct access to the properties and have to use Role.Id.Value to both get and set, this is going to get pretty old in a large app when you have to have the extra step every single time you want access to the properties.

It would be nice if you could assign directly eg. Role.Code = "Fsfs", and be able to do something like Role.Code.IsNull when you are interested in null checking. Is such a thing possible?

答案1

得分: 2

使用中间指针值是一个选项吗?

package main

import "fmt"

type tmp struct {
	Value int
}

func getInt() *int {
	i := 123

	return &i
}

func main() {
	// Re
	var v *int

	v = nil

	fmt.Printf("%T / %v\n", v, v)

	if v == nil {
		println("nil...")
	}

	v = getInt()

	fmt.Printf("%T / %v\n", v, *v)

	if v != nil {
		println("not nil...")
	}

	s := tmp{*v}

	fmt.Printf("%T / %v\n", s, s)
}

http://play.golang.org/p/lBrwTKh6-v

英文:

Is using intermediate pointer value(s) an option?

package main

import "fmt"

type tmp struct {
	Value int
}

func getInt() *int {
	i := 123

	return &i
}

func main() {
	// Re
	var v *int

	v = nil

	fmt.Printf("%T / %v\n", v, v)

	if v == nil {
		println("nil...")
	}

	v = getInt()

	fmt.Printf("%T / %v\n", v, *v)

	if v != nil {
		println("not nil...")
	}

	s := tmp{*v}

	fmt.Printf("%T / %v\n", s, s)
}

http://play.golang.org/p/lBrwTKh6-v

答案2

得分: 1

你可以这样访问 Role.Code:

var r *Role
r.Code = *code

你可以这样检查是否为 null:

fmt.Println(r.Code, r.Code.Valid)

如果你在不使用 sql.Scanner 的情况下手动更改 r.Code 的值,可以使用 Setter 方法:

func (r *Role) SetCode(code string) {
  r.Code.String = code
  r.Code.Valid = true
}
func main() {
  var r *Role
  r.SetCode("mi")
  if r.Code.Valid {
    fmt.Println(r.Code)
  }
}

你可以在这里尝试一下:https://play.golang.org/p/faxQUm-2lr

英文:

You can access Role.Code like that:

var r *Role
r.Code = *code

You can check for null like this:

fmt.Println(r.Code, r.Code.Valid)

If you change the value of r.Code manually without using an sql.Scanner a Setter could be helpful:

func (r *Role) SetCode(code string) {
  r.Code.String = code
  r.Code.Valid = true
}
func main() {
var r *Role	
r.SetCode("mi")
if r.Code.Valid {
  fmt.Println(r.Code)
}

I tried this out here: https://play.golang.org/p/faxQUm-2lr

答案3

得分: 0

保持应用程序和数据库代码分离

// 角色属于应用程序代码,不做妥协。

type Role struct {
Id int64
Code string
}

对数据库建模。

// 数据库有表和列。

type Table struct {
Name string
Columns []string
}

var RoleTable = Table{
Name: "roles",
Columns: []string{
"id",
"code",
},
}

编写一次代码以在模型和数据库行之间进行转换。

// 数据库包需要使其工作。

// 将模型写入数据库行。
type Writer struct {
Role
}

func (w *Writer) Write() []interface{} {
return []interface{}{
w.Role.Id,
sql.NullString{
Valid: len(w.Role.Code) > 0,
String: w.Role.String,
},
}
}

// 将数据库行读入模型。
type Reader struct {
Id int64
Code sql.NullString
}

func (r *Reader) Scan(row *sql.Row) error {
return row.Scan(
&r.Id,
&r.Code,
)
}

func (r *Reader) Read() Role {
return Role{
Id: r.Id,
Code: r.Code.String,
}
}

您的模式与应用程序模型解耦。您可以在保存或加载时展开用户联系方式等结构。

// 应用程序代码中的嵌套结构。
type User struct {
TwitterProfile struct {
Id string
ScreenName string
}
}

// 数据库行是规范化的扁平结构。
var UserTable = Table{
Name: "users",
Columns: []string{
"twitter_id",
"twitter_screen_name",
},
}

它很灵活。您甚至可以在没有中间结构的情况下扫描连接行。

type RowMux struct {
vs []interface{}
}

func (mux *RowMux) Scan(vs ...interface{}) error {
mux.vs = append(mux.vs, vs...)
return nil
}

func (mux *RowMux) Mux(row *sql.Row) error {
return row.Scan(mux.vs...)
}

// 扫描连接行!
row := db.QueryRow(SELECT users.*, roles.* FROM users JOIN roles ON users.id = roles.user_id WHERE users.twitter_id = "123")
mux := &RowMux{}
userReader := &UserReader{}
userReader.Scan(mux)
roleReader := &RoleReader{}
roleReader.Scan(mux)
if err := mux.Mux(row); err != nil {
panic(err)
}
user := userReader.Read()
role := roleReader.Read()

英文:

Keep app and database code separate.

// Role belongs to app code, no compromises.

type Role struct {
    Id int64
    Code string
}

Model the database.

// Database has tables with columns.  

type Table struct {
    Name string
    Columns []string
}

var RoleTable = Table{
    Name: "roles",
    Columns: []string{
        "id",
        "code",
    },
}

Write code once to convert between model and database row.

// Database package needs to make it work.

// Write a model to database row. 
type Writer struct {
    Role
}

func (w *Writer) Write() []interface{} {
    return []interface{}{
        w.Role.Id,
        sql.NullString{
            Valid: len(w.Role.Code) > 0,
            String: w.Role.String,
        },
    }
}

// Read a database row into model. 
type Reader struct {
    Id int64
    Code sql.NullString
}

func (r *Reader) Scan(row *sql.Row) error {
    return row.Scan(
        &r.Id,
        &r.Code,
    )
}

func (r *Reader) Read() Role {
    return Role{
        Id: r.Id,
        Code: r.Code.String,
    }
}

Your schema is decoupled from app model. You can flatten structures like user contact details when saving or loading.

// Nested struct in app code. 
type User struct {
    TwitterProfile struct {
        Id string
        ScreenName string
    }
}

// Database row is normalized flat. 
var UserTable = Table{
    Name: "users",
    Columns: []string{
        "twitter_id",
        "twitter_screen_name",
    },
}

It's flexible. You can even scan join rows without intermediate structs.

type RowMux struct {
    vs []interface{}
}

func (mux *RowMux) Scan(vs ...interface{}) error {
    mux.vs = append(mux.vs, vs...)
    return nil
}

func (mux *RowMux) Mux(row *sql.Row) error {
    return row.Scan(mux.vs...)
}

// Scan join rows!
row := db.QueryRow(`
    SELECT users.*, roles.*
    FROM users
    JOIN roles ON users.id = roles.user_id
    WHERE users.twitter_id = "123"
`)
mux := &RowMux{}
userReader := &UserReader{}
userReader.Scan(mux)
roleReader := &RoleReader{}
roleReader.Scan(mux)
if err := mux.Mux(row); err != nil {
    panic(err)
}
user := userReader.Read()
role := roleReader.Read()

huangapple
  • 本文由 发表于 2014年1月16日 09:38:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/21151876.html
匿名

发表评论

匿名网友

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

确定