๊ฐœ๋ฐœ ์ผ์ง€ ๐Ÿ’ป/Nutri Capture

Nutri Capture ๋ฐฑ์—”๋“œ - Room์˜ @DAO, @Database ๊ตฌํ˜„

interfacer_han 2024. 10. 23. 12:26

#1 ๊ฐœ์š”

์ด์ „ ๊ฒŒ์‹œ๊ธ€์— ์ด์–ด, Room์˜ ๋‚จ์€ ๋ถ€๋ถ„์„ ๊ตฌํ˜„ํ•œ๋‹ค.

 

#2 ์ฝ”๋“œ

#2-1 @DAO

// package com.example.nutri_capture_new.db

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import java.time.LocalDate

@Dao
interface MainDAO {
    @Query("SELECT day_id FROM day_table WHERE day_date = :date LIMIT 1")
    suspend fun getDayId(date: LocalDate): Long?

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertDay(day: Day): Long

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertMeal(meal: Meal): Long

    @Delete
    suspend fun deleteMeal(meal: Meal): Int

    @Query("""
    SELECT meal_table.* 
    FROM meal_table 
    INNER JOIN day_table ON meal_table.day_id = day_table.day_id 
    WHERE day_table.day_date = :targetDate 
    ORDER BY meal_table.meal_time ASC
    """)
    suspend fun getMealsOrderedByTime(targetDate: LocalDate): List<Meal>

    @Query("SELECT COUNT(*) FROM meal_table WHERE day_id = :dayId")
    suspend fun getMealCountForDay(dayId: Long): Int

    @Query("DELETE FROM day_table WHERE day_id = :dayId")
    suspend fun deleteDay(dayId: Long)
}

์ฒ˜์Œ๋ถ€ํ„ฐ ์ด ๋ชจ๋“  ํ•จ์ˆ˜๋“ค์„ ์ƒ๊ฐํ•ด๋‚ธ ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. ๋ณธ ๊ฒŒ์‹œ๊ธ€ ์ดํ›„ ๊ฒŒ์‹œ๊ธ€์—์„œ INSERT, SELECT ๋“ฑ์„ View์—์„œ๋ถ€ํ„ฐ Triggerํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค์—ˆ๋Š”๋ฐ ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค๋ฉฐ ์ด DAO์˜ ํ•จ์ˆ˜๋“ค๊ณผ ์•„๋ž˜์— ์žˆ๋Š” Repository์˜ ํ•จ์ˆ˜๋“ค์„ ํ•˜๋‚˜ํ•˜๋‚˜ ๋งŒ๋“ค์–ด๋ƒˆ๋‹ค. ์ฆ‰, ์ง€๊ธˆ์€ @Dao ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ์ธํ„ฐํŽ˜์ด์Šค ํ•˜๋‚˜๋งŒ ์ •์˜ํ•˜๊ณ  ๋„˜์–ด๊ฐ€๋„ ์ƒ๊ด€์ด ์—†๋‹ค๋Š” ๋ง์ด๋‹ค.

 

#2-2 Repository

// package com.example.nutri_capture_new.db

import java.time.LocalDate

class MainRepository(private val dao: MainDAO) {
    suspend fun insertMeal(meal: Meal, date: LocalDate): Long {
        var dayId = dao.getDayId(date)
        if (dayId == null) {
            dayId = dao.insertDay(Day(date = date))
        }
        return dao.insertMeal(meal.copy(dayId = dayId))
    }

    suspend fun deleteMeal(meal: Meal): Int {
        val deletedRowCount = dao.deleteMeal(meal)

        if (deletedRowCount == 0) {
            return 0

        } else {
            val mealCount = dao.getMealCountForDay(meal.dayId)
            if (mealCount == 0) {
                dao.deleteDay(meal.dayId)
            }
            return deletedRowCount
        }
    }

    suspend fun getMealsOrderedByTime(targetDate: LocalDate): List<Meal> {
        return dao.getMealsOrderedByTime(targetDate)
    }
}

insertMeal( ... )

Meal์„ INSERTํ•˜๋ ค๊ณ  ์‹œ๋„ํ•  ๋•Œ ๋ถ€๋ชจ ํ…Œ์ด๋ธ” ์ฆ‰, Day ํ…Œ์ด๋ธ”์ด ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด ์—๋Ÿฌ๊ฐ€ ๋‚  ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ, Day ํ…Œ์ด๋ธ”์ด ์—†๋‹ค๋ฉด ๋จผ์ € Day ํ…Œ์ด๋ธ”๋ถ€ํ„ฐ ์ƒ์„ฑํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ง‘์–ด๋„ฃ๋Š”๋‹ค.

 

deleteMeal( ... )

Meal์ด ํ•˜๋‚˜๋„ ์—†๋Š” Day๋Š” ์กด์žฌ ์ด์œ ๊ฐ€ ์—†๋‹ค. ๋”ฐ๋ผ์„œ, Meal์ด ํ•˜๋‚˜๋„ ์—†๋‹ค๋ฉด Day ํ…Œ์ด๋ธ”์„ ์‚ญ์ œํ•œ๋‹ค.

 

getMealsOrderedByTime( ... )

์–ด๋–ค ๋‚ (Day)์˜ ๋จน์€ ๊ฒƒ๋“ค(List<Meal>)์„ ์˜ค๋ฆ„์ฐจ์ˆœ(#2-1์˜ DAO ์ฝ”๋“œ ์ฐธ์กฐ)์œผ๋กœ ๊ฐ€์ ธ์˜จ๋‹ค.

 

#2-3 TypeConverter

 

Room์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ์ฐธ์กฐ  |  Android Developers

์ด ํŽ˜์ด์ง€๋Š” Cloud Translation API๋ฅผ ํ†ตํ•ด ๋ฒˆ์—ญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. Room์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ์ฐธ์กฐ ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด ์ •๋ฆฌํ•˜๊ธฐ ๋‚ด ํ™˜๊ฒฝ์„ค์ •์„ ๊ธฐ์ค€์œผ๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ถ„๋ฅ˜ํ•˜์„ธ์š”. Room์€ ๊ธฐ๋ณธ ์œ ํ˜•๊ณผ

developer.android.com

Room์€ LocalDateํ˜• ๋ฐ LocalTimeํ˜• ๋ณ€์ˆ˜๋ฅผ ๋‚ด๋ถ€์ ์œผ๋กœ ์ €์žฅํ•˜์ง€ ๋ชปํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ์œ„ ๊ณต์‹๋ฌธ์„œ์˜ ๊ฐ€์ด๋“œ๋ฅผ ๋”ฐ๋ผ ์•„๋ž˜ ์žˆ๋Š” TypeConverter ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

 

// package com.example.nutri_capture_new.db

import androidx.room.TypeConverter
import java.time.LocalDate
import java.time.LocalTime
import java.time.format.DateTimeFormatter

class Converters {
    private val dateFormatter = DateTimeFormatter.ISO_LOCAL_DATE
    private val timeFormatter = DateTimeFormatter.ISO_LOCAL_TIME

    // LocalDate -> String
    @TypeConverter
    fun fromLocalDate(date: LocalDate?): String? {
        return date?.format(dateFormatter)
    }

    // String -> LocalDate
    @TypeConverter
    fun toLocalDate(dateString: String?): LocalDate? {
        return dateString?.let { LocalDate.parse(it, dateFormatter) }
    }

    // LocalTime -> String
    @TypeConverter
    fun fromLocalTime(time: LocalTime?): String? {
        return time?.format(timeFormatter)
    }

    // String -> LocalTime
    @TypeConverter
    fun toLocalTime(timeString: String?): LocalTime? {
        return timeString?.let { LocalTime.parse(it, timeFormatter) }
    }
}

 

#2-4 @Database

// package com.example.nutri_capture_new.db

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters

@Database(
    entities = [
        Day::class,
        Meal::class
    ],
    version = 1
)
@TypeConverters(Converters::class)
abstract class MainDatabase : RoomDatabase() {

    abstract val mainDAO: MainDAO

    companion object {
        @Volatile
        private var INSTANCE: MainDatabase? = null
        fun getInstance(context: Context): MainDatabase {
            synchronized(this) {
                var instance = INSTANCE
                if (instance == null) {
                    instance = Room.databaseBuilder(
                        context.applicationContext,
                        MainDatabase::class.java,
                        "main_database"
                    ).build()
                    INSTANCE = instance
                }
                return instance
            }
        }
    }
}

์ด ๊ฒŒ์‹œ๊ธ€์— ์žˆ๋Š” Database ์ƒ์šฉ๊ตฌ ์ฝ”๋“œ๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ–ˆ๋‹ค. #2-3์—์„œ ๋งŒ๋“  TypeConverter๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ตฌ๋ฌธ(@TypeConverters(Converters::class))๋„ ๋„ฃ์–ด์ค€๋‹ค.

 

#3 ์š”์•ฝ

Room์˜ Entity ๋ถ€๋ถ„์„ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€(Dao์™€ Database ๊ทธ๋ฆฌ๊ณ  TypeConverter์™€ Repository)๋ฅผ ๊ตฌํ˜„ํ–ˆ๋‹ค.

 

#4 ์™„์„ฑ๋œ ์•ฑ

#4-1 ์ด ๊ฒŒ์‹œ๊ธ€ ์‹œ์ ์˜ Commit

 

GitHub - Kanmanemone/nutri-capture-new

Contribute to Kanmanemone/nutri-capture-new development by creating an account on GitHub.

github.com

 

#4-2 ๋ณธ ํ”„๋กœ์ ํŠธ์˜ ๊ฐ€์žฅ ์ตœ์‹  Commit

 

GitHub - Kanmanemone/nutri-capture-new

Contribute to Kanmanemone/nutri-capture-new development by creating an account on GitHub.

github.com