Το Room είναι μια βιβλιοθήκη ORM από τη σουίτα Android Jetpack που παρέχει ένα επίπεδο αφαίρεσης πάνω από το SQLite για εργασία με τοπικές βάσεις δεδομένων σε Android. Σύμφωνα με την επίσημη τεκμηρίωση Android Developers, 2025, το Room δημιουργεί αυτόματα υλοποιήσεις DAO βάσει σχολιασμών κατά τη μεταγλώττιση, εξαλείφοντας περίπου το 70% του κώδικα προτύπου σε σύγκριση με την άμεση χρήση του SQLiteOpenHelper. Η βιβλιοθήκη εκτελεί επαλήθευση των ερωτημάτων SQL στο στάδιο της μεταγλώττισης, επιτρέποντας τον εντοπισμό συντακτικών σφαλμάτων πριν από την εκτέλεση της εφαρμογής στη συσκευή.
Κύρια Σημεία
Το Room είναι μια βιβλιοθήκη ORM από τη σουίτα Android Jetpack, που δημιουργήθηκε από την Google για να απλοποιήσει την εργασία με τοπικές βάσεις δεδομένων SQLite στην πλατφόρμα Android. Παρέχει σχολιασμούς για την περιγραφή του σχήματος δεδομένων και δημιουργεί αυτόματα την υλοποίηση των διεπαφών DAO στο στάδιο της μεταγλώττισης. Σε αντίθεση με την άμεση χρήση του SQLiteOpenHelper, το Room απελευθερώνει τον προγραμματιστή από τη συγγραφή σημαντικής ποσότητας κώδικα προτύπου για τη δημιουργία, το άνοιγμα και τη διαχείριση της σύνδεσης με τη βάση δεδομένων.
Η βιβλιοθήκη παρουσιάστηκε στο Google I/O 2017 ως μέρος των αρχιτεκτονικών στοιχείων Android. Από τότε το Room έχει γίνει το de facto πρότυπο για τοπική αποθήκευση δεδομένων, ξεπερνώντας σε δημοτικότητα λύσεις όπως τα GreenDAO και Realm για Android. Σύμφωνα με την Google, η βιβλιοθήκη χρησιμοποιείται σε περισσότερο από το 60% των εφαρμογών που δημοσιεύονται στο Google Play και εργάζονται με τοπικά δεδομένα στη συσκευή.
Βασικό χαρακτηριστικό — επαλήθευση ερωτημάτων SQL στο στάδιο μεταγλώττισης με χρήση επεξεργαστή σχολιασμών. Αν ο προγραμματιστής κάνει λάθος σε μια εντολή SQL, για παράδειγμα, αναφέρει ένα ανύπαρκτο όνομα στήλης, η μεταγλώττιση θα τερματιστεί με σφάλμα πριν από την εγκατάσταση της εφαρμογής. Αυτό διαφέρει ριζικά από την προσέγγιση SQLiteOpenHelper, όπου τέτοια σφάλματα ανακαλύπτονται μόνο κατά την εκτέλεση, συχνά στην παραγωγή.
Το SQLite υποστηρίζει μόνο πέντε τύπους δεδομένων: TEXT, INTEGER, REAL, BLOB και NULL. Ωστόσο, σε Java και Kotlin χρησιμοποιούνται σύνθετοι τύποι: Date, List, Enum και προσαρμοσμένα αντικείμενα. Για την αποθήκευσή τους, το Room παρέχει τον μηχανισμό TypeConverters — στατικές μεθόδους που μετατρέπουν έναν σύνθετο τύπο σε πρωτόγονο τύπο κατανοητό από το SQLite. Για παράδειγμα, το αντικείμενο Date μετατρέπεται σε Long (timestamp) και το List<String> σε συμβολοσειρά JSON μέσω Gson ή Moshi.
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
val db = Room
.databaseBuilder(context, AppDatabase::class.java, "app-db")
.build()
Για τη δήλωση του μετατροπέα, αρκεί να προσθέσετε τον σχολιασμό @TypeConverter σε μια στατική μέθοδο και να καθορίσετε την κλάση μετατροπέα στον σχολιασμό @TypeConverters σε επίπεδο βάσης δεδομένων. Το Room εφαρμόζει αυτόματα τον μετατροπέα κατά την ανάγνωση και εγγραφή του αντίστοιχου τύπου σε κάθε ερώτημα SQL χωρίς μη αυτόματη κλήση μεθόδων μετατροπής.
Το Room αποτελείται από τρία βασικά στοιχεία: Entity, DAO και Database. Κάθε ένα εκτελεί έναν αυστηρά καθορισμένο ρόλο και σχολιάζεται με τον αντίστοιχο σχολιασμό. Μαζί σχηματίζουν ένα πλήρες επίπεδο πρόσβασης δεδομένων που απομονώνει την επιχειρηματική λογική της εφαρμογής από τις λεπτομέρειες υλοποίησης του SQLite.
Το Entity είναι μια κλάση δεδομένων που περιγράφει τη δομή ενός πίνακα στη βάση δεδομένων. Κάθε πεδίο της κλάσης αντιστοιχεί σε μια στήλη του πίνακα και κάθε γραμμή στη βάση δεδομένων σε ένα στιγμιότυπο της κλάσης. Ο σχολιασμός @Entity ενημερώνει το Room ότι η κλάση είναι πίνακας. Το πεδίο με τον σχολιασμό @PrimaryKey καθορίζει το πρωτεύον κλειδί, το οποίο μπορεί να είναι αυτό-αυξανόμενο ή σύνθετο. Για σχέσεις μεταξύ πινάκων χρησιμοποιείται το @ForeignKey, που εξασφαλίζει την ακεραιότητα δεδομένων σε επίπεδο βάσης.
@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
@ColumnInfo(name = "full_name")
val name: String,
val age: Int,
val email: String
)
Το DAO (Data Access Object) είναι μια διεπαφή ή αφηρημένη κλάση που δηλώνει λειτουργίες για εργασία με δεδομένα: εισαγωγή, ανάγνωση, ενημέρωση και διαγραφή. Κάθε λειτουργία σχολιάζεται με @Insert, @Query, @Update ή @Delete. Το Room δημιουργεί αυτόματα την υλοποίηση αυτής της διεπαφής στο στάδιο μεταγλώττισης. Ιδιαίτερη αξία έχει ο σχολιασμός @Query, ο οποίος δέχεται ένα ερώτημα SQL ως συμβολοσειρά και ελέγχει την ορθότητά του στο στάδιο κατασκευής.
@Dao
interface UserDao {
@Insert
suspend fun insert(user: User): Long
@Query("SELECT * FROM users WHERE id = :userId")
suspend fun getUserById(userId: Int): User?
@Query("SELECT * FROM users")
fun getAllUsers(): Flow<List<User>>
@Delete
suspend fun delete(user: User)
}
Η Database είναι μια αφηρημένη κλάση που κληρονομεί το RoomDatabase, χρησιμεύοντας ως σημείο εισόδου στη βάση δεδομένων. Περιέχει μια λίστα όλων των Entity και παρέχει αφηρημένες μεθόδους για τη λήψη DAO. Η κλάση σχολιάζεται με @Database, όπου καθορίζονται η έκδοση σχήματος και η λίστα οντοτήτων. Η δημιουργία στιγμιότυπου της βάσης πραγματοποιείται μέσω του Room.databaseBuilder με καθορισμό του περιβάλλοντος εφαρμογής, του ονόματος αρχείου και της κλάσης Database.
Το Room δεν αντικαθιστά το SQLite, αλλά λειτουργεί πάνω από αυτό ως επίπεδο αφαίρεσης. Η εσωτερική αρχιτεκτονική περιλαμβάνει έναν επεξεργαστή σχολιασμών, μια γεννήτρια κώδικα και μια δεξαμενή συνδέσεων. Στο στάδιο μεταγλώττισης, ο επεξεργαστής σχολιασμών αναλύει τις κλάσεις Entity, DAO και Database, στη συνέχεια δημιουργεί κλάσεις υλοποίησης με το επίθημα _Impl. Όλες οι δημιουργημένες κλάσεις τοποθετούνται στο πακέτο κατασκευής και δεν είναι άμεσα ορατές στον προγραμματιστή.
Η δημιουργία κώδικα στο στάδιο μεταγλώττισης — ο κεντρικός μηχανισμός του Room. Για κάθε διεπαφή DAO δημιουργείται μια κλάση με πλήρη υλοποίηση όλων των σχολιασμένων μεθόδων. Τα ερωτήματα SQL από τον σχολιασμό @Query ελέγχονται για ορθότητα: ο επεξεργαστής αντιστοιχίζει τα ονόματα στηλών με τα πεδία Entity και ελέγχει τη σύνταξη SQL. Κατά τον εντοπισμό σφάλματος, η μεταγλώττιση διακόπτεται με ένα κατανοητό μήνυμα. Αυτό είναι αδύνατο κατά τη χρήση ακατέργαστου SQLiteOpenHelper, όπου τα σφάλματα εμφανίζονται μόνο κατά το runtime.
Η διαδικασία δημιουργίας περιλαμβάνει τρία στάδια. Πρώτο — επικύρωση σχήματος: ο επεξεργαστής ελέγχει αν όλες οι κλάσεις που αναφέρονται στο @Database είναι έγκυρες Entity. Δεύτερο — δημιουργία σώματος DAO: για κάθε μέθοδο δημιουργείται μια υλοποίηση χρησιμοποιώντας το εσωτερικό αντικείμενο RoomSQLiteQuery που εκτελεί προετοιμασμένα ερωτήματα. Τρίτο — δημιουργία της κλάσης Database_Impl, που υλοποιεί τη δημιουργία και το άνοιγμα της βάσης καθώς και την αρχικοποίηση όλων των αντικειμένων DAO.
class UserDao_Impl(private val __db: RoomDatabase) : UserDao {
private val __insertionAdapter = __db
.createInsertionAdapter(User::class, 0)
override suspend fun insert(user: User): Long {
__db.assertNotSuspendingTransaction()
return __db.runInTransaction {
__insertionAdapter.insertAndReturnId(user)
}
}
}
Το Room δεν δημιουργεί ξεχωριστή δεξαμενή νημάτων για λειτουργίες βάσης δεδομένων. Από προεπιλογή, τα ερωτήματα εκτελούνται στο νήμα κλήσης με έναν περιορισμό: η ανάγνωση και η εγγραφή μπλοκάρουν το νήμα. Για ασύγχρονη εργασία, το Room ενσωματώνεται με τα coroutines Kotlin μέσω συναρτήσεων suspend, με το LiveData μέσω τιμών επιστροφής και με το Flow μέσω αντιδραστικών περιτυλιγμάτων. Αυτό δίνει στον προγραμματιστή ευελιξία στην επιλογή της αρχιτεκτονικής λύσης για μια συγκεκριμένη εργασία.
Ας εξετάσουμε ένα πρακτικό παράδειγμα δημιουργίας εφαρμογής για αποθήκευση σημειώσεων με χρήση του Room. Η εφαρμογή περιέχει έναν πίνακα Note με πεδία id, title, content και timestamp. Ο χρήστης θα μπορεί να προσθέτει, να βλέπει και να διαγράφει σημειώσεις. Για την επίδειξη χρησιμοποιούνται coroutines για ασύγχρονες λειτουργίες.
Για τη σύνδεση του Room σε ένα έργο Android, πρέπει να προστεθούν εξαρτήσεις στο αρχείο build.gradle της ενότητας εφαρμογής. Το Room απαιτεί τρία στοιχεία: βιβλιοθήκη runtime, επεξεργαστή σχολιασμών kapt και προαιρετική υποστήριξη για coroutines. Η έκδοση της βιβλιοθήκης καθορίζεται στη μεταβλητή room_version για ευκολία ενημέρωσης. Από το Room 2.4.0, το KSP υποστηρίζεται ως εναλλακτική του kapt με υψηλότερη ταχύτητα κατασκευής.
dependencies {
def room_version = "2.6.1"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
// Προαιρετικά: δοκιμή
testImplementation "androidx.room:room-testing:$room_version"
}
Μετά τη διαμόρφωση των εξαρτήσεων, δημιουργούνται τρία αρχεία: το Entity Note, η διεπαφή NoteDao και η κλάση AppDatabase. Το Entity Note περιέχει πεδία με σχολιασμούς @PrimaryKey και @ColumnInfo. Το DAO παρέχει μεθόδους για εισαγωγή, λήψη λίστας και διαγραφή. Η Database συνδέει το Entity και το DAO μέσω του σχολιασμού @Database.
@Entity(tableName = "notes")
data class Note(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
val title: String,
val content: String,
@ColumnInfo(name = "created_at")
val timestamp: Long = System.currentTimeMillis()
)
@Dao
interface NoteDao {
@Insert
suspend fun insert(note: Note)
@Query("SELECT * FROM notes ORDER BY created_at DESC")
fun getAllNotes(): Flow<List<Note>>
@Delete
suspend fun delete(note: Note)
}
Το αρχείο AppDatabase δηλώνεται ως αφηρημένη κλάση που κληρονομεί το RoomDatabase. Στον σχολιασμό @Database αναφέρονται όλες οι Entity της τρέχουσας έκδοσης και ο αριθμός έκδοσης σχήματος. Για τη λήψη στιγμιότυπου χρησιμοποιείται το μοτίβο singleton μέσω της μεθόδου build του Room.databaseBuilder με το περιβάλλον εφαρμογής. Η προσωρινή αποθήκευση του στιγμιότυπου βάσης αποτρέπει πολλαπλές δημιουργίες που θα μπορούσαν να οδηγήσουν σε διαρροές μνήμης.
Οι μετεγκαταστάσεις στο Room είναι ένας μηχανισμός για την αλλαγή του σχήματος βάσης δεδομένων κατά την ενημέρωση της εφαρμογής χωρίς απώλεια υπάρχοντων δεδομένων. Όταν ο χρήστης εγκαθιστά μια νέα έκδοση με τροποποιημένα Entity, το Room ανιχνεύει την αναντιστοιχία εκδόσεων και εκτελεί τα καθορισμένα βήματα μετεγκατάστασης. Χωρίς μετεγκατάσταση, η βάση δεδομένων θα διαγραφεί και θα δημιουργηθεί εκ νέου, οδηγώντας σε απώλεια όλων των αποθηκευμένων δεδομένων χρήστη.
Η μετεγκατάσταση περιγράφεται από την κλάση Migration, η οποία δέχεται την αρχική και τελική έκδοση της βάσης. Εντός της μεθόδου migrate εκτελείται ένα ερώτημα SQL ALTER TABLE ή CREATE TABLE για την αλλαγή του σχήματος. Το Room δεν μπορεί να ανιχνεύσει αυτόματα αλλαγές σχήματος — ο προγραμματιστής πρέπει να γράψει χειροκίνητα τη μετεγκατάσταση για κάθε αλλαγή Entity. Από την έκδοση Room 2.4.0, είναι διαθέσιμη η πειραματική λειτουργία autoMigrations για αυτόματη δημιουργία μετεγκαταστάσεων.
Η λειτουργία autoMigrations επιτρέπει στο Room να δημιουργεί αυτόματα μετεγκαταστάσεις βάσει διαφορών μεταξύ εκδόσεων Entity. Για τη χρήση της, αρκεί να προσθέσετε τον σχολιασμό @AutoMigration στο @Database και να καθορίσετε την εξαγωγή σχήματος σε JSON. Το Room συγκρίνει τα σχήματα γειτονικών εκδόσεων και δημιουργεί τα απαραίτητα ερωτήματα ALTER. Ωστόσο, το autoMigrations υποστηρίζει μόνο αλλαγές συμβατές προς τα πίσω: προσθήκη στηλών, δημιουργία ευρετηρίων και αλλαγή τύπων με συμβατές μετατροπές.
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"ALTER TABLE users ADD COLUMN phone TEXT"
)
}
}
val db = Room
.databaseBuilder(context, AppDatabase::class.java, "app-db")
.addMigrations(MIGRATION_1_2)
.build()
Κατά την προσθήκη σύνθετων αλλαγών, όπως η μετονομασία στηλών ή η συγχώνευση πινάκων, απαιτείται χειροκίνητη μετεγκατάσταση με χρήση προσωρινών πινάκων. Τυπικό σενάριο: δημιουργήστε έναν προσωρινό πίνακα με το παλιό σχήμα, αντιγράψτε δεδομένα από τον παλιό πίνακα στον νέο με μετατροπές, διαγράψτε τον παλιό πίνακα και μετονομάστε τον προσωρινό. Το Room εγγυάται ότι όλες οι μετεγκαταστάσεις εκτελούνται σε μία συναλλαγή και σε περίπτωση σφάλματος, οι αλλαγές ανακαλούνται πλήρως.
Συχνές Ερωτήσεις
Το Room παρέχει αφαίρεση ORM με σχολιασμούς και επαλήθευση SQL στο στάδιο μεταγλώττισης, ενώ το SQLiteOpenHelper απαιτεί χειροκίνητη σύνταξη όλων των ερωτημάτων και διαχείριση σύνδεσης. Το Room δημιουργεί αυτόματα κώδικα για λειτουργίες CRUD και ενσωματώνεται με τα αρχιτεκτονικά στοιχεία Android, συμπεριλαμβανομένων των LiveData και Flow.
Το Room υποστηρίζει όλους τους πρωτόγονους τύπους Java: Int, Long, Boolean, Float, Double, καθώς και String, ByteArray και Date. Για σύνθετους τύπους, όπως List ή Enum, χρησιμοποιούνται TypeConverters — στατικές μέθοδοι μετατροπής που μετατρέπουν μη τυπικούς τύπους σε μορφές υποστηριζόμενες από το SQLite.
Ναι, το Room υποστηρίζει σύγχρονες κλήσεις χωρίς coroutines, αλλά αυτές μπλοκάρουν το νήμα στο οποίο εκτελούνται. Για ασύγχρονη εργασία μπορούν να χρησιμοποιηθούν LiveData ή RxJava αντί για coroutines. Η Google συνιστά τη χρήση coroutines ως τον κύριο τρόπο ασύγχρονης πρόσβασης δεδομένων σε νέα έργα.
Εάν το Room ανιχνεύσει αναντιστοιχία έκδοσης βάσης δεδομένων και δεν βρει κατάλληλη μετεγκατάσταση, από προεπιλογή προκύπτει IllegalStateException με περιγραφή σφάλματος. Ο προγραμματιστής μπορεί να παρακάμψει αυτή τη συμπεριφορά με τη μέθοδο fallbackToDestructiveMigration, η οποία θα διαγράψει την υπάρχουσα βάση και θα δημιουργήσει μια νέα με απώλεια όλων των δεδομένων.
Το Room υποστηρίζει σχέσεις μέσω ένθετων αντικειμένων με σχολιασμό @Embedded και μέσω κλάσεων σχέσης με σχολιασμό @Relation. Για σύνθετα ερωτήματα με σύνδεση πινάκων, χρησιμοποιούνται προσαρμοσμένες κλάσεις POJO των οποίων τα πεδία συμπληρώνονται από αποτελέσματα @Query με τον τελεστή JOIN σε SQL.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης