Esquema

Cuando usas Isar para almacenar los datos de tu aplicación, estás tratando con colecciones. Una colección es como una tabla en la base de datos Isar asociada y sólo puede contener un tipo de objeto Dart. Cada objeto de la colección representa una línea de datos en la tabla correspondiente.

La definición de una colección es llamada "esquema" ("schema" en inglés). El generador Isar hará el trabajo pesado por ti y generará la mayoría del código que necesitas para usar tu colección.

Anatomía de una colección

Cada colección Isar se define anotando una clase con @collection o @Collection(). Una colección Isar incluye campos para cada columna en la tabla correspondiente en la base de datos, incluyendo uno que corresponde a la clave primaria.

El código siguiente es un ejemplo de una colección simple que define una table User con columnas para ID, nombre, y apellido:

@collection
class User {
  Id? id;

  String? firstName;

  String? lastName;
}

Consejo

Para almacenar un campo, Isar debe tener acceso al mismo. Puedes asegurarte que Isar tiene acceso a un campo haciéndolo público o proporcionando métodos getter y setter.

Existen algunos parámetros opcionales para personalizar la colección:

ConfiguraciónDescripción
inheritanceControla si los campos de la clase padre y mixins serán almacenados en Isar. Habilitado por defecto.
accessorPermite renombrar el punto de acceso por defecto de la colección (por ejemplo isar.contacts para la colección Contact).
ignorePermite ignorar ciertas propiedades. Éstas también son respetadas para las super clases.

Isar Id

Cada clase que defina una colección Isar, debe definir una propiedad id y debe ser de tipo Id identificando inequívocamente un objecto. Id es simplemente un alias para int que le permite al generador Isar reconocer la propiedad id.

Isar indexa automáticamente los campos id, que permite obtener y modificar objectos de manera eficiente basándose en su id.

Puedes establecer tus propios ids o pedir a Isar que asigne un id auto-incrementable. Si el campo id es null y no final, Isar asignará un id auto-incrementable. Si quieres un id auto-incrementable y no-null, puedes usar Isar.autoIncrement en lugar de null.

Consejo

Los ids auto incrementables no se reusan cuando un objeto es eliminado. La única manera de reiniciar los ids auto incrementables es borrando la base de datos.

Renombrando colecciones y campos

Por defecto, Isar usa el nombre de la clase como nombre de la colección. De manera similar, Isar usa los nombres de los campos como nombres de las columnas en la base de datos. Si quieres que una colección o campo tenga un nombre diferente, agrega la anotación @Name. El ejemplo siguiente demuestra el uso de nombres personalizados para colecciones y campos:

@collection
@Name("User")
class MyUserClass1 {

  @Name("id")
  Id myObjectId;

  @Name("firstName")
  String theFirstName;

  @Name("lastName")
  String familyNameOrWhatever;
}

Específicamente si quieres renombrar objectos Dart o clases que ya están almacenados en la base de datos, deberías considerar usar la anotación @Name. De otra manera, la base de datos eliminiará y creará nuevamente el campo o la colección.

Ignorando campos

Isar almacena todos los campos públicos de una clase que defina una colección. Anotando una propiedad o getter con @ignore, puedes excluir dicha propiedad del almacenamiento, como se muestra en el siguiente extracto de código:

@collection
class User {
  Id? id;

  String? firstName;

  String? lastName;

  @ignore
  String? password;
}

En los casos donde una colección hereda los campos de una colección padre, es generalmente más fácil usar la propiedad ignore de la anotación @Collection:

@collection
class User {
  Image? profilePicture;
}

@Collection(ignore: {'profilePicture'})
class Member extends User {
  Id? id;

  String? firstName;

  String? lastName;
}

Si una colección contiene un campo con un tipo de dato no soportado por Isar, éste campo debe ser ignorado.

Advertencia

Ten en cuenta que no es una buena práctica guardar información en objectos Isar que no serán almacenados.

Tipos de datos soportados

Isar soporta los siguientes tipos de datos:

  • bool
  • byte
  • short
  • int
  • float
  • double
  • DateTime
  • String
  • List<bool>
  • List<byte>
  • List<short>
  • List<int>
  • List<float>
  • List<double>
  • List<DateTime>
  • List<String>

Adicionalmente, Isar soporta objetos embebidos y enums. Explicaremos éstos más adelante.

byte, short, float

Para muchos casos de uso, no es necesario el rango completo de 64-bits de un entero o punto flotante (int o double). Isar contiene soporte para tipos adicionales que te permiten ahorrar espacio y memoria cuando se almacenan número más pequeños.

TipoTamaño en bytesRango
byte10 a 255
short4-2,147,483,647 a 2,147,483,647
int8-9,223,372,036,854,775,807 a 9,223,372,036,854,775,807
float4-3.4e38 a 3.4e38
double8-1.7e308 a 1.7e308

Los tipos numéricos adicionales con sólo aliases de los tipos de datos nativos de Dart, por lo que usar short, por ejemplo, funciona de la misma manera que usando int.

El siguiente es un ejemplo de una colección que contiene todos los tipos de datos vistos anteriormente:

@collection
class TestCollection {
  Id? id;

  late byte byteValue;

  short? shortValue;

  int? intValue;

  float? floatValue;

  double? doubleValue;
}

Todos los tipos numéricos también pueden ser usados en listas. Para almacenar bytes, deberías usar List<byte>.

Tipos nulables

Entender cómo funciona la nulabilidad en Isar en esencial: los tipos numéricos NO tienen una representación null dedicada. Por el contrario, un valor específico es usado:

TipoVM
short-2147483648
intint.MIN
floatdouble.NaN
doubledouble.NaN

bool, String, y List tienen una representación null por separado.

Este comportamiento habilita mejoras en el rendimiento, y te permite cambiar libremente la nulabilidad de tus campos sin requerir una migración o código especial para lidiar con valores null.

Advertencia

El tipo byte no soporta valores null.

DateTime

Isar no almacena información de zonas horarias en tus campos DateTime. En su lugar, convierte DateTimes a UTC antes de almacenarlos. Isar devuelve todas las fechas en hora local.

Los DateTimes se almacenan con presición de microsegundos. En navegadores web, sólo se soporta presición de milisegundos debido a una limitación de Javascript.

Enum

Isar permite almacenar y usar enums como cualquier otro tipo de dato Isar. Sin embargo, tendrás que decidir cómo Isar debería representar el enum en el disco. Isar soporta cuatro estrategias diferentes:

EnumTypeDescripción
ordinalEl índice del emun se almacena como byte. Esto es muy eficiente pero no permite enums nulables
ordinal32El índice del enum se almacena como short (entero de 4 bytes).
nameEl nombre del enum se almacena como String.
valuePara recuperar el valor del enum se utiliza una propiedad personalizada.

Advertencia

ordinal y ordinal32 dependen del orden de los valores en el enum. Si cambias el orden, bases de datos existentes retornarán valores incorrectos.

Veamos un ejemplo para cada estrategia.

@collection
class EnumCollection {
  Id? id;

  @enumerated // same as EnumType.ordinal
  late TestEnum byteIndex; // cannot be nullable

  @Enumerated(EnumType.ordinal)
  late TestEnum byteIndex2; // cannot be nullable

  @Enumerated(EnumType.ordinal32)
  TestEnum? shortIndex;

  @Enumerated(EnumType.name)
  TestEnum? name;

  @Enumerated(EnumType.value, 'myValue')
  TestEnum? myValue;
}

enum TestEnum {
  first(10),
  second(100),
  third(1000);

  const TestEnum(this.myValue);

  final short myValue;
}

Por supuesto, los Enums pueden usarse también en listas.

Objetos Embebidos

Con frecuencia es útil tener objetos anidados en tus colecciones. No hay límite en cuanto a la profundidad que un objeto anidado puede tener. Sin embargo, es necesario tener en cuenta que actualizar un objeto anidado requerirá escribir el árbol completo del objeto en la base de datos.

@collection
class Email {
  Id? id;

  String? title;

  Recepient? recipient;
}

@embedded
class Recepient {
  String? name;

  String? address;
}

Los objectos embebidos pueden ser nulables y extender otros objectos. El único requerimiento es que sean anotados con @embedded y que tengan un constructor predeterminado sin parámetros requeridos.