Creación de Entidades y DAO en Base de Datos con Room en Kotlin

Clase 28 de 32Curso de Patrones de Diseño en Android

Contenido del curso

Arquitectura

Resumen

Construir una base de datos local en Android con Room requiere tres componentes fundamentales: una entidad que define la estructura de la tabla, un DAO que gestiona las operaciones y una clase de conexión que administra la instancia de la base de datos. A continuación se desglosa paso a paso cómo implementar cada uno de estos elementos en un proyecto real de transferencias.

¿Cómo se define una entidad en Room para almacenar transferencias?

Dentro del paquete transfers, se crea un nuevo paquete llamado data donde vivirá toda la capa de persistencia. La primera pieza es la entidad, representada por una data class llamada TransferEntity [0:25]. Al usar una data class, no es necesario escribir una clase completa; basta con declarar las variables directamente en el constructor.

Los campos definidos son:

  • transactionId: entero con valor por defecto en cero.
  • userId: string que identifica al usuario.
  • username: string con el nombre del usuario.
  • transactionDate: string con la fecha de la transacción.
  • transactionAmount: string con el monto.
  • receiverUserId: string que indica quién recibió la transferencia.

Room funciona con anotaciones. Se agrega @Entity(tableName = "transfers") para indicar que esta clase generará una tabla llamada transfers [1:40]. La anotación @PrimaryKey(autoGenerate = true) sobre transactionId garantiza que cada registro tenga un identificador único generado automáticamente [2:01].

Cada variable recibe la anotación @ColumnInfo(name = "...") con su nombre en snake_case, que es la convención estándar en bases de datos. Por ejemplo, transactionDate se almacena como transaction_date [2:30]. Si no se define @ColumnInfo, Room usará el nombre original de la variable.

¿Qué es un DAO y cómo se implementa para acceder a los datos?

El segundo componente es el DAO (Data Access Object), una interfaz llamada TransfersDAO [3:22]. Este objeto permite acceder, insertar, consultar y eliminar registros de forma estructurada.

Se definen cuatro funciones principales:

  • getAll(): retorna una lista de TransferEntity con todas las transferencias guardadas.
  • findTransferByUsername(username: String): busca transferencias filtradas por nombre de usuario y retorna una lista.
  • saveTransfer(transferEntity: TransferEntity): guarda una nueva transferencia.
  • delete(transferEntity: TransferEntity): elimina una transferencia existente.

¿Cómo se escriben las consultas SQL con anotaciones?

Para las funciones de lectura se usa @Query con sentencias SQL directas [4:30]. La consulta para obtener todos los registros es SELECT * FROM transfers. Para buscar por usuario, la sentencia incluye un parámetro dinámico con la sintaxis :username, que Room reemplaza en tiempo de ejecución:

sql SELECT * FROM transfers WHERE user_name = :username

Para guardar se utiliza la anotación @Insert y para eliminar @Delete, sin necesidad de escribir SQL manualmente [5:20]. Room autogenera las clases que implementan esta interfaz y ejecuta los métodos de forma transparente.

¿Cómo se configura la conexión a la base de datos con el patrón singleton?

El tercer componente es ApplicationDatabase, una clase abstracta que hereda de RoomDatabase [5:55]. Aquí se implementa el patrón de diseño singleton mediante un companion object que mantiene una única instancia de la base de datos.

¿Cómo funciona la creación y destrucción de la instancia?

La variable instance es privada y nullable, inicializada en null [6:20]. La función getAppDatabase(context: Context) verifica si la instancia es nula; de serlo, la crea usando Room.databaseBuilder() con tres parámetros:

  • El contexto de la aplicación.
  • La referencia a ApplicationDatabase::class.java.
  • El nombre de la base de datos: "plazi_wallet_database".

Se encadena .allowMainThreadQueries() y .build() para construir la instancia [7:15]. Para liberar recursos, la función destroyInstance() asigna null a la variable.

Dentro de ApplicationDatabase se declara una función abstracta getDao() que retorna el TransfersDAO, conectando así la base de datos con el objeto de acceso a datos [8:20].

Para usar todo desde la interfaz, por ejemplo en el click listener del botón de transferencia, se obtiene la instancia con ApplicationDatabase.getAppDatabase(context!!) y luego se llama a .getTransferDao() para ejecutar operaciones como guardar una nueva transferencia [8:40].

¿Has implementado Room en algún proyecto? Comparte tu experiencia y las dificultades que encontraste en el camino.