Creating a Database

There are three steps to creating a database with Room:

  • annotating your model class to make it a database entity

  • creating the class that will represent the database itself

  • creating a type converter so that your database can handle your model data

Room makes each of these steps straightforward, as you are about to see.

Defining entities

Room structures the database tables for your application based on the entities you define. Entities are model classes you create, annotated with the @Entity annotation. Room will create a database table for any class with that annotation.

Since you want to store crime objects in your database, update Crime to be a Room entity. Open Crime.kt and add two annotations:

Listing 11.2  Making Crime an entity (Crime.kt)

@Entity
data class Crime(@PrimaryKey val id: UUID = UUID.randomUUID(),
                 var title: String = "",
                 var date: Date = Date(),
                 var isSolved: Boolean = false)

The first annotation, @Entity, is applied at the class level. This entity annotation indicates that the class defines the structure of a table, or set of tables, in the database. In this case, each row in the table will represent an individual Crime. Each property defined on the class will be a column in the table, with the name of the property as the name of the column. The table that stores your crimes will have four columns: id, title, date, and isSolved.

The other annotation you added is @PrimaryKey, which you added to the id property. This annotation specifies which column in your database is the primary key. The primary key in a database is a column that holds data that is unique for each entry, or row, so that it can be used to look up individual entries. The id property is unique for every Crime, so by adding @PrimaryKey to this property you will be able to query a single crime from the database using its id.

Now that your Crime class is annotated, you can move on to creating your database class.

Creating a database class

Entity classes define the structure of database tables. A single entity class could be used across multiple databases, should your app have more than one database. That case is not common, but it is possible. For this reason, an entity class is not used by Room to create a table unless you explicitly associate it with a database, which you will do shortly.

First, create a new package called database for your database-specific code. In the project tool window, right-click the com.bignerdranch.android.criminalintent folder and choose NewPackage. Name your new package database.

Now, create a new class called CrimeDatabase in the database package and define the class as shown below.

Listing 11.3  Initial CrimeDatabase class (database/CrimeDatabase.kt)

@Database(entities = [ Crime::class ], version=1)
abstract class CrimeDatabase : RoomDatabase() {
}

The @Database annotation tells Room that this class represents a database in your app. The annotation itself requires two parameters. The first parameter is a list of entity classes, which tells Room which entity classes to use when creating and managing tables for this database. In this case, you only pass the Crime class, since it is the only entity in the app.

The second parameter is the version of the database. When you first create a database, the version should be 1. As you develop your app in the future, you may add new entities and new properties to existing entities. When this happens, you will need to modify your entities list and increment your database version to tell Room something has changed. (You will do this in Chapter 15.)

The database class itself is empty at this point. CrimeDatabase extends from RoomDatabase and is marked as abstract, so you cannot make an instance of it directly. You will learn how to use Room to get a database instance you can use later in this chapter.

Creating a type converter

Room uses SQLite under the hood. SQLite is an open source relational database, like MySQL or PostgreSQL. (SQL, short for Structured Query Language, is a standard language used for interacting with databases. People pronounce SQL as either sequel or as an initialism, S-Q-L.) Unlike other databases, SQLite stores its data in simple files you can read and write using the SQLite library. Android includes this SQLite library in its standard library, along with some additional helper classes.

Room makes using SQLite even easier and cleaner, serving as an object-relational mapping (or ORM) layer between your Kotlin objects and database implementation. For the most part, you do not need to know or care about SQLite when using Room, but if you want to learn more you can visit www.sqlite.org, which has complete SQLite documentation.

Room is able to store primitive types with ease in the underlying SQLite database tables, but other types will cause issues. Your Crime class relies on the Date and UUID objects, which Room does not know how to store by default. You need to give the database a hand so it knows how to store these types and how to pull them out of the database table correctly.

To tell Room how to convert your data types, you specify a type converter. A type converter tells Room how to convert a specific type to the format it needs to store in the database. You will need two functions, which you will annotate with @TypeConverter, for each type: One tells Room how to convert the type to store it in the database, and the other tells Room how to convert from the database representation back to the original type.

Create a class called CrimeTypeConverters in the database package and add two functions each for the Date and UUID types.

Listing 11.4  Adding TypeConverter functions (database/CrimeTypeConverters.kt)

class CrimeTypeConverters {

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

    @TypeConverter
    fun toDate(millisSinceEpoch: Long?): Date? {
        return millisSinceEpoch?.let {
            Date(it)
        }
    }

    @TypeConverter
    fun toUUID(uuid: String?): UUID? {
        return UUID.fromString(uuid)
    }

    @TypeConverter
    fun fromUUID(uuid: UUID?): String? {
        return uuid?.toString()
    }
}

The first two functions handle the Date object, and the second two handle the UUIDs. Make sure you import the java.util.Date version of the Date class.

Declaring the converter functions does not enable your database to use them. You must explicitly add the converters to your database class.

Listing 11.5  Enabling TypeConverters (database/CrimeDatabase.kt)

@Database(entities = [ Crime::class ], version=1)
@TypeConverters(CrimeTypeConverters::class)
abstract class CrimeDatabase : RoomDatabase() {
}

By adding the @TypeConverters annotation and passing in your CrimeTypeConverters class, you tell your database to use the functions in that class when converting your types.

With that, your database and table definitions are complete.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.77.114