Frozen-Burrito / hydrate-app

Repositorio para la app de apoyo de la botella hydrate, desarrollada con Flutter.
2 stars 0 forks source link

[BUG] `SQLiteDB.insert()` produce registros incorrectos para relaciones muchos a muchos #13

Closed Frozen-Burrito closed 2 years ago

Frozen-Burrito commented 2 years ago

Descripción del Error Cuando el método SQLiteDB.insert() crea los registros necesarios para describir una relación muchos a muchos usada por alguna entidad, parece que crea registros duplicados en la tabla muchos a muchos.

Cómo Reproducir Pasos para reproducir el error:

  1. Crear una nueva entidad que use relaciones muchos a muchos (probado con perfil, que se relaciona con varios entornos)
  2. Persistir la entidad en la base de datos, usando SQLiteDB.insert().
  3. Obtener los registros insertados en la tabla para relación muchos a muchos.
  4. Ver el error

Comportamiento Esperado El método debería insertar sólo los registros necesarios en la tabla de relación muchos a muchos.

Entorno

Frozen-Burrito commented 2 years ago

Un posible problema que contribuya a esto puede ser la inconsistencia entre la definición y la implementación de SQLiteDb.insert():

En su comentario de documentación se menciona:

/// Inserta un [SQLiteModel] en la base de datos.
/// 
/// Retorna el ID de la entidad insertada.

Mientras que en su implementación, el verdadero valor retornado es el número total de registros modificados:

  // Insertar la entidad principal.
  final insertedId = await db.insert(entity.table, simpleEntityColumns);

  if (insertedId >= 0) {
    // Incrementar filas modificadas.
    totalRowsAltered++;

    // Manejar las relaciones muchos a muchos y uno a muchos de la entidad.
    totalRowsAltered += await _describeRelationships(entity, insertedId: insertedId);
  }

  return totalRowsAltered;

Esto posiblemente ha resultado en un uso inconsistente del método en la app.

Frozen-Burrito commented 2 years ago

Además, al probar este código, se encontró que al insertar un perfil de usuario vacío (como resultado de presionar el botón "omitir" en el formulario inicial) el valor de totalRowsAltered es 3.

Esto es un valor sospechoso, ya que en teoría solo dos registros deberían ser alterados:

  1. El registro para el nuevo perfil de usuario en sí.
  2. El registro para relacionar el nuevo perfil con el entorno por defecto. Este registro es insertado en la tabla entornos_perfil.

Puede no ser un valor incorrecto, pero es necesario revisar más a fondo.

Frozen-Burrito commented 2 years ago

Solucionado

Se resolvieron los dos problemas identificados en este issue:

  1. SQLiteDb.insert() ahora retorna el ID de la entidad principal insertada, en vez de la cantidad de registros alterados. Se aclaró su documentación.
  2. La siguiente sección de código era invocada incondicionalmente en cada iteración de un loop for (var column in mappedEntity.entries) en el método SQLiteDb.insert(). El loop identifica los registros secundarios que deben ser agregados como relaciones muchos a muchos, por lo que se extrajo del loop la sección de código mostrada para que sea invocada una sola vez por invocación de SQLiteDb.insert(), si es que se identificaron registros secundarios m-m:
final requiresMtmOperations = secondaryInsertions.isNotEmpty 
  || secondaryDeletions.isNotEmpty;

if (mappedEntity['id'] is int && requiresMtmOperations) {
  // Hacer las operaciones necesarias en tablas muchos-a-muchos.
  totalRowsAltered += await _modifyManyToMany(
    entity.table, 
    mappedEntity['id'] as int,
    otherInsertedRowIds: secondaryInsertions,
    otherDeletedRowIds: secondaryDeletions,
  );
}