Convert Figma logo to code with AI

jmoiron logosqlx

general purpose extensions to golang's database/sql

15,960
1,072
15,960
359

Top Related Projects

36,491

The fantastic ORM library for Golang, aims to be developer friendly

12,095

Generate type-safe code from SQL

Generate a Go ORM tailored to your database schema.

3,591

SQL-first Golang ORM

2,347

SQL builder and query library for golang

15,388

An entity framework for Go

Quick Overview

sqlx is a powerful and feature-rich library for Go that extends the standard database/sql package. It provides a set of extensions to enhance database operations, including named query support, advanced scanning capabilities, and convenient helpers for common tasks.

Pros

  • Supports multiple database drivers (MySQL, PostgreSQL, SQLite, and more)
  • Offers both low-level and high-level APIs for flexibility
  • Provides efficient connection pooling and resource management
  • Includes support for named parameters and struct mapping

Cons

  • Learning curve for developers new to Go or SQL
  • May introduce additional complexity for simple database operations
  • Limited support for NoSQL databases
  • Some advanced features may not be compatible with all supported database drivers

Code Examples

  1. Basic query execution:
rows, err := db.Query("SELECT * FROM users WHERE age > ?", 18)
if err != nil {
    log.Fatal(err)
}
defer rows.Close()
  1. Named query with struct mapping:
type User struct {
    ID   int    `db:"id"`
    Name string `db:"name"`
    Age  int    `db:"age"`
}

users := []User{}
err := db.Select(&users, "SELECT * FROM users WHERE age > :age", map[string]interface{}{"age": 18})
if err != nil {
    log.Fatal(err)
}
  1. Transaction handling:
tx, err := db.Beginx()
if err != nil {
    log.Fatal(err)
}
defer tx.Rollback()

_, err = tx.Exec("INSERT INTO users (name, age) VALUES (?, ?)", "John", 30)
if err != nil {
    log.Fatal(err)
}

err = tx.Commit()
if err != nil {
    log.Fatal(err)
}

Getting Started

To start using sqlx, first install it using Go modules:

go get github.com/jmoiron/sqlx

Then, in your Go code:

import (
    "log"
    "github.com/jmoiron/sqlx"
    _ "github.com/go-sql-driver/mysql" // Import your database driver
)

func main() {
    db, err := sqlx.Connect("mysql", "user:password@tcp(localhost:3306)/dbname")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // Now you can use db to perform database operations
}

Competitor Comparisons

36,491

The fantastic ORM library for Golang, aims to be developer friendly

Pros of GORM

  • Provides a higher level of abstraction with ORM features like associations, hooks, and migrations
  • Offers automatic creation and updates of database schema based on struct definitions
  • Includes built-in features for soft deletes, optimistic locking, and transactions

Cons of GORM

  • Can be slower for complex queries due to the ORM layer overhead
  • May hide SQL complexity, potentially leading to less optimized queries
  • Steeper learning curve for developers new to ORM concepts

Code Comparison

GORM example:

db.Where("name = ?", "jinzhu").First(&user)

SQLX example:

db.Get(&user, "SELECT * FROM users WHERE name = $1", "jinzhu")

Key Differences

  • GORM uses method chaining and struct tags for query building, while SQLX relies more on raw SQL queries
  • SQLX provides a thin layer over database/sql, offering more control over SQL statements
  • GORM automates many database operations, while SQLX requires more manual handling

Use Cases

  • GORM: Ideal for rapid development of CRUD applications with complex data relationships
  • SQLX: Better suited for projects requiring fine-grained control over SQL queries and performance optimization
12,095

Generate type-safe code from SQL

Pros of sqlc

  • Generates type-safe Go code from SQL queries, reducing runtime errors
  • Provides compile-time SQL query validation
  • Offers better performance due to static code generation

Cons of sqlc

  • Requires an additional build step in the development process
  • Less flexible for dynamic queries compared to sqlx
  • Steeper learning curve for developers new to code generation tools

Code Comparison

sqlc:

// Generated by sqlc. DO NOT EDIT.
func (q *Queries) GetAuthor(ctx context.Context, id int64) (Author, error) {
    row := q.db.QueryRowContext(ctx, getAuthor, id)
    var i Author
    err := row.Scan(&i.ID, &i.Name, &i.Bio)
    return i, err
}

sqlx:

func GetAuthor(ctx context.Context, db *sqlx.DB, id int64) (Author, error) {
    var author Author
    err := db.GetContext(ctx, &author, "SELECT * FROM authors WHERE id = ?", id)
    return author, err
}

sqlc generates type-safe code with predefined queries, while sqlx allows for more flexible, runtime query construction. sqlc's approach can lead to fewer runtime errors and better performance, but sqlx offers more flexibility for dynamic queries.

Generate a Go ORM tailored to your database schema.

Pros of SQLBoiler

  • Generates type-safe, database-specific code, reducing runtime errors
  • Provides powerful querying capabilities with its query builder
  • Offers excellent performance due to generated code

Cons of SQLBoiler

  • Requires code generation step, which can complicate the development process
  • Less flexible for ad-hoc queries compared to SQLx's more dynamic approach
  • Steeper learning curve due to its generated code structure

Code Comparison

SQLBoiler:

users, err := models.Users().All(ctx, db)

SQLx:

var users []User
err := db.Select(&users, "SELECT * FROM users")

Key Differences

  • SQLBoiler generates models and query methods, while SQLx uses struct tags and reflection
  • SQLBoiler provides a more type-safe approach, whereas SQLx offers more flexibility
  • SQLBoiler excels in complex queries and relationships, while SQLx shines in simplicity and ease of use

Both libraries have their strengths, and the choice depends on project requirements, team preferences, and performance needs. SQLBoiler is ideal for larger projects with complex database interactions, while SQLx is great for simpler applications or rapid prototyping.

3,591

SQL-first Golang ORM

Pros of Bun

  • Supports more advanced features like soft deletes and optimistic locking
  • Offers a more expressive and flexible query builder
  • Provides built-in support for database migrations

Cons of Bun

  • Less mature and less widely adopted compared to sqlx
  • May have a steeper learning curve due to its more complex API
  • Potentially slower performance for simple queries due to additional abstractions

Code Comparison

sqlx:

var users []User
err := sqlx.Select(db, &users, "SELECT * FROM users WHERE active = ?", true)

Bun:

var users []User
err := db.NewSelect().Model(&users).Where("active = ?", true).Scan(ctx)

Both libraries provide a way to execute SQL queries and map results to structs. sqlx uses a more straightforward approach with raw SQL, while Bun offers a more expressive query builder. Bun's approach allows for more complex queries to be built programmatically, but may be overkill for simple queries.

Bun provides additional features like automatic table creation and relationship handling, making it more suitable for complex applications. However, sqlx's simpler API may be preferable for projects with straightforward database needs or when performance is a top priority.

2,347

SQL builder and query library for golang

Pros of goqu

  • More expressive and flexible query building with a fluent API
  • Better support for complex queries and subqueries
  • Includes a dialect system for easier database switching

Cons of goqu

  • Steeper learning curve due to its more complex API
  • Less direct mapping to SQL, which may reduce control in some cases
  • Potentially slower execution compared to raw SQL queries

Code Comparison

sqlx:

rows, err := db.Query("SELECT * FROM users WHERE age > ?", 18)

goqu:

sql, _, _ := goqu.From("users").Where(goqu.C("age").Gt(18)).ToSQL()
rows, err := db.Query(sql)

Key Differences

  • sqlx focuses on extending the standard database/sql package with convenience methods and struct scanning
  • goqu provides a more abstracted query-building approach with its own DSL
  • sqlx is closer to raw SQL, while goqu offers a more programmatic way to construct queries
  • goqu includes more advanced features like automatic dialect translation and complex join support
  • sqlx is generally easier to pick up for developers familiar with SQL, while goqu may require more time to master

Both libraries have their strengths, and the choice between them often depends on the specific project requirements and developer preferences.

15,388

An entity framework for Go

Pros of ent

  • Provides a powerful schema-as-code approach for defining database models
  • Offers automatic migration generation and execution
  • Includes built-in support for graph-like queries and traversals

Cons of ent

  • Steeper learning curve due to its unique approach and concepts
  • Less flexible for complex custom SQL queries compared to sqlx
  • May introduce additional overhead for simpler database operations

Code Comparison

ent:

type User struct {
    ent.Schema
}

func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("name"),
        field.Int("age"),
    }
}

sqlx:

type User struct {
    ID   int    `db:"id"`
    Name string `db:"name"`
    Age  int    `db:"age"`
}

users := []User{}
err := db.Select(&users, "SELECT * FROM users")

ent focuses on defining schemas and relationships, while sqlx provides a more direct approach to writing SQL queries and mapping results to structs. ent offers more abstraction and code generation, whereas sqlx gives developers more control over raw SQL operations. The choice between the two depends on project requirements, team expertise, and the desired level of abstraction in database interactions.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

sqlx

CircleCI Coverage Status Godoc license

sqlx is a library which provides a set of extensions on go's standard database/sql library. The sqlx versions of sql.DB, sql.TX, sql.Stmt, et al. all leave the underlying interfaces untouched, so that their interfaces are a superset on the standard ones. This makes it relatively painless to integrate existing codebases using database/sql with sqlx.

Major additional concepts are:

  • Marshal rows into structs (with embedded struct support), maps, and slices
  • Named parameter support including prepared statements
  • Get and Select to go quickly from query to struct/slice

In addition to the godoc API documentation, there is also some user documentation that explains how to use database/sql along with sqlx.

Recent Changes

1.3.0:

  • sqlx.DB.Connx(context.Context) *sqlx.Conn
  • sqlx.BindDriver(driverName, bindType)
  • support for []map[string]interface{} to do "batch" insertions
  • allocation & perf improvements for sqlx.In

DB.Connx returns an sqlx.Conn, which is an sql.Conn-alike consistent with sqlx's wrapping of other types.

BindDriver allows users to control the bindvars that sqlx will use for drivers, and add new drivers at runtime. This results in a very slight performance hit when resolving the driver into a bind type (~40ns per call), but it allows users to specify what bindtype their driver uses even when sqlx has not been updated to know about it by default.

Backwards Compatibility

Compatibility with the most recent two versions of Go is a requirement for any new changes. Compatibility beyond that is not guaranteed.

Versioning is done with Go modules. Breaking changes (eg. removing deprecated API) will get major version number bumps.

install

go get github.com/jmoiron/sqlx

issues

Row headers can be ambiguous (SELECT 1 AS a, 2 AS a), and the result of Columns() does not fully qualify column names in queries like:

SELECT a.id, a.name, b.id, b.name FROM foos AS a JOIN foos AS b ON a.parent = b.id;

making a struct or map destination ambiguous. Use AS in your queries to give columns distinct names, rows.Scan to scan them manually, or SliceScan to get a slice of results.

usage

Below is an example which shows some common use cases for sqlx. Check sqlx_test.go for more usage.

package main

import (
    "database/sql"
    "fmt"
    "log"
    
    _ "github.com/lib/pq"
    "github.com/jmoiron/sqlx"
)

var schema = `
CREATE TABLE person (
    first_name text,
    last_name text,
    email text
);

CREATE TABLE place (
    country text,
    city text NULL,
    telcode integer
)`

type Person struct {
    FirstName string `db:"first_name"`
    LastName  string `db:"last_name"`
    Email     string
}

type Place struct {
    Country string
    City    sql.NullString
    TelCode int
}

func main() {
    // this Pings the database trying to connect
    // use sqlx.Open() for sql.Open() semantics
    db, err := sqlx.Connect("postgres", "user=foo dbname=bar sslmode=disable")
    if err != nil {
        log.Fatalln(err)
    }

    // exec the schema or fail; multi-statement Exec behavior varies between
    // database drivers;  pq will exec them all, sqlite3 won't, ymmv
    db.MustExec(schema)
    
    tx := db.MustBegin()
    tx.MustExec("INSERT INTO person (first_name, last_name, email) VALUES ($1, $2, $3)", "Jason", "Moiron", "jmoiron@jmoiron.net")
    tx.MustExec("INSERT INTO person (first_name, last_name, email) VALUES ($1, $2, $3)", "John", "Doe", "johndoeDNE@gmail.net")
    tx.MustExec("INSERT INTO place (country, city, telcode) VALUES ($1, $2, $3)", "United States", "New York", "1")
    tx.MustExec("INSERT INTO place (country, telcode) VALUES ($1, $2)", "Hong Kong", "852")
    tx.MustExec("INSERT INTO place (country, telcode) VALUES ($1, $2)", "Singapore", "65")
    // Named queries can use structs, so if you have an existing struct (i.e. person := &Person{}) that you have populated, you can pass it in as &person
    tx.NamedExec("INSERT INTO person (first_name, last_name, email) VALUES (:first_name, :last_name, :email)", &Person{"Jane", "Citizen", "jane.citzen@example.com"})
    tx.Commit()

    // Query the database, storing results in a []Person (wrapped in []interface{})
    people := []Person{}
    db.Select(&people, "SELECT * FROM person ORDER BY first_name ASC")
    jason, john := people[0], people[1]

    fmt.Printf("%#v\n%#v", jason, john)
    // Person{FirstName:"Jason", LastName:"Moiron", Email:"jmoiron@jmoiron.net"}
    // Person{FirstName:"John", LastName:"Doe", Email:"johndoeDNE@gmail.net"}

    // You can also get a single result, a la QueryRow
    jason = Person{}
    err = db.Get(&jason, "SELECT * FROM person WHERE first_name=$1", "Jason")
    fmt.Printf("%#v\n", jason)
    // Person{FirstName:"Jason", LastName:"Moiron", Email:"jmoiron@jmoiron.net"}

    // if you have null fields and use SELECT *, you must use sql.Null* in your struct
    places := []Place{}
    err = db.Select(&places, "SELECT * FROM place ORDER BY telcode ASC")
    if err != nil {
        fmt.Println(err)
        return
    }
    usa, singsing, honkers := places[0], places[1], places[2]
    
    fmt.Printf("%#v\n%#v\n%#v\n", usa, singsing, honkers)
    // Place{Country:"United States", City:sql.NullString{String:"New York", Valid:true}, TelCode:1}
    // Place{Country:"Singapore", City:sql.NullString{String:"", Valid:false}, TelCode:65}
    // Place{Country:"Hong Kong", City:sql.NullString{String:"", Valid:false}, TelCode:852}

    // Loop through rows using only one struct
    place := Place{}
    rows, err := db.Queryx("SELECT * FROM place")
    for rows.Next() {
        err := rows.StructScan(&place)
        if err != nil {
            log.Fatalln(err)
        } 
        fmt.Printf("%#v\n", place)
    }
    // Place{Country:"United States", City:sql.NullString{String:"New York", Valid:true}, TelCode:1}
    // Place{Country:"Hong Kong", City:sql.NullString{String:"", Valid:false}, TelCode:852}
    // Place{Country:"Singapore", City:sql.NullString{String:"", Valid:false}, TelCode:65}

    // Named queries, using `:name` as the bindvar.  Automatic bindvar support
    // which takes into account the dbtype based on the driverName on sqlx.Open/Connect
    _, err = db.NamedExec(`INSERT INTO person (first_name,last_name,email) VALUES (:first,:last,:email)`, 
        map[string]interface{}{
            "first": "Bin",
            "last": "Smuth",
            "email": "bensmith@allblacks.nz",
    })

    // Selects Mr. Smith from the database
    rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:fn`, map[string]interface{}{"fn": "Bin"})

    // Named queries can also use structs.  Their bind names follow the same rules
    // as the name -> db mapping, so struct fields are lowercased and the `db` tag
    // is taken into consideration.
    rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:first_name`, jason)
    
    
    // batch insert
    
    // batch insert with structs
    personStructs := []Person{
        {FirstName: "Ardie", LastName: "Savea", Email: "asavea@ab.co.nz"},
        {FirstName: "Sonny Bill", LastName: "Williams", Email: "sbw@ab.co.nz"},
        {FirstName: "Ngani", LastName: "Laumape", Email: "nlaumape@ab.co.nz"},
    }

    _, err = db.NamedExec(`INSERT INTO person (first_name, last_name, email)
        VALUES (:first_name, :last_name, :email)`, personStructs)

    // batch insert with maps
    personMaps := []map[string]interface{}{
        {"first_name": "Ardie", "last_name": "Savea", "email": "asavea@ab.co.nz"},
        {"first_name": "Sonny Bill", "last_name": "Williams", "email": "sbw@ab.co.nz"},
        {"first_name": "Ngani", "last_name": "Laumape", "email": "nlaumape@ab.co.nz"},
    }

    _, err = db.NamedExec(`INSERT INTO person (first_name, last_name, email)
        VALUES (:first_name, :last_name, :email)`, personMaps)
}