13/03/2020 - GO, MYSQL
Bu örnek, CRUD işlemlerini 'yazma' ve 'okuma' olarak iki bölüme ayırır. Yazma bölüme INSERT
, UPDATE
ve DELETE
, okuma ise SELECT
sorgularını ilgilendirir. Ayrıca uzun süren sorguları sonlandırmak için context.WithTimeout
kullanıyoruz. SELECT
sorgularına dikkat edin, çünkü bunlar MAX_EXECUTION_TIME
adlı bir özelliğe bağlıdır.
Sorgu değişkenlerinin SQL Injection içermediğinden %100 emin değilseniz bunu tercih edin.
Ağ gidiş dönüş sayısını üçe katladığı için yavaş bir işlemdir (Prepare
, Execute
ve Close
). Sorgu, ?
bağımsız değişken yer tutucularını kullanır.
Aşağıdaki her iki örnek de aynı şekilde çalışacaktır, bu yüzden daha temiz olduğundan ikincisini kullanmanızı öneririm. Ayrıca, sorgu değişkenleri dinamik veya statik olsada, ağ gidiş dönüş sayısı üç katında kalacaktır.
func Create(args ...interface{}) error {
ctx, cancel := context.WithTimeout(context.Background(), 3 * time.Second)
defer cancel()
stmt, err := db.PrepareContext(ctx, `
INSERT INTO leagues
(uuid, name, address, int_rank, is_active, founded_at, created_at)
VALUES
(?, ?, ?, ?, ?, ?, UTC_TIMESTAMP())
`)
if err != nil {
return err
}
defer stmt.Close()
res, err := stmt.ExecContext(ctx, args...)
if err != nil {
return err
}
tot, err := res.RowsAffected()
if err != nil {
return err
}
if tot != 1 {
return errors.New("no rows were affected")
}
return nil
}
func Create(args ...interface{}) error {
ctx, cancel := context.WithTimeout(context.Background(), 3 * time.Second)
defer cancel()
q := `
INSERT INTO leagues
(uuid, name, address, int_rank, is_active, founded_at, created_at)
VALUES
(?, ?, ?, ?, ?, ?, UTC_TIMESTAMP())
`
res, err := db.ExecContext(ctx, q, args...)
if err != nil {
return err
}
tot, err := res.RowsAffected()
if err != nil {
return err
}
if tot != 1 {
return errors.New("no rows were affected")
}
return nil
}
2020-03-13T13:14:36.035537Z 9 Prepare INSERT INTO leagues
(uuid, name, address, int_rank, is_active, founded_at, created_at)
VALUES
(?, ?, ?, ?, ?, ?, UTC_TIMESTAMP())
2020-03-13T13:14:36.038073Z 9 Execute INSERT INTO leagues
(uuid, name, address, int_rank, is_active, founded_at, created_at)
VALUES
('6a980454-0727-4260-a757-ce619e79af83', 'League 1', 'Address', 2, 0, '2001-01-01', UTC_TIMESTAMP())
2020-03-13T13:14:36.052590Z 9 Close stmt
Sorgu değişkenlerinin SQL Injection içermediğinden %100 eminseniz bunu tercih edin.
Tek bir ağ gidiş dönüşü (Query
) çalıştıracağından hızlı bir işlemdir. Sorgu, uygun değişken biçimlendiricileri kullanılarak fmt.Sprintf
işleviyle biçimlendirilir.
func Create(args ...interface{}) error {
ctx, cancel := context.WithTimeout(context.Background(), 3 * time.Second)
defer cancel()
q := fmt.Sprintf(`
INSERT INTO leagues
(uuid, name, address, int_rank, is_active, founded_at, created_at)
VALUES
('%s', '%s', '%s', %d, %t, '%s', UTC_TIMESTAMP())
`,
args...,
)
res, err := db.ExecContext(ctx, q)
if err != nil {
return err
}
tot, err := res.RowsAffected()
if err != nil {
return err
}
if tot != 1 {
return errors.New("no rows were affected")
}
return nil
}
2020-03-13T13:30:26.812083Z 15 Query INSERT INTO leagues
(uuid, name, address, int_rank, is_active, founded_at, created_at)
VALUES
('2250b0d9-365a-4655-a116-1b092f6d6f20', 'League 1', 'Address', 2, false, '2001-01-01', UTC_TIMESTAMP())
Uzun çalışan sorguları iptal etmek için her ne kadar context.WithTimeout
kullanıyorsakta, MySQL protokol yapısı nedeniyle temel sorguları sonlandırmayacaktır. İptal işlemi yalnızca Go düzeyinde gerçekleşir. Ancak, SELECT
sorguları için bu sorunu, MySQL sunucusuna verilen süre sınırından daha uzun sürerse, yürütmeyi sonlandırmasını istemek için MySQL'in MAX_EXECUTION_TIME sorgu ipucunu kullanarak çözebilirsiniz. Bu gibi durumlarda Error 3024: Query execution was interrupted, maximum statement execution time exceeded
hatası verilir.
Sorgu değişkenlerinin SQL Injection içermediğinden %100 emin değilseniz bunu tercih edin.
Ağ gidiş dönüş sayısını üçe katladığı için yavaş bir işlemdir (Prepare
, Execute
ve Close
). Sorgu, ?
bağımsız değişken yer tutucularını kullanır.
Sorgu değişkenleri dinamik veya statik olsada, ağ gidiş dönüş sayısı üç katında kalacaktır.
type League struct {
ID sql.NullInt64
UUID sql.NullString
Name sql.NullString
Address sql.NullString
IntRank sql.NullInt64
IsActive sql.NullBool
FoundedAt mysql.NullTime
CreatedAt mysql.NullTime
DeletedAt mysql.NullTime
}
func Read(uuid string) (League, bool, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3 * time.Second)
defer cancel()
row := db.QueryRowContext(ctx, `
SELECT
/*+ MAX_EXECUTION_TIME(3000) */
id, uuid, name, int_rank, address, is_active, founded_at, created_at, deleted_at
FROM leagues
WHERE
uuid = ?
LIMIT 1`,
uuid,
)
var model League
err := row.Scan(
&model.ID,
&model.UUID,
&model.Name,
&model.IntRank,
&model.Address,
&model.IsActive,
&model.FoundedAt,
&model.CreatedAt,
&model.DeletedAt,
)
switch {
case err == sql.ErrNoRows:
return model, false, nil // 404
case err != nil:
return model, false, err // 500
default:
return model, true, nil // 200
}
}
2020-03-13T16:00:11.624465Z 16 Prepare SELECT
id, uuid, name, int_rank, address, is_active, founded_at, created_at, deleted_at
FROM leagues
WHERE
uuid = ?
LIMIT 1
2020-03-13T16:00:11.643215Z 16 Execute SELECT
id, uuid, name, int_rank, address, is_active, founded_at, created_at, deleted_at
FROM leagues
WHERE
uuid = '2250b0d9-365a-4655-a116-1b092f6d6f20'
LIMIT 1
2020-03-13T16:00:11.674942Z 16 Close stmt
Sorgu değişkenlerinin SQL Injection içermediğinden %100 eminseniz bunu tercih edin.
Tek bir ağ gidiş dönüşü (Query
) çalıştıracağından hızlı bir işlemdir. Sorgu, uygun değişken biçimlendiricileri kullanılarak fmt.Sprintf
işleviyle biçimlendirilir.
func Read(uuid string) (League, bool, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3 * time.Second)
defer cancel()
q := fmt.Sprintf(`
SELECT
/*+ MAX_EXECUTION_TIME(3000) */
id, uuid, name, int_rank, address, is_active, founded_at, created_at, deleted_at
FROM leagues
WHERE
uuid = '%s'
LIMIT 1
`,
uuid,
)
row := db.QueryRowContext(ctx, q)
var model League
err := row.Scan(
&model.ID,
&model.UUID,
&model.Name,
&model.IntRank,
&model.Address,
&model.IsActive,
&model.FoundedAt,
&model.CreatedAt,
&model.DeletedAt,
)
switch {
case err == sql.ErrNoRows:
return model, false, nil // 404
case err != nil:
return model, false, err // 500
default:
return model, true, nil // 200
}
}
2020-03-13T16:06:49.526538Z 18 Query SELECT
id, uuid, name, int_rank, address, is_active, founded_at, created_at, deleted_at
FROM leagues
WHERE
uuid = '2250b0d9-365a-4655-a116-1b092f6d6f20'
LIMIT 1
Sorgu değişkenlerinin SQL Injection içermediğinden %100 emin değilseniz bunu tercih edin.
Ağ gidiş dönüş sayısını üçe katladığı için yavaş bir işlemdir (Prepare
, Execute
ve Close
). Sorgu, ?
bağımsız değişken yer tutucularını kullanır.
Sorgu değişkenleri dinamik veya statik olsada, ağ gidiş dönüş sayısı üç katında kalacaktır.
type League struct {
ID sql.NullInt64
UUID sql.NullString
Name sql.NullString
Address sql.NullString
IntRank sql.NullInt64
IsActive sql.NullBool
FoundedAt mysql.NullTime
CreatedAt mysql.NullTime
DeletedAt mysql.NullTime
}
func Read(limit, offset int) ([]League, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3 * time.Second)
defer cancel()
rows, err := db.QueryContext(ctx, `
SELECT
/*+ MAX_EXECUTION_TIME(3000) */
id, uuid, name, int_rank, address, is_active, founded_at, created_at, deleted_at
FROM leagues
LIMIT ?
OFFSET ?`,
limit,
offset,
)
var models []League
if err != nil {
return models, err // 500
}
defer rows.Close()
for rows.Next() {
var model League
err := rows.Scan(
&model.ID,
&model.UUID,
&model.Name,
&model.IntRank,
&model.Address,
&model.IsActive,
&model.FoundedAt,
&model.CreatedAt,
&model.DeletedAt,
)
if err != nil {
return models, err // 500
}
models = append(models, model)
}
if err = rows.Err(); err != nil {
return models, err // 500
}
return models, nil
}
2020-03-13T16:35:51.216389Z 22 Prepare SELECT
id, uuid, name, int_rank, address, is_active, founded_at, created_at, deleted_at
FROM leagues
LIMIT ?
OFFSET ?
2020-03-13T16:35:51.223332Z 22 Execute SELECT
id, uuid, name, int_rank, address, is_active, founded_at, created_at, deleted_at
FROM leagues
LIMIT 10
OFFSET 0
2020-03-13T16:35:51.245423Z 22 Close stmt
Sorgu değişkenlerinin SQL Injection içermediğinden %100 eminseniz bunu tercih edin.
Tek bir ağ gidiş dönüşü (Query
) çalıştıracağından hızlı bir işlemdir. Sorgu, uygun değişken biçimlendiricileri kullanılarak fmt.Sprintf
işleviyle biçimlendirilir.
func Read(limit, offset int) ([]League, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3 * time.Second)
defer cancel()
q := fmt.Sprintf(`
SELECT
/*+ MAX_EXECUTION_TIME(3000) */
id, uuid, name, int_rank, address, is_active, founded_at, created_at, deleted_at
FROM leagues
LIMIT %d
OFFSET %d
`,
limit,
offset,
)
rows, err := db.QueryContext(ctx, q)
var models []League
if err != nil {
return models, err // 500
}
defer rows.Close()
for rows.Next() {
var model League
err := rows.Scan(
&model.ID,
&model.UUID,
&model.Name,
&model.IntRank,
&model.Address,
&model.IsActive,
&model.FoundedAt,
&model.CreatedAt,
&model.DeletedAt,
)
if err != nil {
return models, err // 500
}
models = append(models, model)
}
if err = rows.Err(); err != nil {
return models, err // 500
}
return models, nil
}
2020-03-13T16:39:42.933817Z 23 Query SELECT
id, uuid, name, int_rank, address, is_active, founded_at, created_at, deleted_at
FROM leagues
LIMIT 10
OFFSET 0