Hello I am using pgx to use my postgres, and I have doubts as to how I can transform a row in the database into an aggregate
I am using entities and value objects
without value object it seems easy using marshal, but using value object I think it is not a good idea to have fields exported and then my question comes in, how can I convert my line into a struct of my aggregate
my aggregrate :
type Email struct {
address string
}
type Password struct {
value string
}
type Name struct {
firstName string
lastName string
}
type Person struct {
Id string
Name valueObject.Name
Email valueObject.Email
Password valueObject.Password
Created time.Time
Updated time.Time
}
func NewPerson(name valueObject.Name, email valueObject.Email, password valueObject.Password) *Person {
id := uuid.New()
return &Person{
Id: id.String(),
Name: name,
Email: email,
Password: password,
Created: time.Now(),
Updated: time.Now(),
}
}
all my value objects have a method to get the private value through a function simulating a get, I didn’t put the rest of the code of my value objects so it wouldn’t get big
func to get all rows from table:
func (r *personRepository) GetAll() (persons []*entities.Person, err error) {
qry := `select id, first_name, last_name, email, password created_at, updated_at from persons`
rows, err := r.conn.Query(context.Background(), qry)
return nil, fmt.Errorf("err")
}
If someone can give me a glimpse of how I can pass this line from the bank to a struct of my aggregate using this value object
You can use something like this (not yet tested and need optimization):
func (r *personRepository) GetAll() (persons []*entities.Person, err error) {
qry := `select id, first_name, last_name, email, password, created_at, updated_at from persons`
rows, err := r.conn.Query(context.Background(), qry)
var items []*entities.Person
if err != nil {
// No result found with the query.
if err == pgx.ErrNoRows {
return items, nil
}
// Error happened
log.Printf("can't get list person: %v\n", err)
return items, err
}
defer rows.Close()
for rows.Next() {
// Build item Person for earch row.
// must be the same with the query column position.
var id, firstName, lastName, email, password string
var createdAt, updatedAt time.Time
err = rows.Scan(&id, &firstName, &lastName, &email,
&createdAt, updatedAt)
if err != nil {
log.Printf("Failed to build item: %v\n", err)
return items, err
}
item := &entities.Person{
Id: id,
FirstName: firstName,
// fill other value
}
// Add item to the list.
items = append(items, item)
}
return items, nil
}
Don't forget to add the comma after text password in your query.
I am using entities and value objects without value object it seems easy using marshal,
Sorry, I don't know about the value object in your question.
Related
Have this struct of User and Post and I try to make Name from User to be included within Post Struct when a user create a new post.
type User struct {
ID int
Name string
Created time.Time
}
type Post struct {
ID int
PostTitle string
PostDesc string
Created time.Time
}
How can I create something connected between this two struct such as Author of the Post ?
The goal is try to get the name of the post author which from User struct with the code below:
post, err := app.Models.Posts.GetPost(id)
GetPost() just run SELECT query and scan row
This approach is without any ORM.
It's a simple query that can return multiple rows. You've to scan the whole resultset and map each column on the struct's fields.
Keep in mind to always check for errors.
Below you can find the solution:
package main
import (
"database/sql"
"fmt"
"time"
_ "github.com/lib/pq"
)
type Post struct {
ID int
PostTitle string
PostDesc string
Created time.Time
UserID int
User User
}
type User struct {
ID int
Name string
Created time.Time
}
func main() {
conn, err := sql.Open("postgres", "host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable")
if err != nil {
panic(err)
}
defer conn.Close()
// get MAX id
var id int
conn.QueryRow(`SELECT MAX(id) FROM posts`).Scan(&id)
// insert
sqlInsertStmt := `INSERT INTO posts (id, post_title, post_desc, created, user_id) VALUES ($1,$2,$3,$4,$5)`
if _, err = conn.Exec(sqlInsertStmt, id+1, "TDD", "Introduction to Test-Driven-Development", time.Now(), 1); err != nil {
panic(err)
}
// read
rows, err := conn.Query(`SELECT posts.id, post_title, post_desc, posts.created, users.id, users.name, users.created FROM posts INNER JOIN users ON posts.user_id=users.id`)
if err != nil {
panic(err)
}
var posts []Post
for rows.Next() {
var post Post
if err = rows.Scan(&post.ID, &post.PostTitle, &post.PostDesc, &post.Created, &post.User.ID, &post.User.Name, &post.User.Created); err != nil {
panic(err)
}
posts = append(posts, post)
}
if err = rows.Err(); err != nil {
panic(err)
}
for _, v := range posts {
fmt.Printf("author name: %q\n", v.User.Name)
}
}
Let me know if this helps.
Edit
I've also included an example of INSERT in Postgres. To achieve it, we've to use the db.Exec() function, and provide the parameters.
Pay attention to how you construct the query as you can get a SQL-Injection vulnerability.
Lastly, in a real-world scenario, you shouldn't lookup for the MAX id in the posts table but should be automatically generated.
Give it a try to this solution, maybe it resolves your issue.
First, I defined the structs in this way:
// "Post" belongs to "User", "UserID" is the foreign key
type Post struct {
gorm.Model
ID int
PostTitle string
PostDesc string
Created time.Time
UserID int
User User
}
type User struct {
ID int
Name string
Created time.Time
}
In this way, you can say that Post belongs to User and access the User's information within the Post struct.
To query the records, you've to use Preload("User") to be sure to eager load the User records from the separate table.
Keep attention to the name you pass in as the argument in Preload, as it can be tricky.
Lastly, you'll be able to access data in the embedded struct (with the dot notation).
Below, you can find a complete working example (implemented with the use of Docker):
package main
import (
"fmt"
"time"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
// "Post" belongs to "User", "UserID" is the foreign key
type Post struct {
gorm.Model
ID int
PostTitle string
PostDesc string
Created time.Time
UserID int
User User
}
type User struct {
ID int
Name string
Created time.Time
}
func main() {
dsn := "host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
db.AutoMigrate(&Post{})
newPost := &Post{ID: 1, PostTitle: "Golang", PostDesc: "Introduction to Golang", Created: time.Now(), UserID: 1, User: User{ID: 1, Name: "John Doe", Created: time.Now()}}
db.Create(newPost)
var post Post
db.Preload("User").Find(&post, 1)
fmt.Printf("author name: %q\n", post.User.Name)
}
Let me know if I answered your question!
This question already has answers here:
How do I batch sql statements with package database/sql
(12 answers)
Golang Route for insert - PrepareContext error
(1 answer)
Need to insert struct directly in a PostgreSQL DB
(2 answers)
Closed 9 months ago.
The community reviewed whether to reopen this question 9 months ago and left it closed:
Original close reason(s) were not resolved
My question is not related to that one. My question was mostly on preparex and ctx. I have already done with the implementation using the db.NamedExec and my code is working for those. What I am trying to do here is to understand context.context and preparex. Implement using these two.
CODE SNIPPET:
model.go
type User struct {
ID int `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
DeletedAt time.Time `db:"deleted_at" json:"deleted_at"`
Username string `db:"username" json:"username"`
Password string `db:"password" json:"password"`
FirstName string `db:"first_name" json:"first_name"`
LastName string `db:"last_name" json:"last_name"`
Phone string `db:"phone" json:"phone"`
Status bool `db:"status" json:"status"`
Addrs []UserAddress
}
Query:
queryInsertUserData = `
INSERT INTO users (id, created_at, updated_at, deleted_at, username, password, first_name, last_name, phone, status) VALUES($1, now(), now(), $2, $3, $4, $5, $6, $7, $8)
`
type queries struct {
insertUserData,
insertAddrData *sqlx.Stmt
}
//prepareStatement is a method for preparing sqlx statement
func (queries *queries) prepareStatement(db *sqlx.DB, query string) (*sqlx.Stmt, error) {
stmt, err := db.Preparex(query) //https://go.dev/doc/database/prepared-statements
if err != nil {
return nil, err
}
return stmt, err
}
constructUserData, err := queries.prepareStatement(db, queryInsertUserData)
queries.insertUserData = constructUserData
Implementation:
// Insert User data
func (postgres *Postgres) InsertUserData(ctx context.Context) (*entity.User, error) {
c := entity.User{}
err := postgres.queries.insertUserData.SelectContext(ctx, &c) //<---here
if err != nil {
return nil, err
}
return &c, nil
}
my ctx is :
ctx = context.WithValue(ctx, "ID", 1)
ctx = context.WithValue(ctx, "Username", "John")
ctx = context.WithValue(ctx, "Password", "pass")
when I am passing to postgres.queries.insertUserData.SelectContext(ctx, &c)
I am getting: sql: expected 8 arguments, got 0
why it is saying got 0? Can anyone help me with this? How to pass ctx and provide the insert query values?
I didn't get the structure of your code, but from the main context (inserting query) you should do something like this:
package main
import (
"context"
"database/sql"
"log"
)
var db *sql.DB
type User struct {
ID int `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
DeletedAt time.Time `db:"deleted_at" json:"deleted_at"`
Username string `db:"username" json:"username"`
Password string `db:"password" json:"password"`
FirstName string `db:"first_name" json:"first_name"`
LastName string `db:"last_name" json:"last_name"`
Phone string `db:"phone" json:"phone"`
Status bool `db:"status" json:"status"`
Addrs []UserAddress
}
func main() {
users := []User {
{...User 1 data...},
{...User 2 data...},
}
stmt, err := db.Prepare("INSERT INTO users (id, created_at, updated_at, deleted_at, username, password, first_name, last_name, phone, status) VALUES($1, now(), now(), $2, $3, $4, $5, $6, $7, $8)")
if err != nil {
log.Fatal(err)
}
defer stmt.Close() // Prepared statements take up server resources and should be closed after use.
for id, user := range users {
if _, err := stmt.Exec(id+1, user.Username, user.Password,... other data); err != nil {
log.Fatal(err)
}
}
}
also, you can use the Gorm powerful package for using all relational databases.
I use this helper for inserts.
Works with query syntax as follows:
INSERT INTO checks (
status) VALUES (:status)
returning id;
Sample struct
type Row struct {
Status string `db:"status"`
}
// Insert inserts row into table using query SQL command
// table used only for loging, actual table name defined in query
// function expects Query with named parameters
func Insert(ctx context.Context, row interface{}, query string, table string, tx *sqlx.Tx) (int64, error) {
// convert named query to native parameters format
query, args, err := tx.BindNamed(query, row)
if err != nil {
return 0, fmt.Errorf("cannot bind parameters for insert into %q: %w", table, err)
}
var id struct {
Val int64 `db:"id"`
}
err = sqlx.GetContext(ctx, tx, &id, query, args...)
if err != nil {
return 0, fmt.Errorf("cannot insert into %q: %w", table, err)
}
return id.Val, nil
}
type Product struct {
ID string `json:"id"`
Name string `json:"name"`
Area string `json:"area"`
OwnerName string `json:"ownerName"`
Value int `json:"cost"`
Budget int `json:"budget"`
}
func (pc *ProductTransferSmartContract) AddProduct(ctx contractapi.TransactionContextInterface, id string, name string, area string, ownerName string, cost int, budget int) error {
productJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return fmt.Errorf("Failed to read the data from world state", err)
}
if productJSON != nil {
return fmt.Errorf("the product %s already exists", id)
}
prop := Product{
ID: id,
Name: name,
Area: area,
OwnerName: ownerName,
Value: cost,
}
productBytes, err := json.Marshal(prop)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, productBytes)
}
here i want to attach budget only with owner name so that if owner got changed i can change the budget too. Here i have written chaincode in golang.
If requirement is to restrict budget access only for owner, then private data collection can be used. Corresponding APIs are getPrivateData and putPrivateData.
I was trying to make a rest API for user registration and on that api there is a field named "gender", so I'm receiving that field as Struct but on user table there is no field for "gender". Is it possible to skip that "gender" field from struct while inserting with gorm?
This is my DataModel
package DataModels
type User struct {
Id uint `json:"id"`
Otp int `json:"otp"`
UserId string `json:"user_id"`
UserType string `json:"user_type"`
FullName string `json:"full_name"`
MobileNo string `json:"mobile"`
Email string `json:"email"`
Gender string `json:"gender"` // I want to skip this filed while inserting to users table
Password string `json:"password"`
}
func (b *User) TableName() string {
return "users"
}
This my Controller Function
func CreateUser(c *gin.Context) {
var user Models.User
_ = c.BindJSON(&user)
err := Models.CreateUser(&user) // want to skip that gender filed while inserting
if err != nil {
fmt.Println(err.Error())
c.AbortWithStatus(http.StatusNotFound)
} else {
c.JSON(http.StatusOK, user)
}
}
This is my model function for inserting
func CreateUser(user *User) (err error) {
if err = Config.DB.Create(user).Error; err != nil {
return err
}
return nil
}
GORM allows you to ignore the field with tag, use gorm:"-" to ignore the field
type User struct {
...
Gender string `json:"gender" gorm:"-"` // ignore this field when write and read
}
Offical doc details about Field-Level Permission
Eklavyas Answer is omitting gender always, not just on Create.
If I am correct you want to Skip the Gender field within the Registration. You can use Omit for that.
db.Omit("Gender").Create(&user)
Here is the code:
// User Model
type User struct {
UserID int `db:"user_id"`
UserNme string `db:"user_nme"`
UserEmail string `db:"user_email"`
UserAddressID sql.NullInt64 `db:"user_address_id"`
}
func (ur *userRepository) FindAll() ([]models.User, error) {
var users []models.User
query := "select user_nme from users"
err := ur.Db.Select(&users, query)
if err != nil {
return nil, err
}
return users, nil
}
Result:
&[]models.User{models.User{UserID:0, UserNme:"Jay Durgan", UserEmail:"", UserAddressID:sql.NullInt64{Int64:0, Valid:false}}, models.User{UserID:0, UserNme:"Arne Balistreri", UserEmail:"", UserAddressID:sql.NullInt64{Int64:0, Valid:false}}, models.User{UserID:0, UserNme:"Greg Willms", UserEmail:"", UserAddressID:sql.NullInt64{Int64:0, Valid:false}}, models.User{UserID:0, UserNme:"Lady Aisha McLaughlin", UserEmail:"", UserAddressID:sql.NullInt64{Int64:0, Valid:false}}, models.User{UserID:0, UserNme:"Mrs. Phoebe Boyle", UserEmail:"", UserAddressID:sql.NullInt64{Int64:0, Valid:false}}}%
As you can see, I didn't query user_id, user_email and user_address_id columns, but the result give me these fields with zero value.
So, is there a way only get the fields correspond to the queried columns?
Beside, I don't want to write it like this: &user.userNme, &user.xxx, &user.xxx which means write each field and populate it. It's too verbose.
Expected result is: {UserNme: "Jay Durgan"}...
By using struct, you can't.
The other fields will still be there with it's zero value. The fields are property of the struct so whether you need it or not, whether it stored retrieved value from db operation or not, all the fields will still be there.
The only solution for your case is by using map, so only value of correspondent fields will be retrieved.
var users []map[string]interface{}
query := "select user_nme from users"
err := ur.Db.Select(&users, query)
if err != nil {
return nil, err
}
Result:
&[]map[string]interface{}{map[string]interface{}{UserNme:"Jay Durgan"}, ...}
You can try this
func (ur *userRepository) FindAll() ([]models.User, error) {
users := []models.User{}.UserNme
query := "select user_nme from users"
err := ur.Db.Select(&users, query)
if err != nil {
return nil, err
}
return users, nil
}
Also you can follow this link
http://go-database-sql.org/retrieving.html