Best practices for working with database/sql in Go.
1import (2 "database/sql"3 "time"4)56// 1. Connection pool configuration7db, err := sql.Open("postgres", dsn)8if err != nil {9 log.Fatal(err)10}11db.SetMaxOpenConns(25) // Maximum connections12db.SetMaxIdleConns(10) // Maximum idle connections13db.SetConnMaxLifetime(5 * time.Minute) // Maximum lifetime14db.SetConnMaxIdleTime(1 * time.Minute) // Maximum idle time1516// 2. Connection check17ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)18defer cancel()19if err := db.PingContext(ctx); err != nil {20 log.Fatal(err)21}2223// 3. QueryRow with context24var user User25err = db.QueryRowContext(ctx, "SELECT * FROM users WHERE id = $1", id).26 Scan(&user.ID, &user.Name, &user.Email)27if err != nil {28 if err == sql.ErrNoRows {29 return nil, ErrNotFound30 }31 return nil, fmt.Errorf("query user: %w", err)32}3334// 4. Prepared statements for repeated queries35prep, err := db.PrepareContext(ctx, "SELECT * FROM users WHERE email = $1")36if err != nil {37 log.Fatal(err)38}39defer prep.Close()4041// 5. Transactions42tx, err := db.BeginTx(ctx, nil)43if err != nil {44 log.Fatal(err)45}46defer tx.Rollback() // Rollback on error4748_, err = tx.ExecContext(ctx, "UPDATE accounts SET balance = balance - $1 WHERE id = $2", amount, fromID)49if err != nil {50 return err51}5253_, err = tx.ExecContext(ctx, "UPDATE accounts SET balance = balance + $1 WHERE id = $2", amount, toID)54if err != nil {55 return err56}5758return tx.Commit()5960// 6. Rows.Close() for multiple queries61rows, err := db.QueryContext(ctx, "SELECT * FROM users")62if err != nil {63 return err64}65defer rows.Close()6667for rows.Next() {68 var user User69 if err := rows.Scan(&user.ID, &user.Name); err != nil {70 return err71 }72 process(user)73}74return rows.Err() // Check errors after iteration
Errors: