-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Room 데이터베이스, 마이그레이션 세팅 및 GoalDao 구현 (#17)
* feat: add Goal property createdAt, compledAt * build: add room dependency * feat: add GoalEntity * feat: add InstantConvertor * feat: add db, dao * feat: load to koinModule * chore: delete .gitkeep * test: add goalEntity fixtures * test: GoalDao Test * feat: db migration base * test: db migration All test * style: ktFormat * chore: rename dao insert Method * chore: DobeDobeDatabase add internal modifier * chore: rearrange db annotation * fix: change to generic type DobeDobeDatabase * chore: import Goal.State * style: ktFormat * refactor: migrate Goal.State to isCompleted * chore: reformat
- Loading branch information
Showing
19 changed files
with
441 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,32 @@ | ||
plugins { | ||
alias(libs.plugins.dobedobe.android.library) | ||
alias(libs.plugins.room) | ||
alias(libs.plugins.ksp) | ||
} | ||
|
||
android { | ||
namespace = "com.chipichipi.dobedobe.core.database" | ||
|
||
sourceSets { | ||
getByName("androidTest").assets.srcDir("$projectDir/schemas") | ||
} | ||
} | ||
|
||
ksp { | ||
arg("room.generateKotlin", "true") | ||
} | ||
|
||
room { | ||
schemaDirectory("$projectDir/schemas") | ||
} | ||
|
||
dependencies { | ||
api(projects.core.model) | ||
implementation(platform(libs.koin.bom)) | ||
implementation(libs.koin.core) | ||
implementation(libs.androidx.room.runtime) | ||
implementation(libs.androidx.room.ktx) | ||
ksp(libs.androidx.room.compiler) | ||
|
||
androidTestImplementation(libs.androidx.room.testing) | ||
} |
64 changes: 64 additions & 0 deletions
64
core/database/schemas/com.chipichipi.dobedobe.core.database.db.DobeDobeDatabase/1.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
{ | ||
"formatVersion": 1, | ||
"database": { | ||
"version": 1, | ||
"identityHash": "8d37e040dbba463eff631f177a3d3b67", | ||
"entities": [ | ||
{ | ||
"tableName": "Goal", | ||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `isPinned` INTEGER NOT NULL, `isCompleted` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `completedAt` INTEGER)", | ||
"fields": [ | ||
{ | ||
"fieldPath": "id", | ||
"columnName": "id", | ||
"affinity": "INTEGER", | ||
"notNull": true | ||
}, | ||
{ | ||
"fieldPath": "title", | ||
"columnName": "title", | ||
"affinity": "TEXT", | ||
"notNull": true | ||
}, | ||
{ | ||
"fieldPath": "isPinned", | ||
"columnName": "isPinned", | ||
"affinity": "INTEGER", | ||
"notNull": true | ||
}, | ||
{ | ||
"fieldPath": "isCompleted", | ||
"columnName": "isCompleted", | ||
"affinity": "INTEGER", | ||
"notNull": true | ||
}, | ||
{ | ||
"fieldPath": "createdAt", | ||
"columnName": "createdAt", | ||
"affinity": "INTEGER", | ||
"notNull": true | ||
}, | ||
{ | ||
"fieldPath": "completedAt", | ||
"columnName": "completedAt", | ||
"affinity": "INTEGER", | ||
"notNull": false | ||
} | ||
], | ||
"primaryKey": { | ||
"autoGenerate": true, | ||
"columnNames": [ | ||
"id" | ||
] | ||
}, | ||
"indices": [], | ||
"foreignKeys": [] | ||
} | ||
], | ||
"views": [], | ||
"setupQueries": [ | ||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", | ||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8d37e040dbba463eff631f177a3d3b67')" | ||
] | ||
} | ||
} |
22 changes: 0 additions & 22 deletions
22
...e/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/ExampleInstrumentedTest.kt
This file was deleted.
Oops, something went wrong.
72 changes: 72 additions & 0 deletions
72
.../database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDaoTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package com.chipichipi.dobedobe.core.database.dao | ||
|
||
import androidx.room.Room | ||
import androidx.test.core.app.ApplicationProvider | ||
import com.chipichipi.dobedobe.core.database.db.DobeDobeDatabase | ||
import com.chipichipi.dobedobe.core.database.entity.GoalEntity | ||
import com.chipichipi.dobedobe.core.database.fixtures.fakeGoalEntities | ||
import com.chipichipi.dobedobe.core.database.fixtures.fakeGoalEntity | ||
import io.kotest.matchers.booleans.shouldBeTrue | ||
import io.kotest.matchers.collections.shouldHaveSize | ||
import io.kotest.matchers.shouldBe | ||
import kotlinx.coroutines.flow.first | ||
import kotlinx.coroutines.test.runTest | ||
import kotlinx.datetime.Instant | ||
import org.junit.Before | ||
import kotlin.test.Test | ||
|
||
class GoalDaoTest { | ||
private lateinit var goalDao: GoalDao | ||
|
||
@Before | ||
fun setUp() { | ||
goalDao = | ||
Room.inMemoryDatabaseBuilder( | ||
ApplicationProvider.getApplicationContext(), | ||
DobeDobeDatabase::class.java, | ||
).build().goalDao() | ||
} | ||
|
||
@Test | ||
fun 목표_저장_테스트() = | ||
runTest { | ||
// given | ||
val goals: List<GoalEntity> = fakeGoalEntities("준원", "준혁") | ||
|
||
// when | ||
goalDao.insertGoals(goals) | ||
|
||
// then | ||
val retrievedGoals: List<GoalEntity> = goalDao.getGoals().first() | ||
val retrievedGoalTitles = retrievedGoals.map { it.title } | ||
retrievedGoals.shouldHaveSize(2) | ||
retrievedGoalTitles shouldBe listOf("준원", "준혁") | ||
} | ||
|
||
@Test | ||
fun 목표_저장후_삭제_테스트() = | ||
runTest { | ||
// given | ||
val goal: GoalEntity = fakeGoalEntity(id = 1L, title = "준원") | ||
|
||
// when | ||
goalDao.insertGoal(goal) | ||
goalDao.deleteGoal(goalId = 1L) | ||
// then | ||
val retrievedGoals: List<GoalEntity> = goalDao.getGoals().first() | ||
retrievedGoals.shouldHaveSize(0) | ||
} | ||
|
||
@Test | ||
fun 목표_완료하기_테스트() = | ||
runTest { | ||
// given | ||
val goal: GoalEntity = fakeGoalEntity(id = 1L, title = "준원", isCompleted = false) | ||
// when | ||
goalDao.insertGoal(goal) | ||
goalDao.updateGoal(goal.copy(isCompleted = true, completedAt = Instant.DISTANT_FUTURE)) | ||
// then | ||
val retrievedGoal: GoalEntity = goalDao.getGoal(1L).first() | ||
retrievedGoal.isCompleted.shouldBeTrue() | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
.../src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeMigrationTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package com.chipichipi.dobedobe.core.database.db | ||
|
||
import android.content.Context | ||
import androidx.room.Room | ||
import androidx.room.testing.MigrationTestHelper | ||
import androidx.test.core.app.ApplicationProvider | ||
import androidx.test.platform.app.InstrumentationRegistry | ||
import org.junit.Rule | ||
import org.junit.Test | ||
import org.junit.jupiter.api.DisplayName | ||
import java.io.IOException | ||
|
||
class DobeDobeMigrationTest { | ||
@get:Rule | ||
val helper = | ||
MigrationTestHelper( | ||
InstrumentationRegistry.getInstrumentation(), | ||
DobeDobeDatabase::class.java, | ||
) | ||
|
||
@Test | ||
@DisplayName("모든 database 버전 마이그레이션 테스트") | ||
@Throws(IOException::class) | ||
fun test_migrateAll() { | ||
// given | ||
val dbName = "TEST_DB" | ||
val oldestDbVersion = 1 | ||
val testContext = ApplicationProvider.getApplicationContext<Context>() | ||
|
||
// when : 가장 오래된 버전의 DB를 생성하고 닫음 | ||
helper.createDatabase(dbName, oldestDbVersion) | ||
.close() | ||
|
||
// then : 최신 버전 DB로 마이그레이션 후 검증 | ||
Room.databaseBuilder( | ||
testContext, | ||
DobeDobeDatabase::class.java, | ||
dbName, | ||
).addMigrations(*DobeDobeDatabase.MIGRATIONS) | ||
.build().apply { | ||
openHelper.writableDatabase.close() | ||
} | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
...e/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/fixtures/FakeGoalEntity.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package com.chipichipi.dobedobe.core.database.fixtures | ||
|
||
import com.chipichipi.dobedobe.core.database.entity.GoalEntity | ||
import kotlinx.datetime.Clock | ||
import kotlinx.datetime.Instant | ||
|
||
fun fakeGoalEntities( | ||
vararg titles: String, | ||
): List<GoalEntity> { | ||
return titles.mapIndexed { index, title -> | ||
fakeGoalEntity( | ||
id = (index + 1).toLong(), | ||
title = title, | ||
) | ||
} | ||
} | ||
|
||
fun fakeGoalEntity( | ||
id: Long, | ||
title: String, | ||
isPinned: Boolean = false, | ||
isCompleted: Boolean = false, | ||
createdAt: Instant = Clock.System.now(), | ||
completedAt: Instant? = null, | ||
): GoalEntity { | ||
return GoalEntity( | ||
id = id, | ||
title = title, | ||
isPinned = isPinned, | ||
isCompleted = isCompleted, | ||
createdAt = createdAt, | ||
completedAt = completedAt, | ||
) | ||
} |
Empty file.
14 changes: 14 additions & 0 deletions
14
...abase/src/main/kotlin/com/chipichipi/dobedobe/core/database/convertor/InstantConverter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.chipichipi.dobedobe.core.database.convertor | ||
|
||
import androidx.room.TypeConverter | ||
import kotlinx.datetime.Instant | ||
|
||
internal class InstantConverter { | ||
@TypeConverter | ||
fun longToInstant(value: Long?): Instant? = | ||
value?.let(Instant::fromEpochMilliseconds) | ||
|
||
@TypeConverter | ||
fun instantToLong(instant: Instant?): Long? = | ||
instant?.toEpochMilliseconds() | ||
} |
33 changes: 33 additions & 0 deletions
33
core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDao.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package com.chipichipi.dobedobe.core.database.dao | ||
|
||
import androidx.room.Dao | ||
import androidx.room.Insert | ||
import androidx.room.OnConflictStrategy | ||
import androidx.room.Query | ||
import androidx.room.Update | ||
import com.chipichipi.dobedobe.core.database.entity.GoalEntity | ||
import kotlinx.coroutines.flow.Flow | ||
|
||
@Dao | ||
interface GoalDao { | ||
@Query("SELECT * FROM Goal") | ||
fun getGoals(): Flow<List<GoalEntity>> | ||
|
||
@Query("SELECT * FROM Goal WHERE id = :id") | ||
fun getGoal(id: Long): Flow<GoalEntity> | ||
|
||
@Insert(onConflict = OnConflictStrategy.REPLACE) | ||
suspend fun insertGoal(goals: GoalEntity) | ||
|
||
@Insert(onConflict = OnConflictStrategy.REPLACE) | ||
suspend fun insertGoals(goals: List<GoalEntity>) | ||
|
||
@Query("DELETE FROM Goal WHERE id = :goalId") | ||
suspend fun deleteGoal(goalId: Long) | ||
|
||
@Update | ||
suspend fun updateGoal(goal: GoalEntity) | ||
|
||
@Query("DELETE FROM Goal") | ||
suspend fun clear() | ||
} |
25 changes: 25 additions & 0 deletions
25
core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package com.chipichipi.dobedobe.core.database.db | ||
|
||
import androidx.room.Database | ||
import androidx.room.RoomDatabase | ||
import androidx.room.TypeConverters | ||
import androidx.room.migration.Migration | ||
import com.chipichipi.dobedobe.core.database.convertor.InstantConverter | ||
import com.chipichipi.dobedobe.core.database.dao.GoalDao | ||
import com.chipichipi.dobedobe.core.database.entity.GoalEntity | ||
|
||
@Database( | ||
entities = [GoalEntity::class], | ||
version = 1, | ||
) | ||
@TypeConverters( | ||
InstantConverter::class, | ||
) | ||
internal abstract class DobeDobeDatabase : RoomDatabase() { | ||
abstract fun goalDao(): GoalDao | ||
|
||
companion object { | ||
const val DATABASE_NAME = "dobedobe.db" | ||
val MIGRATIONS: Array<Migration> = arrayOf() | ||
} | ||
} |
4 changes: 4 additions & 0 deletions
4
core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/di/DaoModule.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,12 @@ | ||
package com.chipichipi.dobedobe.core.database.di | ||
|
||
import com.chipichipi.dobedobe.core.database.dao.GoalDao | ||
import com.chipichipi.dobedobe.core.database.db.DobeDobeDatabase | ||
import org.koin.dsl.module | ||
|
||
val daoModule = | ||
module { | ||
includes(databaseModule) | ||
|
||
single<GoalDao> { get<DobeDobeDatabase>().goalDao() } | ||
} |
11 changes: 11 additions & 0 deletions
11
core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/di/DatabaseModule.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,18 @@ | ||
package com.chipichipi.dobedobe.core.database.di | ||
|
||
import androidx.room.Room | ||
import com.chipichipi.dobedobe.core.database.db.DobeDobeDatabase | ||
import org.koin.dsl.module | ||
|
||
internal val databaseModule = | ||
module { | ||
single<DobeDobeDatabase> { | ||
Room.databaseBuilder( | ||
get(), | ||
DobeDobeDatabase::class.java, | ||
DobeDobeDatabase.DATABASE_NAME, | ||
).fallbackToDestructiveMigration() // TODO: 최초 배포 시 삭제 | ||
.addMigrations(*DobeDobeDatabase.MIGRATIONS) | ||
.build() | ||
} | ||
} |
Oops, something went wrong.