How to Build a CRUD App With Kotlin and Android Studio

Chapter 4 DAO Classes

4.1 GenderDao.kt

Listing 8: GenderDao.kt
package app.gedama.tutorial.cruddiary.dao
import androidx.lifecycle.LiveData
import androidx.room.*
import app.gedama.tutorial.cruddiary.data.Gender
@Dao
interface GenderDao {
    @Insert
    suspend fun insert(gender: Gender): Long
    @Update
    suspend fun update(gender: Gender)
    @Delete
    suspend fun delete(gender: Gender)
    @Query("SELECT * FROM genders WHERE gender_id = :id")
    fun get(id: Long): LiveData<Gender>
    @Query("SELECT * FROM genders WHERE gender_id > :id AND inactive <= :inactiveValue ORDER BY name COLLATE LOCALIZED ASC")
    fun getAllGenders(id: Long, inactiveValue: Boolean = false): LiveData<List<Gender>>
}

The DAO interface GenderDao.kt belonging to Gender.kt contains the usual database methods insert, update, delte and get. It also contains the method getAllGenders, which returns all entries whose ID is greater than the passed parameter id and whose inactive value is ¡= the parameter inactiveValue, sorted alphabetically. The addition ’COLLATE LOCALIZED’ ensures that, for example, German umlauts are sorted correctly. The parameter id in the method getAllGenders has the following purpose: For tables whose values are displayed in a drop-down list (such as gender), an empty value (with the string value ’—’) is created as the first entry with ID=1 when the database is created. If the app needs this empty value, the id parameter with the value 0 is passed to the getAllGenders method. If the app does not need this empty value (e.g. in the RecyclerView list in which all editable entries are displayed), the id parameter with the value 1 is passed to the getAllGenders method. The parameter inactiveValue specifies whether inactive entries are to be returned (value=1) or not (value=0). If this parameter is not specified when the method is called, 0 is used as the default value (inactive entries are therefore not returned).

4.2 PersonDao.kt

Listing 9: PersonDao.kt
package app.gedama.tutorial.cruddiary.dao
import androidx.lifecycle.LiveData
import androidx.room.*
import app.gedama.tutorial.cruddiary.data.Gender
import app.gedama.tutorial.cruddiary.data.Person
@Dao
interface PersonDao {
    @Insert
    suspend fun insert(person: Person): Long
    @Update
    suspend fun update(person: Person)
    @Delete
    suspend fun delete(person: Person)
    @Query("SELECT * FROM persons WHERE person_id = :id")
    fun get(id: Long): LiveData<Person>
    @Query("SELECT * FROM persons WHERE person_id > :id AND inactive <= :inactiveValue ORDER BY name COLLATE LOCALIZED ASC")
    fun getAllPersons(id: Long, inactiveValue: Boolean = false): LiveData<List<Person>>
    @Query("SELECT * FROM genders WHERE gender_id > :id AND inactive <= :inactiveValue ORDER BY name COLLATE LOCALIZED ASC")
    fun getAllGenders(id: Long, inactiveValue: Boolean = false): LiveData<List<Gender>>
}

The DAO interface PersonDao.kt belonging to Person.kt contains the usual database methods. Analogous to GenderDao.kt, there is a method getAllPersons, which returns persons contained in the database depending on the parameters passed. Furthermore, the method getAllGenders is defined in this DAO class, which is used to determine the genders for the corresponding drop-down list in the user interface.

4.3 TagDao.kt

Listing 10: TagDao.kt
package app.gedama.tutorial.cruddiary.dao
import androidx.lifecycle.LiveData
import androidx.room.*
import app.gedama.tutorial.cruddiary.data.Tag
@Dao
interface TagDao {
    @Insert
    suspend fun insert(tag: Tag): Long
    @Update
    suspend fun update(tag: Tag)
    @Delete
    suspend fun delete(tag: Tag)
    @Query("SELECT * FROM tags WHERE tag_id = :id")
    fun get(id: Long): LiveData<Tag>
    @Query("SELECT * FROM tags WHERE tag_id > :id AND inactive <= :inactiveValue ORDER BY title COLLATE LOCALIZED ASC")
    fun getAllTags(id: Long, inactiveValue: Boolean = false): LiveData<List<Tag>>
}

The DAO interface TagDao.kt belonging to Tag.kt contains the usual database methods and the method getAllTags, which should be understandable based on the previous descriptions.

4.4 PreferencesDao.kt

Listing 11: PreferencesDao.kt
package app.gedama.tutorial.cruddiary.dao
import androidx.lifecycle.LiveData
import androidx.room.*
import app.gedama.tutorial.cruddiary.data.Person
import app.gedama.tutorial.cruddiary.data.Preferences
@Dao
interface PreferencesDao {
    @Insert
    suspend fun insert(preferences: Preferences): Long
    @Update
    suspend fun update(preferences: Preferences)
    @Query("SELECT main_user_id FROM preferences")
    fun getMainUser(): LiveData<Long>
    @Query("SELECT * FROM preferences WHERE preference_id = :id")
    fun getPreferences(id: Long = 1L): LiveData<Preferences>
    @Query("SELECT * FROM persons WHERE person_id > :id AND inactive <= :inactiveValue ORDER BY name COLLATE LOCALIZED ASC")
    fun getAllPersons(id: Long, inactiveValue: Boolean = false): LiveData<List<Person>>
}

The DAO interface PreferencesDao.kt belonging to Preferences.kt contains the usual database methods and the methods getMainUser, which determines the main user, as well as the methods getPreferences() and getAllPersons(), which should be understandable due to the previous descriptions.

4.5 DiaryEntryDao.kt

Listing 12: DiaryEntryDao.kt
package app.gedama.tutorial.cruddiary.dao
import androidx.lifecycle.LiveData
import androidx.room.*
import app.gedama.tutorial.cruddiary.data.DiaryEntriesTagsCrossRef
import app.gedama.tutorial.cruddiary.data.DiaryEntry
import app.gedama.tutorial.cruddiary.data.DiaryEntryWithEverything
import app.gedama.tutorial.cruddiary.data.Person
@Dao
interface DiaryEntryDao {
    @Insert
    suspend fun insert(diaryEntry: DiaryEntry): Long
    @Update
    suspend fun update(diaryEntry: DiaryEntry)
    @Delete
    suspend fun delete(diaryEntry: DiaryEntry)
    @Transaction
    @Query("SELECT * FROM diary_entries WHERE entry_id = :id")
    fun getDiaryEntryWithEverything(id: Long): LiveData<DiaryEntryWithEverything>
    @Transaction
    @Query("SELECT * FROM diary_entries WHERE entry_id > :id AND inactive <= :inactiveValue ORDER BY date DESC")
    fun getDiaryEntriesWithEverything(id: Long, inactiveValue: Boolean = false): LiveData<List<DiaryEntryWithEverything>>
    @Query("SELECT * FROM persons WHERE person_id > :id AND inactive <= :inactiveValue ORDER BY name COLLATE LOCALIZED ASC")
    fun getAllPersons(id: Long, inactiveValue: Boolean = false): LiveData<List<Person>>
    @Query("SELECT * FROM tags WHERE tag_id > :id AND inactive <= :inactive ORDER BY title COLLATE LOCALIZED ASC")
    fun getAllTags(id: Long, inactive: Boolean = false): LiveData<List<Tag>>
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insertDiaryEntryTag(join: DiaryEntriesTagsCrossRef)
    @Query("DELETE FROM diary_entries_tags WHERE entry_id = :id")
    suspend fun deleteAllTagsForEntry(id: Long)
}

The DAO interface DiaryEntryDao.kt belonging to DiaryEntry.kt contains the usual database methods for insert, update and delete. The read methods getDiaryEntryWithEverything() and getDiaryEntriesWithEverything(), on the other hand, use our extended class DiaryEntryWithEverything.kt, so that we also have access to the assigned person and the assigned keywords. Since several database tables are accessed here in one SQL statement (diary_entries, diary_entries_tags, tags), the statements must be marked with the annotation @Transaction. The method inserDiaryEntryTag() is responsible for inserting entries in the table diary_entries_tags, the method deleteAllTagsForEntry() deletes all keywords of a concrete diary entry (with the ID passed as parameter) from the table diary_entries_tags.