Data: Database Schema (Room)
Relevant source files
The following files were used as context for generating this wiki page:- app/src/main/java/com/musagenius/ocrapp/data/local/entity/ScanEntity.kt
- app/src/main/java/com/musagenius/ocrapp/data/local/dao/ScanDao.kt
- app/src/main/java/com/musagenius/ocrapp/data/local/database/AppDatabase.kt
- app/src/main/java/com/musagenius/ocrapp/data/local/Converters.kt
- app/src/main/java/com/musagenius/ocrapp/di/DatabaseModule.kt
- app/src/main/java/com/musagenius/ocrapp/data/repository/ScanRepositoryImpl.kt
The application utilizes the Jetpack Room Persistence Library to provide an abstraction layer over SQLite for robust local data storage. This system is responsible for persisting all OCR scan results, allowing for offline access, history management, and search functionality. The implementation follows modern Android architecture principles, using a repository pattern and Hilt for dependency injection to create a decoupled and testable data layer.
The core components of the database system are the AppDatabase class, the ScanEntity data class which defines the database schema, and the ScanDao interface which provides methods for database access. These components work together to manage all CRUD (Create, Read, Update, Delete) operations for the scanned data.
Architecture Overview
The database is integrated into the application's data layer, following a standard MVVM with Clean Architecture pattern. The ScanRepositoryImpl acts as the single source of truth for scan data, abstracting the data source (Room database) from the domain and presentation layers. ViewModels interact with the repository, which in turn uses the ScanDao to execute database operations.
This diagram illustrates the flow of data and dependencies from the UI/ViewModel down to the Room database.
Database Class (AppDatabase)
The AppDatabase class is the main entry point for accessing the database. It is an abstract class that extends RoomDatabase.
- Entities: It defines
ScanEntityas the only entity, which corresponds to thescanstable in the database. - Version: The database is currently at
version = 1. - Type Converters: It uses
Convertersto handle types that Room cannot store natively, such asDateandUri. - DAO: It provides an abstract function
scanDao()to get an instance ofScanDao.
Sources: app/src/main/java/com/musagenius/ocrapp/data/local/database/AppDatabase.kt:13-23
@Database(
entities = [ScanEntity::class],
version = 1,
exportSchema = true
)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun scanDao(): ScanDao
companion object {
const val DATABASE_NAME = "ocr_app_database"
// ... migrations
}
}
Sources: app/src/main/java/com/musagenius/ocrapp/data/local/database/AppDatabase.kt:13-28
Migrations
The AppDatabase companion object includes a placeholder for database migrations (MIGRATION_1_2). While no migrations are currently active, the structure is in place to handle future schema changes without losing user data. For development, fallbackToDestructiveMigration() is enabled in debug builds to simplify schema updates by recreating the database.
Sources: app/src/main/java/com/musagenius/ocrapp/data/local/database/AppDatabase.kt:33-38, app/src/main/java/com/musagenius/ocrapp/di/DatabaseModule.kt:33-35
Entity: ScanEntity
The ScanEntity data class defines the schema for the scans table in the database. Each instance of this class represents a single row in the table, corresponding to one saved OCR scan.
This Entity-Relationship diagram shows the structure of the scans table.
Table Schema: scans
The table is indexed on timestamp and language to improve query performance for sorting and filtering operations.
| Field | Type (Kotlin/SQL) | Description | Constraints & Defaults |
|---|---|---|---|
id | Long / INTEGER | Auto-generated primary key for the scan. | PRIMARY KEY, AUTOINCREMENT |
timestamp | Date / INTEGER | The date and time the scan was created. | NOT NULL |
imageUri | Uri / TEXT | The URI pointing to the stored image file. | NOT NULL |
extractedText | String / TEXT | The text extracted by the OCR process. | NOT NULL |
language | String / TEXT | The language code (e.g., "eng") used for OCR. | NOT NULL |
confidenceScore | Float / REAL | The confidence score of the OCR result. | NOT NULL |
title | String / TEXT | An optional title for the scan. | Default: "" |
tags | String / TEXT | Comma-separated list of tags for organization. | Default: "" |
notes | String / TEXT | Optional user notes about the scan. | Default: "" |
isFavorite | Boolean / INTEGER | A flag to mark the scan as a favorite. | Default: 0 (false) |
modifiedTimestamp | Date / INTEGER | Timestamp of the last modification. | Default: value of timestamp |
Sources: app/src/main/java/com/musagenius/ocrapp/data/local/entity/ScanEntity.kt:15-45
Data Access Object (ScanDao)
The ScanDao is an interface annotated with @Dao that defines all the database operations. It provides a clear API for the ScanRepositoryImpl to interact with the scans table. All methods are suspend functions or return a Flow, ensuring that they are executed off the main thread.
Key ScanDao Methods
| Method Signature | Description | Return Type |
|---|---|---|
insert(scan: ScanEntity) | Inserts a single scan. | suspend fun -> Long (ID) |
update(scan: ScanEntity) | Updates an existing scan. | suspend fun |
deleteById(scanId: Long) | Deletes a scan by its primary key. | suspend fun |
deleteAll() | Deletes all scans from the table. | suspend fun |
getById(scanId: Long) | Retrieves a single scan by its ID. | suspend fun -> ScanEntity? |
getAllScans() | Retrieves all scans, ordered by newest first. | Flow<List<ScanEntity>> |
searchScans(query: String) | Performs a full-text search on extracted_text, title, tags, and notes. | Flow<List<ScanEntity>> |
getFavoriteScans() | Retrieves all scans marked as favorites. | Flow<List<ScanEntity>> |
updateFavoriteStatus(scanId: Long, isFavorite: Boolean) | Updates the is_favorite flag for a specific scan. | suspend fun |
deleteOldScans(cutoffDate: Long) | Deletes scans older than a given timestamp. | suspend fun -> Int (count) |
Sources: app/src/main/java/com/musagenius/ocrapp/data/local/dao/ScanDao.kt
Example Query: searchScans
The searchScans function demonstrates a powerful full-text search capability across multiple columns using the LIKE operator.
SELECT * FROM scans
WHERE extracted_text LIKE '%' || :query || '%'
OR title LIKE '%' || :query || '%'
OR tags LIKE '%' || :query || '%'
OR notes LIKE '%' || :query || '%'
ORDER BY timestamp DESC
Sources: app/src/main/java/com/musagenius/ocrapp/data/local/dao/ScanDao.kt:56-63
Type Converters
Since Room can only store primitive types, TypeConverters are used to handle complex types like java.util.Date and android.net.Uri. The Converters class provides functions to convert these types to and from types that Room understands (Long and String, respectively).
class Converters {
@TypeConverter
fun fromTimestamp(value: Long?): Date? {
return value?.let { Date(it) }
}
@TypeConverter
fun dateToTimestamp(date: Date?): Long? {
return date?.time
}
@TypeConverter
fun fromString(value: String?): Uri? {
return value?.let { Uri.parse(it) }
}
@TypeConverter
fun uriToString(uri: Uri?): String? {
return uri?.toString()
}
}
Sources: app/src/main/java/com/musagenius/ocrapp/data/local/Converters.kt:9-35
Dependency Injection
The database and its DAO are provided to the rest of the application via Hilt dependency injection. The DatabaseModule is responsible for creating and providing singleton instances of AppDatabase and ScanDao.
This diagram shows how Hilt provides the database components.
provideAppDatabase: Builds the Room database instance usingRoom.databaseBuilder. It specifies the database name (ocr_app_database) and enables destructive migration for debug builds.provideScanDao: Obtains theScanDaoinstance from the providedAppDatabase.
Both providers are annotated with @Singleton to ensure only one instance of the database and DAO exist throughout the application's lifecycle.
Sources: app/src/main/java/com/musagenius/ocrapp/di/DatabaseModule.kt:20,26,39
Summary
The local database system, built with Room, is a cornerstone of the application's data layer. It provides a robust, efficient, and type-safe mechanism for persisting OCR scan data. The use of Flow for queries enables a reactive UI that automatically updates when the underlying data changes. The entire module is cleanly architected and integrated with Hilt, making it easy to manage, test, and extend.