Верните только что вставленную строку в Postgres с помощью sqlx
Я использую https://github.com/jmoiron/sqlx для отправки запросов в Postgres.
Можно ли вернуть данные всей строки при вставке новой строки?
Вот запрос, который я запускаю:
result, err := Db.Exec("INSERT INTO users (name) VALUES ($1)", user.Name)
Или я должен просто использовать свой существующий user
структура как источник правды о новой записи в базе данных?
3 ответа
Вот документы о транзакции sqlx
:
Результат имеет два возможных фрагмента данных: LastInsertId() или RowsAffected(), доступность которых зависит от драйвера. В MySQL, например, LastInsertId() будет доступен для вставок с ключом автоинкремента, но в PostgreSQL эту информацию можно получить только из обычного курсора строки с помощью предложения RETURNING.
Итак, я сделал полную демонстрацию того, как выполнить транзакцию, используя sqlx
, демонстрация создаст адресную строку в addresses
table, а затем создайте пользователя в users
стол с использованием нового address_id
PK как user_address_id
ФК пользователя.
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
PostgreSQL поддерживает RETURNING
синтаксис для INSERT
заявления.
Пример:
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. Только строки, которые были успешно вставлены или обновлены, будут возвращены.
Это пример кода, который работает с именованными запросами и структурами строгого типа для вставленных данных и идентификаторов.
Запрос и структура включены для покрытия используемого синтаксиса.
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
}