Banderas de características
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
El framework ofrece una colección de características de generación de código que pueden añadirse o eliminarse mediante banderas.
Uso
Las banderas de características pueden proporcionarse mediante flags CLI o como argumentos del paquete gen.
CLI
go run -mod=mod entgo.io/ent/cmd/ent generate --feature privacy,entql ./ent/schema
Go
// +build ignore
package main
import (
"log"
"text/template"
"entgo.io/ent/entc"
"entgo.io/ent/entc/gen"
)
func main() {
err := entc.Generate("./schema", &gen.Config{
Features: []gen.Feature{
gen.FeaturePrivacy,
gen.FeatureEntQL,
},
Templates: []*gen.Template{
gen.MustParse(gen.NewTemplate("static").
Funcs(template.FuncMap{"title": strings.ToTitle}).
ParseFiles("template/static.tmpl")),
},
})
if err != nil {
log.Fatalf("running ent codegen: %v", err)
}
}
Lista de características
Resolución automática de conflictos de fusión
La opción schema/snapshot indica a entc (ent codegen) que almacene una instantánea del último esquema en un paquete interno,
y la utilice para resolver automáticamente conflictos de fusión cuando no se pueda construir el esquema del usuario.
Esta opción puede añadirse a un proyecto con el flag --feature schema/snapshot, pero consulta
ent/ent/issues/852 para más contexto.
Capa de privacidad
La capa de privacidad permite configurar políticas de privacidad para consultas y mutaciones de entidades en la base de datos.
Esta opción puede añadirse a un proyecto con el flag --feature privacy, y puedes aprender más en la
documentación de privacidad.
Filtrado EntQL
La opción entql proporciona capacidades de filtrado genérico y dinámico en tiempo de ejecución para los diferentes constructores de consultas.
Esta opción puede añadirse a un proyecto con el flag --feature entql, y puedes aprender más en la
documentación de privacidad.
Aristas con nombre
La opción namedges proporciona una API para precargar aristas con nombres personalizados.
Esta opción puede añadirse a un proyecto con el flag --feature namedges, y puedes aprender más en la
documentación de Carga temprana.
Referencias bidireccionales en aristas
La opción bidiedges guía a Ent para establecer referencias bidireccionales al cargar aristas (O2M/O2O) de forma temprana.
Esta opción puede añadirse a un proyecto con el flag --feature bidiedges.
Los usuarios que utilicen el estándar encoding/json.MarshalJSON deben eliminar las referencias circulares antes de llamar a json.Marshal.
Configuración de esquema
La opción sql/schemaconfig permite asignar nombres alternativos de bases de datos SQL a los modelos. Resulta útil cuando tus modelos no residen en una única base de datos sino que están distribuidos en diferentes esquemas.
Esta opción puede añadirse a un proyecto con el flag --feature sql/schemaconfig. Tras generar el código, podrás usar una nueva opción así:
c, err := ent.Open(dialect, conn, ent.AlternateSchema(ent.SchemaConfig{
User: "usersdb",
Car: "carsdb",
}))
c.User.Query().All(ctx) // SELECT * FROM `usersdb`.`users`
c.Car.Query().All(ctx) // SELECT * FROM `carsdb`.`cars`
Bloqueos a nivel de fila
La opción sql/lock permite configurar bloqueos a nivel de fila usando la sintaxis SQL SELECT ... FOR {UPDATE | SHARE}.
Esta opción puede añadirse a un proyecto con el flag --feature sql/lock.
tx, err := client.Tx(ctx)
if err != nil {
log.Fatal(err)
}
tx.Pet.Query().
Where(pet.Name(name)).
ForUpdate().
Only(ctx)
tx.Pet.Query().
Where(pet.ID(id)).
ForShare(
sql.WithLockTables(pet.Table),
sql.WithLockAction(sql.NoWait),
).
Only(ctx)
Modificadores SQL personalizados
La opción sql/modifier permite añadir modificadores SQL personalizados a los constructores y mutar las sentencias antes de su ejecución.
Esta opción puede añadirse a un proyecto con el flag --feature sql/modifier.
Ejemplo de modificación 1
client.Pet.
Query().
Modify(func(s *sql.Selector) {
s.Select("SUM(LENGTH(name))")
}).
IntX(ctx)
El código anterior generará la siguiente consulta SQL:
SELECT SUM(LENGTH(name)) FROM `pet`
Seleccionar y escanear valores dinámicos
Si trabajas con modificadores SQL y necesitas escanear valores dinámicos no presentes en tu definición de esquema Ent, como
agregaciones u ordenamientos personalizados, puedes aplicar AppendSelect/AppendSelectAs al sql.Selector. Posteriormente
podrás acceder a sus valores mediante el método Value definido en cada entidad:
const as = "name_length"
// Query the entity with the dynamic value.
p := client.Pet.Query().
Modify(func(s *sql.Selector) {
s.AppendSelectAs("LENGTH(name)", as)
}).
FirstX(ctx)
// Read the value from the entity.
n, err := p.Value(as)
if err != nil {
log.Fatal(err)
}
fmt.Println("Name length: %d == %d", n, len(p.Name))
Ejemplo de modificación 2
var p1 []struct {
ent.Pet
NameLength int `sql:"length"`
}
client.Pet.Query().
Order(ent.Asc(pet.FieldID)).
Modify(func(s *sql.Selector) {
s.AppendSelect("LENGTH(name)")
}).
ScanX(ctx, &p1)
El código anterior generará la siguiente consulta SQL:
SELECT `pet`.*, LENGTH(name) FROM `pet` ORDER BY `pet`.`id` ASC
Ejemplo de modificación 3
var v []struct {
Count int `json:"count"`
Price int `json:"price"`
CreatedAt time.Time `json:"created_at"`
}
client.User.
Query().
Where(
user.CreatedAtGT(x),
user.CreatedAtLT(y),
).
Modify(func(s *sql.Selector) {
s.Select(
sql.As(sql.Count("*"), "count"),
sql.As(sql.Sum("price"), "price"),
sql.As("DATE(created_at)", "created_at"),
).
GroupBy("DATE(created_at)").
OrderBy(sql.Desc("DATE(created_at)"))
}).
ScanX(ctx, &v)
El código anterior generará la siguiente consulta SQL:
SELECT
COUNT(*) AS `count`,
SUM(`price`) AS `price`,
DATE(created_at) AS `created_at`
FROM
`users`
WHERE
`created_at` > x AND `created_at` < y
GROUP BY
DATE(created_at)
ORDER BY
DATE(created_at) DESC
Ejemplo de modificación 4
var gs []struct {
ent.Group
UsersCount int `sql:"users_count"`
}
client.Group.Query().
Order(ent.Asc(group.FieldID)).
Modify(func(s *sql.Selector) {
t := sql.Table(group.UsersTable)
s.LeftJoin(t).
On(
s.C(group.FieldID),
t.C(group.UsersPrimaryKey[1]),
).
// Append the "users_count" column to the selected columns.
AppendSelect(
sql.As(sql.Count(t.C(group.UsersPrimaryKey[1])), "users_count"),
).
GroupBy(s.C(group.FieldID))
}).
ScanX(ctx, &gs)
El código anterior generará la siguiente consulta SQL:
SELECT
`groups`.*,
COUNT(`t1`.`group_id`) AS `users_count`
FROM
`groups` LEFT JOIN `user_groups` AS `t1`
ON
`groups`.`id` = `t1`.`group_id`
GROUP BY
`groups`.`id`
ORDER BY
`groups`.`id` ASC
Ejemplo de modificación 5
client.User.Update().
Modify(func(s *sql.UpdateBuilder) {
s.Set(user.FieldName, sql.Expr(fmt.Sprintf("UPPER(%s)", user.FieldName)))
}).
ExecX(ctx)
El código anterior generará la siguiente consulta SQL:
UPDATE `users` SET `name` = UPPER(`name`)
Ejemplo de modificación 6
client.User.Update().
Modify(func(u *sql.UpdateBuilder) {
u.Set(user.FieldID, sql.ExprFunc(func(b *sql.Builder) {
b.Ident(user.FieldID).WriteOp(sql.OpAdd).Arg(1)
}))
u.OrderBy(sql.Desc(user.FieldID))
}).
ExecX(ctx)
El código anterior generará la siguiente consulta SQL:
UPDATE `users` SET `id` = `id` + 1 ORDER BY `id` DESC
Ejemplo de modificación 7
Añadir elementos al array values en una columna JSON:
client.User.Update().
Modify(func(u *sql.UpdateBuilder) {
sqljson.Append(u, user.FieldTags, []string{"tag1", "tag2"}, sqljson.Path("values"))
}).
ExecX(ctx)
El código anterior generará la siguiente consulta SQL:
UPDATE `users` SET `tags` = CASE
WHEN (JSON_TYPE(JSON_EXTRACT(`tags`, '$.values')) IS NULL OR JSON_TYPE(JSON_EXTRACT(`tags`, '$.values')) = 'NULL')
THEN JSON_SET(`tags`, '$.values', JSON_ARRAY(?, ?))
ELSE JSON_ARRAY_APPEND(`tags`, '$.values', ?, '$.values', ?) END
WHERE `id` = ?
API SQL directa
La opción sql/execquery permite ejecutar sentencias usando los métodos ExecContext/QueryContext del controlador subyacente. Para documentación completa, consulta: DB.ExecContext y DB.QueryContext.
// From ent.Client.
if _, err := client.ExecContext(ctx, "TRUNCATE t1"); err != nil {
return err
}
// From ent.Tx.
tx, err := client.Tx(ctx)
if err != nil {
return err
}
if err := tx.User.Create().Exec(ctx); err != nil {
return err
}
if _, err := tx.ExecContext("SAVEPOINT user_created"); err != nil {
return err
}
// ...
Las sentencias ejecutadas con ExecContext/QueryContext no pasan por Ent, y pueden saltarse capas fundamentales de tu aplicación como hooks, privacidad (autorización) y validadores.
Upsert
La opción sql/upsert permite configurar lógica de upsert y bulk-upsert usando la sintaxis SQL ON CONFLICT / ON DUPLICATE KEY. Para documentación completa, consulta la API de Upsert.
Esta opción se puede añadir a un proyecto usando la bandera --feature sql/upsert.
// Use the new values that were set on create.
id, err := client.User.
Create().
SetAge(30).
SetName("Ariel").
OnConflict().
UpdateNewValues().
ID(ctx)
// In PostgreSQL, the conflict target is required.
err := client.User.
Create().
SetAge(30).
SetName("Ariel").
OnConflictColumns(user.FieldName).
UpdateNewValues().
Exec(ctx)
// Bulk upsert is also supported.
client.User.
CreateBulk(builders...).
OnConflict(
sql.ConflictWhere(...),
sql.UpdateWhere(...),
).
UpdateNewValues().
Exec(ctx)
// INSERT INTO "users" (...) VALUES ... ON CONFLICT WHERE ... DO UPDATE SET ... WHERE ...
ID globalmente único
Por defecto, las claves primarias SQL comienzan desde 1 en cada tabla; lo que significa que múltiples entidades de diferentes tipos pueden compartir el mismo ID. A diferencia de AWS Neptune, donde los IDs de nodo son UUIDs.
Esto no funciona bien si trabajas con GraphQL, que requiere que el ID del objeto sea único.
Para habilitar el soporte de IDs universales en tu proyecto, simplemente usa la bandera --feature sql/globalid.
Si has usado la opción de migración migrate.WithGlobalUniqueID(true) en el pasado, lee esta guía antes de cambiar tu proyecto a la nueva característica globalid.
How does it work? ent migration allocates a 1<<32 range for the IDs of each entity (table),
and store this information alongside your generated code (internal/globalid.go). For example, type A will have the
range of [1,4294967296) for its IDs, and type B will have the range of [4294967296,8589934592), etc.
Ten en cuenta que si esta opción está habilitada, el número máximo de tablas posibles es 65535.