Article image Using Room for Database Management: Defining Entities in Room

59.3. Using Room for Database Management: Defining Entities in Room

Page 80 | Listen in audio

When developing Android applications, efficient data management is crucial. Room, a part of Android Jetpack, serves as a robust and efficient solution for managing databases in Android applications. It provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite. One of the core components of Room is the Entity, which represents a table within the database. Understanding how to define entities in Room is essential for any developer looking to implement database functionality in their Android apps.

To begin with, an entity in Room is a simple Java or Kotlin class annotated with @Entity. This class represents a table in the database, where each field in the class corresponds to a column in the table. The @Entity annotation is crucial as it tells Room that this class should be treated as a table in the database. Here is a basic example:

@Entity(tableName = "users")
data class User(
    @PrimaryKey val uid: Int,
    @ColumnInfo(name = "first_name") val firstName: String?,
    @ColumnInfo(name = "last_name") val lastName: String?
)

In this example, we have a simple User class that Room will treat as a table named "users". The @PrimaryKey annotation is used to denote the primary key of the table, which is a unique identifier for each record. In this case, uid serves as the primary key.

The @ColumnInfo annotation is optional but can be used to specify custom column names. If you omit @ColumnInfo, Room will use the variable name as the column name by default. Here, the firstName and lastName fields are mapped to columns "first_name" and "last_name" respectively.

Room also supports various data types, including primitive types, boxed types, and String. For more complex data types, such as lists or custom objects, you need to use Type Converters. A Type Converter is a class that Room uses to convert complex data types into a form that can be stored in the database, and vice versa. Here is how you can define a Type Converter:

class Converters {
    @TypeConverter
    fun fromTimestamp(value: Long?): Date? {
        return value?.let { Date(it) }
    }

    @TypeConverter
    fun dateToTimestamp(date: Date?): Long? {
        return date?.time?.toLong()
    }
}

To use this converter, you must add it to your database class using the @TypeConverters annotation:

@Database(entities = [User::class], version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

Another important aspect of defining entities is handling relationships between tables. Room supports one-to-one, one-to-many, and many-to-many relationships. Let’s consider a one-to-many relationship between a User and Book entities, where a user can have multiple books:

@Entity
data class Book(
    @PrimaryKey val bookId: Int,
    val userOwnerId: Int,
    val title: String
)

To represent this relationship, you can use a data class that contains the entities involved:

data class UserWithBooks(
    @Embedded val user: User,
    @Relation(
        parentColumn = "uid",
        entityColumn = "userOwnerId"
    )
    val books: List
)

In this example, the @Embedded annotation is used to include the User object, and the @Relation annotation defines the relationship between the User and Book entities. The parentColumn specifies the primary key of the parent entity, and the entityColumn specifies the column in the child entity that references the parent entity’s primary key.

Room also provides support for indexing and foreign keys. Indexing can improve query performance, especially for large datasets. You can define an index on a column using the indices parameter in the @Entity annotation:

@Entity(
    tableName = "users",
    indices = [Index(value = ["last_name"], unique = true)]
)
data class User(
    @PrimaryKey val uid: Int,
    val firstName: String?,
    val lastName: String?
)

The unique parameter ensures that all values in the indexed column are unique.

Foreign keys are used to enforce referential integrity between tables. You can define a foreign key in an entity using the foreignKeys parameter in the @Entity annotation:

@Entity(
    foreignKeys = [ForeignKey(
        entity = User::class,
        parentColumns = arrayOf("uid"),
        childColumns = arrayOf("userOwnerId"),
        onDelete = ForeignKey.CASCADE
    )]
)
data class Book(
    @PrimaryKey val bookId: Int,
    val userOwnerId: Int,
    val title: String
)

In this example, the userOwnerId column in the Book table is a foreign key referencing the uid column in the User table. The onDelete parameter specifies the action to take when a referenced row is deleted. In this case, CASCADE means that when a User is deleted, all associated Book entries will also be deleted.

It is also important to handle data migrations when updating the database schema. Room provides a migration mechanism to ensure data integrity when the database schema changes. Migrations are defined by creating a Migration object and specifying the start and end versions:

val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("ALTER TABLE users ADD COLUMN birthdate INTEGER")
    }
}

Finally, when creating the Room database instance, you need to pass the migration to the Room.databaseBuilder method:

val db = Room.databaseBuilder(
    context.applicationContext,
    AppDatabase::class.java, "database-name"
).addMigrations(MIGRATION_1_2)
 .build()

In conclusion, defining entities in Room is a fundamental aspect of managing databases in Android applications. By understanding how to annotate classes with @Entity, manage relationships, use Type Converters, and handle data migrations, developers can efficiently utilize Room to create robust and efficient data management solutions. Whether you're building a simple app or a complex system with multiple data relationships, mastering Room's capabilities will significantly enhance your Android development skills.

Now answer the exercise about the content:

What is the purpose of the @Entity annotation in Room when developing Android applications?

You are right! Congratulations, now go to the next page

You missed! Try again.

Article image Using Room for Database Management: Creating Data Access Objects (DAOs)

Next page of the Free Ebook:

81Using Room for Database Management: Creating Data Access Objects (DAOs)

6 minutes

Earn your Certificate for this Course for Free! by downloading the Cursa app and reading the ebook there. Available on Google Play or App Store!

Get it on Google Play Get it on App Store

+ 6.5 million
students

Free and Valid
Certificate with QR Code

48 thousand free
exercises

4.8/5 rating in
app stores

Free courses in
video, audio and text