你知道如何在不使用 ORM 的情况下将一对多关联绑定到 Golang 结构体吗?

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

Do you know how to bind one-many association to Golang struct without orm?

问题

你知道如何在不使用 ORM 的情况下,将一对多关联绑定到 Golang 结构体吗?

我想将下面的 SQL 结果绑定到以下的 Golang 结构体:

CREATE TABLE
    users (
        id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(255)
    );

CREATE TABLE
    posts (
        id INT AUTO_INCREMENT PRIMARY KEY,
        user_id INT NOT NULL,
        title VARCHAR(255) NOT NULL
    );

INSERT INTO users (name) VALUES ("one"), ("two");

INSERT INTO
    posts (user_id, title)
VALUES (1, "one's post"), (1, "one's second post"), (1, "one's third post"), (2, "two's post"), (2, "two's second post"), (2, "two's third post");

SELECT u.id, u.name, p.id, p.title
FROM users AS u
JOIN posts AS p ON u.id = p.user_id;
type (
  User struct {
    ID int
    Name string
    Posts []*Post
  }

  Post struct {
    ID int
    Title string
  }
)

我经常使用 MySQL,所以我想要使用 MySQL 的方法。但是如果你知道 PostgreSQL、MariaDb 等其他数据库的好方法,请教我那种方法。

英文:

Do you know how to bind a one-to-many association to a Golang struct without using an ORM?

I want to bind the SQL results below to the following Golang struct:

CREATE TABLE
    users (
        id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(255)
    );

CREATE TABLE
    posts (
        id INT AUTO_INCREMENT PRIMARY KEY,
        user_id INT NOT NULL,
        title VARCHAR(255) NOT NULL
    );

INSERT INTO users (name) VALUES ("one"), ("two");

INSERT INTO
    posts (user_id, title)
VALUES (1, "one's post"), (1, "one's second post"), (1, "one's third post"), (2, "two's post"), (2, "two's second post"), (2, "two's third post");

SELECT u.id, u.name, p.id, p.title
FROM users AS u
JOIN posts AS p ON u.id = p.user_id;
type (
  User struct {
    ID int
    Name string
    Posts []*Post
  }

  Post struct {
    ID int
    Title string
  }
)

I often use MySQL. So I want the method with MySQL. But if you know good method with PostgreSQL, MariaDb and so on, please teach me that method.

答案1

得分: 2

func example() ([]*User, error) {
	// 执行查询语句
	rows, err := db.Query(`SELECT ...`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	// 使用映射以便于查找已经扫描过的用户
	userMap := make(map[int]*User)

	// 遍历返回的行
	for rows.Next() {
		// 为查询返回的每一列声明一个变量
		var (
			uid   int
			uname string
			// 使用 sql.NullXxx 类型,因为你的 JOIN 可能会导致包含 NULL 值的行,
			// 或者在查询中使用 COALESCE,这样你就不需要使用这些类型。
			pid    sql.NullInt64
			ptitle sql.NullString
		)

		// 将当前行的列扫描到变量中
		if err := rows.Scan(&uid, &uname, &pid, &ptitle); err != nil {
			return nil, err
		}

		// 使用扫描到的 id 在映射中查找用户
		u, ok := userMap[uid]
		if !ok {
			// 如果找不到,则创建一个新的用户实例并将其存储在映射中
			u = &User{ID: uid, Name: uname}
			userMap[uid] = u
		}

		// 将帖子实例添加到用户中
		u.Posts = append(u.Posts, &Post{
			ID:    int(pid.Int64),
			Title: ptitle.String,
		})
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}

	// 将映射转换为切片
	users := make([]*User, 0, len(userMap))
	for _, u := range userMap {
		users = append(users, u)
	}
	return users, nil
}
英文:
func example() ([]*User, error) {
	// execute select query
	rows, err := db.Query(`SELECT ...`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	// use a map for easy lookup of already scanned users
	userMap := make(map[int]*User)

	// iterate over the returned rows
	for rows.Next() {
		// declare a variable for each column returned by the query
		var (
			uid   int
			uname string
			// Use sql.NullXxx types since your JOIN
			// can potentially result in rows with NULLs,
			// or use COALESCE in your query, then you won't
			// have to use these types.
			pid    sql.NullInt64
			ptitle sql.NullString
		)

		// scan the current row's columns into the variables
		if err := rows.Scan(&uid, &uname, &pid, &ptitle); err != nil {
			return nil, err
		}

		// lookup the user in the map using the scanned id
		u, ok := userMap[uid]
		if !ok {
			// if not found, create a new instance of the user and store it in the map
			u = &User{ID: uid, Name: uname}
			userMap[uid] = u
		}

		// add the post instance to the user
		u.Posts = append(u.Posts, &Post{
			ID:    int(pid.Int64),
			Title: ptitle.String,
		})
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}

	// convert map into slice
	users := make([]*User, 0, len(userMap))
	for _, u := range userMap {
		users = append(users, u)
	}
	return users, nil
}

答案2

得分: 1

如果你想在没有ORM的情况下将一个一对多的关联绑定到一个Golang结构体中,你可以使用Golang的MySQL驱动程序执行查询,从数据库中获取所需的数据,然后手动将结果映射到结构体中。

我将给你一个使用MySQL驱动程序实现这一目标的示例:

/* *  @Author: Sk Shahriar Ahmed Raka   * Email: skshahriarahmedraka@gmail.com   * @Last Modified by:   Sk Shahirar Ahmed Raka  * @Last Modified time: 2023-04-23 00:16:31  */


package main

import (
    "database/sql"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
)

type User struct {
    ID    int
    Name  string
    Posts []*Post
}

type Post struct {
    ID    int
    Title string
}

func main() {
    // 连接到MySQL数据库
    db, err := sql.Open("mysql", "username:password@tcp(localhost:3306)/databasename")
    if err != nil {
        fmt.Println("🚀 sql.Open() ~ err :", err)
    }
    defer db.Close()
    // 准备SQL查询语句
    query := `SELECT u.id, u.name, p.id, p.title FROM Users AS u JOIN posts AS p ON u.id = p.user_id `
    // 执行查询语句
    rows, err := db.Query(query)
    if err != nil {
        fmt.Println("🚀 db.Query(query)~ err :", err)
    }
    defer rows.Close()

    // 将结果映射到结构体
    Users := []*User{}
    var userMap = make(map[int]*User)
    for rows.Next() {
        userID := 0
        userName := ""
        postID := 0
        postTitle := ""

        err := rows.Scan(&userID, &userName, &postID, &postTitle)
        if err != nil {
            fmt.Println("🚀 rows.Scan() ~ err :", err)
        }

        user, ok := userMap[userID]
        if !ok {
            user = &User{ID: userID, Name: userName, Posts: make([]*Post, 0)}
            userMap[userID] = user
            Users = append(Users, user)
        }

        user.Posts = append(user.Posts, &Post{ID: postID, Title: postTitle})
    }
    // 打印结果
    for _, user := range Users {
        fmt.Printf("UserID: %d, Name: %s\n", user.ID, user.Name)
        for _, post := range user.Posts {
            fmt.Printf("    PostID: %d, Title: %s\n", post.ID, post.Title)
        }
    }
}

对于这个示例,首先你需要使用database/sql包的sql.Open函数连接到MySQL数据库,并准备用于获取所需数据的SQL查询语句。然后使用db.Query函数执行查询,并循环遍历结果行,手动将数据映射到UserPost结构体中。为了将结果映射到结构体,你首先创建一个基于ID存储User结构体的映射,然后循环遍历行来创建或更新User结构体及其关联的Post结构体。最后,通过循环遍历User结构体及其关联的Post结构体来打印结果。

英文:

If you want to bind a one-to-many association to a Golang struct without an ORM, you can use the MySQL driver for Golang to execute a query that fetches the required data from the database and then manually map the result to the struct.

I will give you an example of how you can achieve this using the MySQL driver:

/* *  @Author: Sk Shahriar Ahmed Raka   * Email: skshahriarahmedraka@gmail.com   * @Last Modified by:   Sk Shahirar Ahmed Raka  * @Last Modified time: 2023-04-23 00:16:31  */
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
type User struct {
ID    int
Name  string
Posts []*Post
}
type Post struct {
ID    int
Title string
}
func main() {
// Connect to the mysql database
db, err := sql.Open("mysql", "username:password@tcp(localhost:3306)/databasename")
if err != nil {
fmt.Println("🚀 sql.Open() ~ err : ", err)
}
defer db.Close()
// Prepare the SQL query in a string format
query := `SELECT u.id, u.name, p.id, p.title FROM Users AS u JOIN posts AS p ON u.id = p.user_id `
// Execute your  query string
rows, err := db.Query(query)
if err != nil {
fmt.Println("🚀 db.Query(query)~ err : ", err)
}
defer rows.Close()
// you have to map the results to the struct
Users := []*User{}
var userMap = make(map[int]*User)
for rows.Next() {
userID:=0
userName:=""
postID:=0
postTitle:=""
err := rows.Scan(&userID, &userName, &postID, &postTitle)
if err != nil {
fmt.Println("🚀 rows.Scan() ~ err : ", err)
}
user, ok := userMap[userID]
if !ok {
user = &User{ID: userID, Name: userName, Posts: make([]*Post, 0)}
userMap[userID] = user
Users = append(Users, user)
}
user.Posts = append(user.Posts, &Post{ID: postID, Title: postTitle})
}
// Print your results
for _, user := range Users {
fmt.Printf("UserID: %d, Name: %s\n", user.ID, user.Name)
for _, post := range user.Posts {
fmt.Printf("    PostID: %d, Title: %s\n", post.ID, post.Title)
}
}
}

For this example, first you need to connect to the MySQL database using the sql.Open function from the database/sql package and prepare the SQL query to fetch the required data. You then execute the query using the db.Query function and loop over the resulting rows to map the data to the User and Post structs manually.To map the results to the structs, you first create a map to store the User structs based on their ID, and then loop over the rows to create or update the User structs and their associated Post structs. you print the results by looping over the User structs and their associated Post structs.

huangapple
  • 本文由 发表于 2023年4月22日 18:22:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/76079136.html
匿名

发表评论

匿名网友

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

确定