8.7 KiB
5.1 Interfaz database/sql
Go no provee ningún manejador de base de datos oficial, a diferencia de otros lenguajes como PHP que si lo hacen. Sin embargo, si provee una estándar de interfaz para que los desarrolladores desarrollen manejadores basados en ella. La ventaja es que si tu código es desarrollado de acuerdo a esa interfaz estándar, no vas a tener que cambiar ningún código si tu base de datos cambia. Veamos cuales son los estándares de esta interface.
sql.Register
Esta función está en el paquete database/sql para registrar una base de datos cuando usas un manejador de base de datos de terceros. Todos deberían llamar a la función Register(name String, driver driver.Driver) en la función init() en orden de registrarse a si mismas.
Vamos a mirar los corespondientes llamados en el código de mymysql y sqlite3:
//manejador https://github.com/mattn/go-sqlite3
func init() {
sql.Register("sqlite3", &SQLiteDriver{})
}
//manejador https://github.com/mikespook/mymysql
// Driver automatically registered in database/sql
var d = Driver{proto: "tcp", raddr: "127.0.0.1:3306"}
func init() {
Register("SET NAMES utf8")
sql.Register("mymysql", &d)
}
Vemos que los manejadores de terceros implementan esta función para registrarse a si mismos y Go utiliza un mapa para guardar los manejadores dentro de database/sql.
var drivers = make(map[string]driver.Driver)
drivers[name] = driver
Así, la función de registro puede registrar tantos manejadores como requieras, cada uno con un nombre diferente.
Siempre veremos el siguiente código cuando usemos manejadores de terceros:
import (
"database/sql"
_ "github.com/mattn/go-sqlite3"
)
Aquí, el guión bajo, (también llamado 'blank') _ puede ser un poco confuso para muchos principiantes, pero es una gran característica de Go. Sabemos que el identificador de guión bajo es usado para no guardar valores que una función retorna, y también que debes usar todos los paquetes que importes en tu código Go. Entonces, cuando el guión bajo es usado con un import, significa que necesitas ejecutar la función init() de ese paquete sin usarlo directamente. lo cual encaja perfectamente en el caso de registrar el manejador de base de datos.
driver.Driver
Driver es una interface que contiene un método Open(name string) que retorna una interfaz Conn.
type Driver interface {
Open(name string) (Conn, error)
}
Esta es una conexión de una vez, que significa que solamente puede ser usada una vez por rutina. El siguiente código causará errores:
...
go goroutineA (Conn) // query
go goroutineB (Conn) // insert
...
Porque no no tiene idea de que rutina va a realizar cada operación, la operación de consulta podría obtener el resultado de la operación de incersión y viceversa.
Todos los manejadores de terceros debería tener esta función para analizar el nombre de una Conn y retornar el resultado correcto.
driver.Conn
Esta es la interfaz de conexión con algunos métodos, como dije arriba, la misma conexión solo puede usarse una vez por rutina.
type Conn interface {
Prepare(query string) (Stmt, error)
Close() error
Begin() (Tx, error)
}
Prepareretorna el estado del comando SQL ejecutado para consultas, eliminaciones, etc.Closecierra la conexión actual y limpia los recursos. La mayoría de paquetes de terceros implementan algún tipo de piscina de conexiones, así que no necesitas almacenar las conexiones que puedan causar errores.Beginretorna un Tx que representa una transacción para manejar. Puedes usarlo para consultar, almacenar, o volver hacia atrás algunas transacciones.
driver.Stmt
Este es un estado de listo que corresponde con una Conn, y solamente puede ser usada una vez por rutina (como en el caso de Conn).
type Stmt interface {
Close() error
NumInput() int
Exec(args []Value) (Result, error)
Query(args []Value) (Rows, error)
}
Closecierra la conexión actual, pero sigue retornando la información de la ejecución de una operación de consulta.NumInputretorna el número de argumentos obligados. Los manejadores de bases de datos debería revisar la cantidad de argumentos cuando los resultados son mayor que 0, y retorna -1 cuando el manejador de la base de datos no conoce ningún argumento obligado.Execejecuta un comandoupdate/insertpreparados enPrepare, retorna unResult.Queryejecuta un comandoselectpreparado enPrepare, retorna información.
driver.Tx
Generalmente, las transacciones únicamente tienen métodos de envío y de vuelta atrás, y el manejador de las bases de datos solo necesita implementar estos dos métodos.
type Tx interface {
Commit() error
Rollback() error
}
driver.Execer
Esta es una interfaz opcional.
type Execer interface {
Exec(query string, args []Value) (Result, error)
}
El manejador no implementa esta interfaz, cuando llame DB.Exec, automáticamente llamará Prepare, retornará un Stmt. Después ejecutará el método Exec de Smtt, y cerrará el Smtm.
driver.Result
Esta es la interface para los operaciones de update/insert.
type Result interface {
LastInsertId() (int64, error)
RowsAffected() (int64, error)
}
LastInsertIdretorna el identificador de autoincremento después de una operación de inserción.RowsAffectedretorna la cantidad de filas afectadas por la operación.
driver.Rows
Esta interfaz es el resultado de una operación de consulta.
type Rows interface {
Columns() []string
Close() error
Next(dest []Value) error
}
Columnsretorna la información de los campos de la base de datos. El segmento tiene correspondencia con los campos SQL únicamente. y no retorna todos los campos de la tabla de la base de datos.Closecierra el iterador sobre las columnas.Nextretorna la siguiente información y la asigna a dest, convirtiendo todas las cadenas en arreglos de bytes, y retorna un error io.EOF si no existe mas data disponible.
driver.RowsAffected
Este es un alias para int64, pero implementado por la interfaz Result.
type RowsAffected int64
func (RowsAffected) LastInsertId() (int64, error)
func (v RowsAffected) RowsAffected() (int64, error)
driver.Value
Esta es una interfaz vacía que contiene cualquier tipo de información.
type Value interface{}
El Value debe ser algo con lo que los manejadores puedan operar, o nil, así que debería ser alguno de los siguientes tipos:
int64
float64
bool
[]byte
string [*] Exceptuando los Rows.Next, que no puede retornar una cadena.
time.Time
driver.ValueConverter
Define una interfaz para convertir valores nativos en valores de driver.Value.
type ValueConverter interface {
ConvertValue(v interface{}) (Value, error)
}
Esta interfaz es comunmente usada en manejadores de bases de datos y tiene muchas características útiles:
- Convierte un driver.Value al tipo de campo correspondiente, por ejemplo convierte un int64 a un uint16.
- Conviernte una consulta de base de datos a un driver.Value.
- Conviernte un driver.Value a un usuario definido en la función
scan.
driver.Valuer
Define una interfaz para devolver un driver.Value.
type Valuer interface {
Value() (Value, error)
}
Muchos tipos implementan esta interface para la conversión entre driver.Value y ellos mismos.
En este punto, deberías saber un poco mas sobre el desarrollo de manejadores de bases de datos en Go. una vez que hayas implementado interfaces para operaciones como agregar, eliminar, actualizar, etc, existirán algunos problemas relacionados con comunicarse específicamente con algún tipo de bases de datos.
database/sql
database/sql define en un alto nivel métodos existentes en database/sql/driver para tener las operaciones en un nivel mas conveniente, y sugiera que implementes una piscina de conexiones.
type DB struct {
driver driver.Driver
dsn string
mu sync.Mutex // protects freeConn and closed
freeConn []driver.Conn
closed bool
}
Como puedes ver, la función Open retorna una base de datos que tiene una freeConn, y esta es una piscina de conexiones simple. Su implementación es simple y fea. Usa defer db.putConn(ci, err) en la función Db.Prepare para colocar una conexión en la piscina de conexiones. Siempre que se vaya a llamar la función Conn, verifica la longitud de freeConn. Si es mayor a 0, significa que hay una conexión reusable y la retorna directamente a ti. De otra manera, crea una nueva conexión y la retorna.
Enlaces
- Índice
- Sección anterior: Bases de Datos
- Sección siguiente: MySQL