如何按月分隔房间数据库?

How to saparate room databases by months?

由于需要按月显示数据,我想制作单独的房间数据库。例如:我需要显示 4 月份的费用,所以我需要导出一个代表 4 月份费用的数据库,并仅将其用于这个月。有什么解决办法吗?这是我的数据库:

Expense.kt

@Entity(tableName = "expenses_table")
data class Expense (
    @PrimaryKey(autoGenerate = true)
    val id: Int,
    val expenseDate: String,
    val expenseType: String,
    val expenseCost: Int
)

ExpenseDao.kt

@Dao
interface ExpenseDao {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun addExpense(expense: Expense)

    @Query("SELECT * FROM expenses_table ORDER BY id ASC")
    fun readAllData(): LiveData<List<Expense>>
}

ExpenseDatabase.kt

@Database(entities = [Expense::class], version = 1, exportSchema = false)
   abstract class ExpenseDatabase: RoomDatabase() {

    abstract fun expenseDao(): ExpenseDao

    companion object {
        @Volatile
        private var INSTANCE: ExpenseDatabase? = null

        fun getDatabase(context: Context): ExpenseDatabase {
            val tempInstance = INSTANCE
            if (tempInstance != null) {
                return tempInstance
            }
            synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    ExpenseDatabase::class.java,
                    "expense_table"
                ).build()
                INSTANCE = instance
                return instance
            }
        }

        }
    }

这不是一个理想的解决方案。即使您找到了解决方案,想象一下一年后您将拥有 12 个不同的数据库。

我会建议你根据需要查询数据库。

I want to make separated room databases due to my needs which is showing the data by months.

需要按月获取数据并不等同于需要有单独的数据库。但是,以下示例仅需要对您的 ExpenseDatabase class 进行一些修改:-

@Database(entities = [Expense::class], version = 1, exportSchema = false)
abstract class ExpenseDatabase: RoomDatabase() {

    abstract fun expenseDao(): ExpenseDao

    companion object {
        @Volatile
        private var INSTANCE: ExpenseDatabase? = null

        fun getDatabase(context: Context, /* ADDED >>>>>*/yearMonthPrefix: String, /* ADDED >>>>>*/ swap: Boolean = false): ExpenseDatabase {
            val tempInstance = INSTANCE
            if (tempInstance != null && !swap) {
                return tempInstance
            }
            synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    ExpenseDatabase::class.java,
                     /* CHANGED >>>>>*/ "${yearMonthPrefix}_expense_table")
                    .build()
                INSTANCE = instance
                return instance
            }
        }
    }
}

根据您提供的信息。解决您的问题的最简单且可能最有效的解决方案是拥有一个数据库,其中所有费用都存储在 expenses_table 中,并使用查询来提取当月的费用。

这里的重要因素是 expenseDate column/field 和存储数据格式的适用性。如果您使用 SQLite recognised format,例如 YYYY-MM-DD,则 SQLite Date/Time 函数的格式为 known/understood。

如果是这样,您可以使用以下方法获取当月的 Expense 列表。

@Query("SELECT * FROM expenses_table WHERE strftime('%Y%m',expenseDate) = strftime('%Y%m','now') ORDER BY id ASC")
fun readCurrentMonthsData(): LiveData<List<Expense>>
  • 这利用了 SQLite strftime 函数和 now 时间值

以下是您将年和月作为字符串传递的变体,因此可以获得任何年份的任何月份的数据:-

@Query("SELECT * FROM expenses_table WHERE substr(expenseDate,1,7)=:datepart ORDER BY id ASC")
fun readMonthsData(datepart: String): LiveData<List<Expense>>
  • 这使用 SQLite substr 函数

演示版

考虑以下内容(.allowMainTrhreadQueries 添加到 buildDatabase 以允许演示使用主线程):-

注意 包含查询(演示版本 return List<Expense> 而不是 LiveData<List<Expense>> 为了方便和简洁)

class MainActivity : AppCompatActivity() {
    lateinit var db: ExpenseDatabase
    lateinit var dao: ExpenseDao
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        db = ExpenseDatabase.getDatabase(this, "202201")
        dao = db.expenseDao()

        dao.addExpenseDemo(Expense(0,"2022-01-01","Type",100))
        dao.addExpenseDemo(Expense(0,"2022-01-11","Type",100))
        dao.addExpenseDemo(Expense(0,"2022-01-21","Type",100))
        dao.addExpenseDemo(Expense(0,"2022-01-31","Type",100))

        /* Swap to February Dataabase */
        db = ExpenseDatabase.getDatabase(this,"202202",true)
        dao = db.expenseDao()
        dao.addExpenseDemo(Expense(0,"2022-02-01","Type",100))
        dao.addExpenseDemo(Expense(0,"2022-02-02","Type",100))
        dao.addExpenseDemo(Expense(0,"2022-02-03","Type",100))

        for (e: Expense in dao.readCurrentMonthsDataDemo()) {
            Log.d("EXPENSEINFO001","Expense ID is ${e.id} Date is ${e.expenseDate} etc.")
        }
        /* None will be located as only 2022-02 rows in database */
        for (e: Expense in dao.readMonthsDataDemo("2022-01")) {
            Log.d("EXPENSEINFO002","Expense ID is ${e.id} Date is ${e.expenseDate} etc.")
        }

        /* Swap to January Database */
        db = ExpenseDatabase.getDatabase(this,"202201", true)
        dao = db.expenseDao()
        /* None will be located as only 2022-01 rows in database */
        for (e: Expense in dao.readCurrentMonthsDataDemo()) {
            Log.d("EXPENSEINFO003","Expense ID is ${e.id} Date is ${e.expenseDate} etc.")
        }
        for (e: Expense in dao.readMonthsDataDemo("2022-01")) {
            Log.d("EXPENSEINFO004","Expense ID is ${e.id} Date is ${e.expenseDate} etc.")
        }
    }
}

演示结果(包含在日志中):-

D/EXPENSEINFO001: Expense ID is 1 Date is 2022-02-01 etc.
D/EXPENSEINFO001: Expense ID is 2 Date is 2022-02-02 etc.
D/EXPENSEINFO001: Expense ID is 3 Date is 2022-02-03 etc.
 
 
D/EXPENSEINFO004: Expense ID is 1 Date is 2022-01-01 etc.
D/EXPENSEINFO004: Expense ID is 2 Date is 2022-01-11 etc.
D/EXPENSEINFO004: Expense ID is 3 Date is 2022-01-21 etc.
D/EXPENSEINFO004: Expense ID is 4 Date is 2022-01-31 etc.

通过 App Inspection 的数据库:-

并通过设备文件资源管理器:-

  • 请注意,虽然每个实际的数据库文件只有 4k,但 -wal 文件中的数据将被应用(不是全部,但至少 12K(每个 [=91= 至少 4K ])).因此,多个数据库文件将浪费相对大量的文件 space 每个数据库。

  • 交换数据库也会产生额外的开销。