257 lines
9.6 KiB
Markdown
257 lines
9.6 KiB
Markdown
# 5.5 Desarrollo de un ORM basado en beedb
|
|
( ***El proyecto beedb no está mantenido actualmente, pero el código sigue ahí*** )
|
|
( ***Project beedb is no longer maintained, but the code s still there*** )
|
|
|
|
beedb es un ORM ( object relational mapper ) desarrollado en Go, por mi.
|
|
Usa el lenguaje ideomático de Go para operar con las bases de datos, implementando un mapeo de estructura a base de datos que actua como un Framework ORM liviano de Go. El propósito de desarrollar este ORM no es solamente ayudar a la gente a aprender como implementar un ORM, sino también encontrar un buen balance entre funcionalidad y desempeño en lo que respecta a persistencia de datos.
|
|
|
|
beedb es un proyecto de código abierto que soporta la funcionalidad básica de un ORM, pero no soporta las consultas asociativas.
|
|
|
|
Como beedb soporta los estándares de la interfaz `database/sql` cualquier manejador que implemente esta interfaz podrá ser usado con beedb. Yo lo he probado con los siguientes manejadores:
|
|
|
|
Mysql: [github.com/ziutek/mymysql/godrv](github.com/ziutek/mymysql/godrv)
|
|
|
|
Mysql: [github/go-mysql-driver/mysql](github/go-mysql-driver/mysql)
|
|
|
|
PostgreSQL: [github.com/lib/pq](github.com/lib/pq)
|
|
|
|
SQLite: [github.com/mattn/go-sqlite3](github.com/mattn/go-sqlite3)
|
|
|
|
MS ADODB: [github.com/mattn/go-adodb](github.com/mattn/go-adodb)
|
|
|
|
ODBC: [bitbucket.org/miquella/mgodbc](bitbucket.org/miquella/mgodbc)
|
|
|
|
## Instalación
|
|
|
|
Puedes usar el comando `go get` para instalar beedb localmente.
|
|
```
|
|
go get github.com/astaxie/beedb
|
|
```
|
|
## Inicialización
|
|
|
|
Primero debes importar los paquetes necesarios:
|
|
```
|
|
import (
|
|
"database/sql"
|
|
"github.com/astaxie/beedb"
|
|
_ "github.com/ziutek/mymysql/godrv"
|
|
)
|
|
```
|
|
Luego necesitas abrir la conexión a la base de datos y crear un objeto beedb (MySQL en este ejemplo):
|
|
```
|
|
db, err := sql.Open("mymysql", "test/xiemengjun/123456")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
orm := beedb.New(db)
|
|
```
|
|
`beedb.New()` actualmente toma dos argumentos. El primero es el objeto de la base de datos y el segundo es para indicar que motor de base de datos estás usando. Si estás usando MySQL o SQLite puedes omitir el segundo argumento.
|
|
|
|
De otra manera, este argumento debe ser incluido, por ejemplo en el caso de SQLServer:
|
|
```
|
|
orm = beedb.New(db, "mssql")
|
|
```
|
|
PostgreSQL:
|
|
```
|
|
orm = beedb.New(db, "pg")
|
|
```
|
|
beedb soporta depuración. Usa el siguiente código para habilitarla:
|
|
```
|
|
beedb.OnDebug=true
|
|
```
|
|
Después debemos tener una estructura para la tabla `Userinfo` que hemos usado en las secciones anteriores.
|
|
```
|
|
type Userinfo struct {
|
|
Uid int `PK` // Si la llave primaria no es id, necesitas añadir la etiqueta `PK` para una llave primaria personalizada.
|
|
Username string
|
|
Departname string
|
|
Created time.Time
|
|
}
|
|
```
|
|
Se conciente que beedb autoconvierte la notación de camello a notación de guiones bajos. Por ejemplo, si tenemos `UserInfo` como el nombre de la estructura, beedb lo convertirá a `user_info` en la base de datos. La misma regla se aplica para los nombres de campos de la estructura.
|
|
|
|
## Insertar datos
|
|
|
|
El siguiente ejemplo muestra como usar beedb a guardar una estructura en vez de usar comandos planos de SQL. Usamos el método `Save` de beedb para aplicar el cambio.
|
|
```
|
|
var saveone Userinfo
|
|
saveone.Username = "Test Add User"
|
|
saveone.Departname = "Test Add Departname"
|
|
saveone.Created = time.Now()
|
|
orm.Save(&saveone)
|
|
```
|
|
Puedes verificar el campo `saveone.Uid` después de que este registro es insertado; su valor es un identificador autoincrementado por el que el método `Save` se ocupa por ti.
|
|
|
|
beedb provee otra manera de insertar datos, usando el estilo de mapas de Go.
|
|
```
|
|
add := make(map[string]interface{})
|
|
add["username"] = "astaxie"
|
|
add["departname"] = "cloud develop"
|
|
add["created"] = "2012-12-02"
|
|
orm.SetTable("userinfo").Insert(add)
|
|
```
|
|
Insertando varios datos:
|
|
```
|
|
addslice := make([]map[string]interface{}, 10)
|
|
add:=make(map[string]interface{})
|
|
add2:=make(map[string]interface{})
|
|
add["username"] = "astaxie"
|
|
add["departname"] = "cloud develop"
|
|
add["created"] = "2012-12-02"
|
|
add2["username"] = "astaxie2"
|
|
add2["departname"] = "cloud develop2"
|
|
add2["created"] = "2012-12-02"
|
|
addslice = append(addslice, add, add2)
|
|
orm.SetTable("userinfo").InsertBatch(addslice)
|
|
```
|
|
El método mostrado arriba es similar a una consulta encadenada, la cual te podría ser familiar si has usado jQuery. Retorna el objeto del ORM después de la llamada, entonces continua haciendo otros trabajos.
|
|
|
|
El método `SetTable` le dice al ORM que queremos insertar nuestra data en la tabla `userinfo`.
|
|
|
|
## Actualizar datos
|
|
|
|
Continuemos trabajando con el ejemplo de arriba, para ver como actualizar los datos. Ahora que tenemos la llave primeria de `saveone(Uid)`, beedb ejecuta una operación de actualizacieon en lugar de insertar un nuevo registro.
|
|
```
|
|
saveone.Username = "Update Username"
|
|
saveone.Departname = "Update Departname"
|
|
saveone.Created = time.Now()
|
|
orm.Save(&saveone) // update
|
|
```
|
|
Como antes, también puedes usar un mapa para actualizar los datos:
|
|
```
|
|
t := make(map[string]interface{})
|
|
t["username"] = "astaxie"
|
|
orm.SetTable("userinfo").SetPK("uid").Where(2).Update(t)
|
|
```
|
|
Permíteme explicar algunos de los métodos usados arriba:
|
|
|
|
- `.SetPK()` le dice al ORM que `uid` es la llave primaria del registro en la tabla `userinfo`
|
|
- `.Where()` define condiciones y suporta múltiples argumentos. Si el primer arguento es un entero, es una manera de acortar `Where("<llave primaria>=?", <valor>)`.
|
|
- `.Update()` acepta un mapa y actualiza la base de datos.
|
|
|
|
## Consultado datos
|
|
|
|
Las consultas con la interface beedb son muy fleibles. Veamos algunos ejemplos:
|
|
|
|
Ejemplo 1: Consultando por llaves primarias:
|
|
```
|
|
var user Userinfo
|
|
// Where acepta dos argumentos, el segundo entero
|
|
orm.Where("uid=?", 27).Find(&user)
|
|
```
|
|
Ejemplo 2:
|
|
```
|
|
var user2 Userinfo
|
|
orm.Where(3).Find(&user2) // Forma corta que omite el nombre de la llave primaria
|
|
```
|
|
Ejemplo 3, otras condiciones de consulta
|
|
```
|
|
var user3 Userinfo
|
|
// Where acepta dos argumentos, el segundo cadena
|
|
orm.Where("name = ?", "john").Find(&user3)
|
|
```
|
|
Ejemplo 4, condiciones mas complicadas:
|
|
```
|
|
var user4 Userinfo
|
|
// Where con tres argumentos aceptados
|
|
orm.Where("name = ? and age < ?", "john", 88).Find(&user4)
|
|
```
|
|
Ejemplos para obtener múltiples registros
|
|
|
|
Ejemplo 1, Obtiene 10 registros donde `id>3` Que comienzan en la posición 20
|
|
```
|
|
var allusers []Userinfo
|
|
err := orm.Where("id > ?", "3").Limit(10,20).FindAll(&allusers)
|
|
```
|
|
Ejemplo 2, omite el segundo argumento de límite, entonces comienza en 0 y obtiene 10 registros:
|
|
```
|
|
var tenusers []Userinfo
|
|
err := orm.Where("id > ?", "3").Limit(10).FindAll(&tenusers)
|
|
```
|
|
Ejemplo 3, obtener todos los recursos:
|
|
```
|
|
var everyone []Userinfo
|
|
err := orm.OrderBy("uid desc,username asc").FindAll(&everyone)
|
|
```
|
|
Como puedes ver, el método límite es para limitar el número de resultados.
|
|
|
|
- `.Limit()` suporta dos argumentos: el número de resultados comenzando en la posición. 0 es el valor por defecto de posición inicial.
|
|
- `.OrderBy()` es para ordenar resultados. El argumento es la condición de orden.
|
|
|
|
Todos los ejemplos aquí simplemente mapean registros a estructuras. También puedes colocar data en un mapa como sigue:
|
|
```
|
|
a, _ := orm.SetTable("userinfo").SetPK("uid").Where(2).Select("uid,username").FindMap()
|
|
```
|
|
- `.Select()` le dice a beedb cuantos campos quieres obtener desde la tabla de la base de datos. Si no es especifica, todos los registros son retornados por defecto.
|
|
- `.FindMap()` retorna el tipo `[]map[String][]byte`, entonces puedes convertir a otros tipos tu mismo.
|
|
|
|
## Eliminar datos
|
|
|
|
beedb provee métodos amplios para eliminar datos.
|
|
|
|
Ejemplo 1, eliminar un único registro
|
|
```
|
|
// saveone es el mismo del ejemplo anterior.
|
|
orm.Delete(&saveone)
|
|
```
|
|
Ejemplo 2, elimintar todos los usuarios
|
|
```
|
|
// alluser es un segmento que tiene múltiples usuarios
|
|
orm.DeleteAll(&alluser)
|
|
```
|
|
Ejemplo 3, Eliminar registros con SQL
|
|
```
|
|
orm.SetTable("userinfo").Where("uid>?", 3).DeleteRow()
|
|
```
|
|
## Consultas de asociación
|
|
|
|
beedb no soportar uniones entre asociaciones.
|
|
Sin embargo, desde que algunas aplicaciones necesitan esta caractarísticas, aquí está una implementación:
|
|
```
|
|
a, _ := orm.SetTable("userinfo").Join("LEFT", "userdetail", "userinfo.uid=userdetail.uid")
|
|
.Where("userinfo.uid=?", 1).Select("userinfo.uid,userinfo.username,userdetail.profile").FindMap()
|
|
```
|
|
Nosotros vemos un método llamado `.Join()` que toma tres argumentos
|
|
- El primer argumento: Tipo del Join; INNER, LEFT, OUTER, CROSS, etc.
|
|
- El segundo argumento: la tabla con la que quieres hacer la unión
|
|
- El tercer argumento: la condición de Join
|
|
|
|
## Group By y Having
|
|
|
|
beedb también tiene una implementación de `group by` y `having`.
|
|
```
|
|
a, _ := orm.SetTable("userinfo").GroupBy("username").Having("username='astaxie'").FindMap()
|
|
```
|
|
- `.GroupBy()` indica el campo con el que se va a agrupar.
|
|
- `.Having()` indica la condición del `having`.
|
|
|
|
## Futuro
|
|
|
|
He recibido un montón de retroalimentación de beedb de muchas personas al rededor del mundo y estoy pensando en reconfigurar los siguientes aspectos:
|
|
|
|
- Implementar una interfaz similar a `database/sql/driver` en orden de facilitar las operaciones CRUD
|
|
- Implementar asociaciones de bases de datos relacionales como uno a uno, muchos a uno y muchos a muchos.
|
|
|
|
Aquí hay un ejemplo:
|
|
```
|
|
type Profile struct {
|
|
Nickname string
|
|
Mobile string
|
|
}
|
|
type Userinfo struct {
|
|
Uid int
|
|
PK_Username string
|
|
Departname string
|
|
Created time.Time
|
|
Profile HasOne
|
|
}
|
|
```
|
|
- Auto crear tablas e indices.
|
|
- Implementar una piscina de conexiones usando rutinas.
|
|
|
|
## Enlaces
|
|
|
|
- [Índice](preface.md)
|
|
- Sección previa: [PostgreSQL](05.4.md)
|
|
- Siguiente sección section: [Bases de Datos NoSQL](05.6.md)
|