我使用https://github.com/jmoiron/sqlx来查询Postgres。
在插入新行时,是否可能获取整个行数据?
以下是我运行的查询:
result, err := Db.Exec("INSERT INTO users (name) VALUES ($1)", user.Name)
我应该只使用现有的user
结构作为关于数据库中新条目的真实信息来源吗?
我使用https://github.com/jmoiron/sqlx来查询Postgres。
在插入新行时,是否可能获取整个行数据?
以下是我运行的查询:
result, err := Db.Exec("INSERT INTO users (name) VALUES ($1)", user.Name)
我应该只使用现有的user
结构作为关于数据库中新条目的真实信息来源吗?
sqlx
交易的文档:
因此我创建了一个完整的演示,展示如何使用结果包含两个可能的数据:LastInsertId() 或 RowsAffected(),可用性取决于驱动程序。例如,在 MySQL 中,插入具有自增键的记录时将可用 LastInsertId(),但在 PostgreSQL 中,仅能通过使用 RETURNING 子句从普通行游标中检索此信息。
sqlx
执行事务。该演示将在addresses
表中创建一个地址行,然后使用新的address_id
主键作为用户的user_address_id
外键,在users
表中创建一个用户。package transaction
import (
"database/sql"
"github.com/jmoiron/sqlx"
"log"
"github.com/pkg/errors"
)
import (
"github.com/icrowley/fake"
)
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"`
}
type ITransactionSamples interface {
CreateUserTransaction() (*User, error)
}
type TransactionSamples struct {
Db *sqlx.DB
}
func NewTransactionSamples(Db *sqlx.DB) ITransactionSamples {
return &TransactionSamples{Db}
}
func (ts *TransactionSamples) CreateUserTransaction() (*User, error) {
tx := ts.Db.MustBegin()
var lastInsertId int
err := tx.QueryRowx(`INSERT INTO addresses (address_id, address_city, address_country, address_state) VALUES ($1, $2, $3, $4) RETURNING address_id`, 3, fake.City(), fake.Country(), fake.State()).Scan(&lastInsertId)
if err != nil {
tx.Rollback()
return nil, errors.Wrap(err, "insert address error")
}
log.Println("lastInsertId: ", lastInsertId)
var user User
err = tx.QueryRowx(`INSERT INTO users (user_id, user_nme, user_email, user_address_id) VALUES ($1, $2, $3, $4) RETURNING *;`, 6, fake.UserName(), fake.EmailAddress(), lastInsertId).StructScan(&user)
if err != nil {
tx.Rollback()
return nil, errors.Wrap(err, "insert user error")
}
err = tx.Commit()
if err != nil {
return nil, errors.Wrap(err, "tx.Commit()")
}
return &user, nil
}
这里是测试结果:
☁ transaction [master] ⚡ go test -v -count 1 ./...
=== RUN TestCreateUserTransaction
2019/06/27 16:38:50 lastInsertId: 3
--- PASS: TestCreateUserTransaction (0.01s)
transaction_test.go:28: &transaction.User{UserID:6, UserNme:"corrupti", UserEmail:"reiciendis_quam@Thoughtstorm.mil", UserAddressId:sql.NullInt64{Int64:3, Valid:true}}
PASS
ok sqlx-samples/transaction 3.254s
这是一个与命名查询和强类型结构用于插入数据和ID的示例代码。
包括查询和结构以涵盖所使用的语法。
const query = `INSERT INTO checks (
start, status) VALUES (
:start, :status)
returning id;`
type Row struct {
Status string `db:"status"`
Start time.Time `db:"start"`
}
func InsertCheck(ctx context.Context, row Row, tx *sqlx.Tx) (int64, error) {
return insert(ctx, row, insertCheck, "checks", tx)
}
// insert inserts row into table using query SQL command
// table used only for loging, actual table name defined in query
// should not be used from services directly - implement strong type wrappers
// 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
}
INSERT INTO users(...) VALUES(...) RETURNING id, name, foo, bar
文档: https://www.postgresql.org/docs/9.6/static/sql-insert.html
可选的 RETURNING 子句会导致 INSERT 基于每个实际插入的行(或者更新,如果使用了 ON CONFLICT DO UPDATE 子句)计算并返回值。这主要用于获取由默认值提供的值,例如序列号。但是,任何使用表列的表达式都是允许的。RETURNING 列表的语法与 SELECT 的输出列表完全相同。只有成功插入或更新的行才会被返回。
INSERT
语句中使用RETURNING
语法。例如:INSERT INTO users(...) VALUES(...) RETURNING id, name, foo, bar
。 - pumbo