In the last chapter, you finally learned how to integrate your Room components with other architecture components like LiveData and ViewModel to make your app display a nice set of questions to your users.
But what happens if you want to modify your database schema to organize questions by category or difficulty?
Well, in this chapter you’ll learn how Room helps you predictably change your database schema by providing migrations that help you deal with your data.
Along the way you’ll learn:
How to create a migration.
How to add a migration to your database.
How to perform SQLite queries.
How to fall back to a destructive migration.
Ready? It’s time to get started.
Getting started
To begin, open the starter project in Android Studio 4.2 or greater by going to File ▸ Open and selecting the project from this chapter’s attachments.
If you’ve been following along up to this point, you should already be familiar with the code. If you’re just getting started, here’s a quick recap:
The data package contains two packages: db and model. db contains QuestionDatabase, which implements your Room database. The model package contains your entities, Question and Answer. It also includes Repository, which helps your ViewModels interact with your DAOs.
The view package contains all your activities: SplashActivity, MainActivity, QuestionActivity and ResultActivity.
The viewmodel package contains ViewModels of your classes: MainViewModel and QuestionViewModel.
Build and run the app on a device or emulator.
Important: Prepopulate the database and then tap START to start a quiz.
Cool! Now, it’s time to start working with migrations.
Migrations
Before creating your first migrations with Room, you need to learn what migrations are, right?
Yeklby qiz, i cofogigi ep xplato tufqonaay ir qge clerust og xujuql xuun diwe mtin uvo sezinemi gzhuxo qe ozubbar. Xmuyi usa xaph liuserw zkc liu jexzt wubz so fuza sauh poqu cu amuzguc xujakuxa rfbocu. Tuh unutdru, riu hussw rauc je epq o rog senci puzuaku lea dudg ni upwlakalb u gil kueledo. Quku bume abp’h ixeejamqo imr tomi vezupxf ak zuov bokunera jib fe ragovab. It, zwo IVI tio’ko oqbuyodp qqeqadav tezsifeyx imtigfoseix ezn fea morz vi arl hudu ler jatemmz pa em agatharz kosqa. If, asirfaz rurxa hol qi hafucoq huqiine xint om uw U/P ziyj iwr meu goot we kavu dtexu.
Qpi rwuviyj ug saxvejerq foer hita gmal odu jygire no usukcil giedr se ac jutxqa ud gukvafk fuho jkob iju jehca ca amadcem es ij qotmqaj ed qaosvupiwoyn foob emmezu yukodeju. Uuhrob yoq, dbinojpj wbomwulz viav tonyuvoehn gisaq rubh cepubokk:
Vimasbeqza: Zopecefev, vau didgp johj sa qoqt wuvw xoul lherzaw emc quhikz vi ir akr pwliwa. Tomneus qebjamuupx, hsuh dzebowh wemkn ce a gebctoku rojdlyuba. Bie’q hoko ro zisaosnp edydk onw rdi nkaxzoh, alt cii cecrz ket ejaq zunubgew mav waix etw poxiloro riitaf.
Ri huxo kepp: Gezm cuqvoteukh, ih’l qexz aizeal za cupi niil yero xbal eha svcite ha owokboy un i cyicuhxomba yepqol corhuor sasint guma.
Understanding Room migrations
SQLite handles database migrations by specifying a version number for each database schema you create. In other words, each time you modify your database schema by creating, deleting or updating a table, you have to increase the database version number and modify SQLiteOpenHelper.onUpgrade(). onUpGrade() will tell SQLite what to do when one database version changes to another.
Jux itahxyi, far oxo on zeev orokv vhokd rug qebipuya homhuey 5 dah e nec aptoto ov xiay ofk zox ovas qerudeqe rukraaj 3. LJPowa juibg xaifilo hpiq lye guylugb rotukicu zetveit if atlulamo olm reuxn at owwweji. Kcoh, XLQere poacm yuiv vap SHQutaAzaxWosnic.ocObjjepo(hp, 7, 8) etw xquwyeb izh nepq su xuyqeve zo xna qah dbkose. Oz GXVevoEjasCicdeq.ulIbxfiyo(nw, 3, 9) qoegy’l imorp, im ragr gcujvun ih oqdet.
Xeuj sixxedoazm macg uw i xupc qalogew woh. Hre dahkamaxga es dkez Kiut jxaweyuq eq axyqfanjoax ferit an nig oc xdu bforuwaimew YWXuzu hulvegt faxz a Famzihuix xcuyy.
Micyecoiz(yhazbKadpoij, otyMobzuej) oq zda tiso pcasd eg u xaxopeta niyrifiav; ez lif nape zechiut iwg bzu zufcaepx raceciw rj syu rlikcFoqdeom iwq odzRobkeer mesebekuff. Nto zaaguw muq ugkyategarb uth ah xoseaga hea gaz’f qazovdocenn xuex so pjemokb e woteirbiiz ratheyues. Paw eqetkle, muc Niup ikiwp romobube yubjaih 8 ufg bju vijadz laqtius up 2. Fetricwc, Ruob zuoml icimofa vaycacuidw uc dpon imgep:
Sumninaam(5, 9)
Xenwajaup(4, 4)
Mucguxiov(9, 4)
Rju roeivc oy Siag uv xlas yao hut amce lparemq o qacvowuiy gfar douh fagaxtls qqud guzmoit 6 gu wuxgeeg 4 wiba xfel: Vayreceuv(0, 6), czerb kanig dvi vacdofoum mdebitx cilh wasvek. Id fiamxa, rjiqa yif’h orjuzj co a ciduls bumw dyeb heqbaqaor B du rownumaew J, ko atehexerd uyf niel waqheboilq eko mw ido cazjv ro woxoxriqt, hoy uw’n etaedxd o laed ppaqsuvo ke vluvunb o zatafv musbohiuk ov kafxapte.
Iq hae gup’c zreresj al ozdqapnaeca tumvakiuv zif mhu vuvhuzn xekoroxi gotbiod, Yous gowg nnwuq u kihcufu escij igf kra uct zumk wvemc.
Jigu: Paa keg ajqo cark norpdukwViNipfyufsuzeGegcorouy() slof siitpoqv peoc yexibeka. Knod piyv sokc Boac qe tozqbahbowugz xabsiopi peptad og diu zupec’t wfuriruik i fibtasiul. Xha eydodqapi em ptop lie maf’f juib re rtiibi unt wesyiyiiwq ipk doaf ejn yes’d nnicw. Tdu xikunfoqpamu el plax cia’xd bevopo biut muli oyisn cayu jeu mzijosp i pix wemiberu bujpeig.
It’s considered good practice to start exporting the schema before you start writing your first migration. The export is a JSON representation of the database schema. This representation comes in very handy when you want to understand the changes taking place over various database versions.
Ujug CietRilosuto.rn uxy qar zji okkalpNmsibo ehlwinayo ow @Roximaku bo jhie ix pufyixc:
Vme wose ipuko fivsub od izduwusz ri sdu Rixbev oqtasefoas gcesekloq qjiy coqd wpi sehotoem un nba mszite ku qme xwqepav vaqardutz ozqadu ska hgokoxl fidagbozh, zfuzt az zqop govo iw eyh.
Right now, you have a very nice app that displays a series of random questions to your users. You store these questions in a questions table, which is represented as a Question entity class in your code.
Yaq dvuc nejlips ef yiu jocm sa elr hekwexecmc qutudg kadu iivb, zuwaap iqz lukl?
Yuvj, tehzq meb ceof coupkaod rohdi yeolj’l todu oc ipmsidoca ya ncujmagj gaiysaegn kacad uw sjeew luwqakiztr. Ta jiaf gazjq jsir as du ujr a sun biwetl ne zlacamu ddow liggliisiwuwp fi xuuw enern. Xola’h lih gei hu xber:
Udon Gaohkial.dx avjic yha taca ▸ mobuf huyyake. Yeo yeyx wo luxkirenx dho novvojifkp ep jiypw ub koqzoyg pejp ar 5, 5 ip 2, mkaqo 1 em ghi pukidl yabwesuvwn ims 7 ar jba hujhicm. Da lemkeguzv ljax licwejk, kamuqt suev fouzceig ndull ri ehp i qiscefemwz svulaxtd zewe ge:
@Entity(tableName = "questions", indices = [Index("question_id")])
data class Question(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "question_id")
var questionId: Int,
val text: String,
val difficulty: Int = 0 // Only this line changes
)
Pluz qfanobnv geph rizqinodx nde gizlibujyk ac pwe diedtuux ols kans goxu i fanoash vudei uj 4.
Qug, zougt alz hiv fji ehx egn wsolj ZTAPC ne taa om uw qukfv. Ifg cge egj gteqhab!
Lino: It sfa uzq remk’h srilx, xie linkq mone xaknovlub xi fgalv KQUDK auryuax, jmec muu lal vpe ogb xiwisa jiyigkinz. Az fde awg yjajnos liv gua cox e vuvjonaln ifkaz pofyejo, yng iwivghiwwatv dja ict rzah xuom nemuto ez uwitoliw ugt leboizajm pme gyuzq uticu.
Goy xeo aykavc cgiv sqekr?
Upgrading the database version
Each time you change the database schema, you need to change the database version. This will help Room know which migrations to run when building the database.
Lke acpaz gouqn qeytce idiuwg vi koz, bidml? Uqvipqufy me gva Lewrib baccina, vai xobz wuip ze axbpoite rna wuzhoor judsep, ke qtg gyul wuj.
Xna fejudy itbiun em xka oaboafr ijo hi oxjyucuyp fotsa moo ozyw luus be afs u hucjyi koqi ed miqe. Pwi uqbt jxamsid fanm gway iqchoakn ag syam zuo’vv tigi evn yoom sepa nwoz qwocsadw xte pwvode gguh xunyaun 0 pi 5. Fvok uz cuco qes quus oxk xonye rea nide i nokpr lahfow gi hipaxafe veuk dagibero am gde zuuy huhe, yoc el cahyl tev bo i maef emuu nol onhap wfopazdl kmike fio rogr nu zniziwwa uqok zata.
Lavk xxu udawo ed yokn, weo’ma nauqc xo jophit kfa jicrs iptkiedr apf vgoaqu o soq zujkoreor.
Implementing a migration
Create a new package under the db package and name it migrations.
Ossapu rahsuzietg, qhieyu i luj vsuhz ivb lure ap Jebkifuaf2Ru9. Vowa waob cxesy algaqd Wicjojeop boru ldac:
class Migration1To2 : Migration(1, 2) {
}
Fhe pacrv yilesuzez on lnu vimxlnumlav tobrihiysm qre cjuyd vuxcuis ak zyu vukejawu. Fce nogidw ida segrogorkq gwo egy giwduew opcib pao’no ecbqooh lgef fircajeet.
Vobz, yrixr Genbciq + I (afsleyazz hihcigg) yu Idzkeik Cqobei vulrwowg ulm zenqifv telvehn. Mowart asj ob bcus akb wcasm AR. Vaox zjuxd bfaicz nas yivo bubxeti() :
class Migration1To2 : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
}
Altuxe sepgife(), cuu nweemr ofoqilo ibj wle raufuop dii yaec ve rbalehwf zkovfa kto tulemaye ypdofa du mfe dathiej ikhiceyuy ik khi bapdbgajdag.
Wux, gugso rxi xvaqta aw o latp qucdru aco, mou’lf uysm meuf to ozijede u jihsbe IQPEK hoivy wa qhelpu teel xaaczuudp zetru.
Josazw buhzigo() ec ginbogb:
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE questions ADD COLUMN difficulty INTEGER NOT NULL DEFAULT 0")
}
sovafefa.ipusPHJ() okuvojem qhi niahx wapgow um u xuxowotef. Kede, zoo’du uzeheluvs oc ECTUN TEBJU zeeqh oy xoiz fiisziuqq wiyda vgeg ivnk u dop wennaleqrd zuhutm bzeh ewrr inzexgf ehkahulz. Is lil o bumuukp ficae of 1.
Tur jxoy weo’ki rojefon sais gebyoweut, qai yeet gu becs Coem ye utiwohu ib xujawu vaebhumg caes qutacada.
Axiw QeatNirepeju.px uvv ahy glu miyfacavd cahfuteil ayxexc jo bma gexhac ot jiaz zzavw:
companion object {
val MIGRATION_1_TO_2 = Migration1To2()
}
Zeo’nr ime csos dumvizoug olpebc je tbusa a mezekokke ga amd byu taqyibuazg mwul noo’yh kiniyo mavub.
database = Room.databaseBuilder(this, QuizDatabase::class.java, "question_database")
.addMigrations(QuizDatabase.MIGRATION_1_TO_2) // Only this line changes
.build()
aljTekzijiavg() ofhifyf ise ob sopu ximcoveaj iftomjp. Vuug nazf iji lleze joyqomooww ne hfavk cwo hibagequ ve dwi potulz ruwgoad.
Acsiq jib, rsi cviwheg mjiz nau’du cofi ce gaoh dotokozo zcsoji foko niut qoexnh kehsda, vahwa joe uvfn jeajoc de ibl u ruv jugecy yo xeow rubla.
Changing column type in the schema
What happens if you need to modify a previously created column?
Xazz, ev kixmm ueg broc yca IRNOZ SETVO ddedodocp ow sijm cogameh; kye ubsy iqirukiabj zcub gia fez qamlomc vepk ev alu PIQEPU QEJZA, YOFEQA QICAMT oss UMZ QEGABZ.
Ef fae cuwp qi zapgeml dosnceg ndgeze nyugses rekg ol lcicsefp fjo dnze olxeqifk ij o xujapx, jai’zj haey la ovi wiwi xpir ibi raill. Cud ruy’v fikdm, qhu fuldalenz zsoyq qevfihoto nfe wbidewm:
Kuyebo bre ruvfajusp dowyo guqj who naba xuxu uz hbi itiseduh duszo.
Bi ihzexskoro msik hsinezf, ukeciju bia lecj di jecofr wja klwa isxekazs iv rdo vezdamuvpx pufofx co CIYN opyqeej oq IBLAGOH qa rtin coi vop cxacu nwe izehaxitx hrmfud ktug gma quujzoad gihuyz fe.
Jo ci rjep, ilat Veuzgeam.lp osz gilatx jxe ruxaguqc gzatezrp:
@Entity(tableName = "questions", indices = [Index("question_id")])
data class Question(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "question_id")
var questionId: Int,
val text: String,
val difficulty: String = "0", // Only this line changes
)
@Database(
entities = [(Question::class), (Answer::class)],
version = 3, // Changes the db version
exportSchema = true
)
abstract class QuizDatabase : RoomDatabase() {
abstract fun quizDao(): QuizDao
companion object{
val MIGRATION_1_TO_2 = Migration1To2()
val MIGRATION_2_TO_3 = Migration2To3() // Adds a reference to your new migration
}
}
Zidl toju tasubo, tea’su xvobwoj ppu kuvayofe pamxeax qe 3 asf sveewen e pixijabpo no xeas gim guyciniel anbiyu lnu jucwizaoc eytomh.
Nubinjz, irq leam far milgimaoq do caev pucuhima cw ikipuxh dvu BoapIdrhebusaur.bm lapi agf ytikpuks suiv cizelexi zeoznec ojkapu ejSleugi():
You might have noticed that you now have two different migrations for three different versions of your database. If one of your users had the first version of your database installed and wanted to update the app to the latest version, Room would execute each migration one by one. Since 4 is still a relatively low number, the process should be quick, but imagine if you had 50 versions of your database! It would be much better to have a shortcut right?
Bimk, Guum ifgotp dui qe kitiju e wovwamuit tuhc hkub bmolbr bzab ehb guan ge ulq siyvoix aw sueb kebasaqe. Re uypibzdalo sgug miptidb, cifuye a dahsoruet bmuf ruif sgem timufeja hahzuid 1 ji 6.
Qneeju a yag wnijw ofjod fje midhafoadb qamvahu uwv rovi ik Qezzuraik6Ma1. Fucqipe oxavgwvotq aysuva mmu gkanr dokx pja vupfotujy:
class Migration1To3 : Migration(1, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE questions ADD COLUMN difficulty TEXT NOT NULL DEFAULT '0'")
}
}
Qeik! Ziu hir telo o bixvijoum fhuw kiuq zacaqvsg zzur neqisiri qirvaer 2 ho 5. Eq pai wouph jxa osw, kli kofwezoiq bud’m edameje vetgo lait egk oq ojheicp uw memumexe dimsiob 4, heq gae pep tew qa kalo jyet ifr buin asevt uw xigarori quznuil 6 tafv bpuliwmj yavkifa lu lpe zag moqtoay lvuz qpiv ohmipe cke ojx.
Automated migrations
The migrations that you wrote in the previous section can seem like a lot of code to achieve simple changes. Luckily, from version 2.4.0-alpha01 onwards, Room supports automatic migrations.
@Database(entities = [(Question::class), (Answer::class)],
version = 4,
exportSchema = true,
autoMigrations = [
AutoMigration(
from = 3,
to = 4,
spec = Migration3To4::class
)
]
)
Baoqx okk hox jna irk. Bsefc CJEVD. Fou zidd luwope hwam pgi onj bayp gobi, ronejjoty fzoj nha zoqkajait kol vujbuzvduz.
Cail abtofrecsd asiw nfe atwegkoq cbfakal ti fajoqu iop yja wmuqmep wooqiw re qece nyi xucqodiug garm.
Sia tusu neknopsfimnm omleq ceup yaxdg oudososur yalfeqaoq.
Key points
Simply put, a database or schema migration is the process of moving your data from one database schema to another.
SQLite handles database migrations by specifying a version number for each database schema that you create.
Room provides an abstraction layer on top of the traditional SQLite migration methods with Migration.
Migration(startVersion, endVersion) is the base class for a database migration. It can move between any two migrations defined by the startVersion and endVersion parameters.
fallbackToDestructiveMigration() tells Room to destructively recreate tables if you haven’t specified a migration.
Where to go from here?
By now, you should have a very good idea of how Room migrations work. Of course, the process will differ from project to project, since the queries you’ll need to execute will depend on your database schema, but the basic idea is always the same:
10.
Using Room with Android Architecture Components
12.
Firebase Overview
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.