Slide 1

Slide 1 text

NO ROOM FOR DOUBT Getting Started With ROOM #DevFest17 #DevFestSW

Slide 2

Slide 2 text

Omolara Adejuwon (@_larikraun) Android, iOS @ LawPavilion #DevFest17 #DevFestSW

Slide 3

Slide 3 text

Room is a persistence library that provides an abstraction layer over SQLite.

Slide 4

Slide 4 text

Features ● Wrapper around SQLite ● Eliminates boilerplate code ● Upgrade database versions with ease - MIGRATION ● Enforces you don’t perform database operations on the main thread ● Checks query at compile time ● Testing is easier

Slide 5

Slide 5 text

Features ● Wrapper around SQLite ● Eliminates boilerplate code ● Upgrade database versions with ease - MIGRATION ● Enforces you don’t perform database operations on the main thread ● Checks query at compile time ● Testing is easier

Slide 6

Slide 6 text

Features ● Wrapper around SQLite ● Eliminates boilerplate code ● Upgrade database versions with ease - MIGRATION ● Enforces you don’t perform database operations on the main thread ● Checks query at compile time ● Testing is easier

Slide 7

Slide 7 text

Features ● Wrapper around SQLite ● Eliminates boilerplate code ● Upgrade database versions with ease - MIGRATION ● Enforces you don’t perform database operations on the main thread ● Checks query at compile time ● Testing is easier

Slide 8

Slide 8 text

Features ● Wrapper around SQLite ● Eliminates boilerplate code ● Upgrade database versions with ease - MIGRATION ● Enforces you don’t perform database operations on the main thread ● Checks query at compile time ● Testing is easier

Slide 9

Slide 9 text

Features ● Wrapper around SQLite ● Eliminates boilerplate code ● Upgrade database versions with ease - MIGRATION ● Enforces you don’t perform database operations on the main thread ● Checks query at compile time ● Testing is easier

Slide 10

Slide 10 text

Major Components ● @Entity - Defines table structure ● @DAO - An interface or abstract class. The functions define how to access the database ● @Database - Connects all the pieces of Room together

Slide 11

Slide 11 text

Let’s Get Started

Slide 12

Slide 12 text

Update project level gradle file buildscript { repositories { google() ... } ... }

Slide 13

Slide 13 text

Update app level gradle file dependencies { ... implementation "android.arch.persistence.room:runtime:1.0.0" annotationProcessor "android.arch.persistence.room:compiler:1.0.0" ... }

Slide 14

Slide 14 text

Basic POJO public class Attendee { private int id; private String name; private String email; private String category; private String extraFieldWeWantToIgnore; //other Getters & Setters } id name email category 1 Omolara [email protected] Speaker ... ... ... ... Attendee Table

Slide 15

Slide 15 text

Create Entity @Entity (tableName="Attendee") public class Attendee { @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") private int id; @ColumnInfo(name = "name") private String name; @ColumnInfo(name = "email") private String email; private String category; @Ignore private String extraFieldWeWantToIgnore; //other Getters & Setters } id name email category 1 Omolara [email protected] Speaker ... ... ... ... Attendee Table

Slide 16

Slide 16 text

Create Entity @Entity (tableName="Attendee") public class Attendee { @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") private int id; @ColumnInfo(name = "name") private String name; @ColumnInfo(name = "email") private String email; private String category; @Ignore private String extraFieldWeWantToIgnore; //other Getters & Setters } id name email category 1 Omolara [email protected] Speaker ... ... ... ... Attendee Table

Slide 17

Slide 17 text

Create Entity @Entity (tableName="Attendee") public class Attendee { @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") private int id; @ColumnInfo(name = "name") private String name; @ColumnInfo(name = "email") private String email; private String category; @Ignore private String extraFieldWeWantToIgnore; //other Getters & Setters } id name email category 1 Omolara [email protected] Speaker ... ... ... ... Attendee Table

Slide 18

Slide 18 text

Create Entity @Entity (tableName="Attendee") public class Attendee { @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") private int id; @ColumnInfo(name = "name") private String name; @ColumnInfo(name = "email") private String email; private String category; @Ignore private String extraFieldWeWantToIgnore; //other Getters & Setters } id name email category 1 Omolara [email protected] Speaker ... ... ... ... Attendee Table

Slide 19

Slide 19 text

Create the DAO @Dao public interface AttendeeDao { @Insert/@delete/@update void insert/delete/update (Attendee attendee); } Annotations for the win

Slide 20

Slide 20 text

Create the DAO @Dao public interface AttendeeDao { @Insert/@delete/@update void insert/delete/update (Attendee attendee); @Query("Select * from attendee where email=:email") Attendee getAttendedByEmail (String email); } Parameter Binding

Slide 21

Slide 21 text

Create the DAO @Dao public interface AttendeeDao { @Insert/@delete/@update void insert/delete/update (Attendee attendee); @Query("Select * from attendee where email=:email") Attendee getAttendedByEmail (String email); @Query("Select * from attendee") List getAllAttendees (); } Return a list

Slide 22

Slide 22 text

Create the DAO @Dao public interface AttendeeDao { @Query("Select * from attendeeess where email=:email") Attendee getAttendedByEmail (String email); } Compile Time Error

Slide 23

Slide 23 text

Create the database class @Database(entities = {Attendee.class}, version = 1) public abstract class AppDatabase extends RoomDatabase { public abstract AttendeeDao attendeeDao (); }

Slide 24

Slide 24 text

Get the database instance public static AppDatabase database; database = Room.databaseBuilder (this, AppDatabase.class, "database_name") .build (); CAUTION!!! Recommended to keep it a singleton ‘cos database calls are not cheap

Slide 25

Slide 25 text

Access the DB - Insert new record Attendee attendee = new Attendee (1, "Omolara Adejuwon", "[email protected]", "Speaker"); RoomApp.database.attendeeDao ().insert (attendee);

Slide 26

Slide 26 text

Access the DB - Retrieve All Attendees List attendees = RoomApp.database.attendeeDao ().getAllAttendees ();

Slide 27

Slide 27 text

Access the DB - Retrieve Attendees By Email Attendee attendee = RoomApp.database.attendeeDao () .getAttendeesByEmail ("[email protected]");

Slide 28

Slide 28 text

Migration

Slide 29

Slide 29 text

defines the actions that should be performed when migrating from one specific version to another Migration 1 2 3

Slide 30

Slide 30 text

Scenarios and actions ● Modify schema, no change in version - IllegalStateException

Slide 31

Slide 31 text

Scenarios and actions ● Modify schema, no change in version - IllegalStateException ● Increase version, no provision for migration - IllegalStateException

Slide 32

Slide 32 text

Scenarios and actions ● Modify schema, no change in version - IllegalStateException ● Increase version, no provision for migration - IllegalStateException ● Increase version, enable fallback to destructive migration - wipes data

Slide 33

Slide 33 text

Scenarios and actions ● Modify schema, no change in version - IllegalStateException ● Increase version, no provision for migration - IllegalStateException ● Increase version, enable fallback to destructive migration - wipes data ● Increase version, provide migration - preserves data

Slide 34

Slide 34 text

Fallback to destructive migration? database = Room.databaseBuilder (this, AppDatabase.class, "database_name") .fallbackToDestructiveMigration () .build (); Wipe the data. I don’t care

Slide 35

Slide 35 text

Modify Schema, Provide Migration @Database(entities = {Attendee.class}, version = 2) public abstract class AppDatabase extends RoomDatabase { public abstract AttendeeDao attendeeDao (); } Change version number

Slide 36

Slide 36 text

Modify Schema, Provide Migration public static final Migration MIGRATION_1_2 = new Migration (1, 2) { @Override public void migrate (@NonNull SupportSQLiteDatabase database) { database.execSQL ("ALTER TABLE attendee ADD COLUMN phone_number TEXT"); } }; ------------------------------------------------------------------------- database = Room.databaseBuilder (this, AppDatabase.class, "devfestsw-java.db") .addMigrations (AppDatabase.MIGRATION_1_2) .build ();

Slide 37

Slide 37 text

Testing

Slide 38

Slide 38 text

Testing DAOs - Initialize DB @Before public void initDb () { database = Room.inMemoryDatabaseBuilder (InstrumentationRegistry.getContext (), AppDatabase.class).build (); }

Slide 39

Slide 39 text

Testing DAOs - Insert new record @Test public void insert_retrieveAll_hasCorrectSize () throws Exception { database.attendeeDao ().insert (ATTENDEE); }

Slide 40

Slide 40 text

Testing DAOs - Retrieve all records @Test public void insert_retrieveAll_hasCorrectSize () throws Exception { database.attendeeDao ().insert (ATTENDEE); List attendees = database.attendeeDao ().getAllAttendees (); }

Slide 41

Slide 41 text

Testing DAOs - Verify records @Test public void insert_retrieveAll_hasCorrectSize () throws Exception { database.attendeeDao ().insert (ATTENDEE); List attendees = database.attendeeDao ().getAllAttendees (); assertEquals (1, attendees.size ()); assertEquals (ATTENDEE.getName (), attendees.get (0).getName ()); }

Slide 42

Slide 42 text

Testing DAOs - Close DB @After public void closeDb () { database.close (); }

Slide 43

Slide 43 text

Testing Migrations - Define Schema Location defaultConfig { ... javaCompileOptions { annotationProcessorOptions { arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] } } }

Slide 44

Slide 44 text

Testing Migrations - Add schema location to source sets android { ... sourceSets { androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) } }

Slide 45

Slide 45 text

Testing Migrations - Update build.gradle file dependencies { ... androidTestImplementation "android.arch.persistence.room:testing:1.0.0" }

Slide 46

Slide 46 text

Testing Migrations - Create test rule @Rule public MigrationTestHelper mMigrationTestHelper = new MigrationTestHelper (InstrumentationRegistry.getInstrumentation (), AppDatabase.class.getCanonicalName (), new FrameworkSQLiteOpenHelperFactory ());

Slide 47

Slide 47 text

Testing Migrations - Migrate and validate @Test public void migrationFrom1To2_containsCorrectData () throws IOException { SupportSQLiteDatabase db = mMigrationTestHelper.createDatabase (DB_NAME, 1); db.execSQL ("INSERT INTO attendee VALUES(" + ATTENDEE.getId () + ",'" + ATTENDEE.getName () + "','" + ATTENDEE.getEmail () + "','" + ATTENDEE.getCategory () + "')"); db.close (); mMigrationTestHelper.runMigrationsAndValidate (DB_NAME, 2, validateDroppedTables , MIGRATION_1_2); ... }

Slide 48

Slide 48 text

Testing Migrations - Migrate and validate @Test public void migrationFrom1To2_containsCorrectData () throws IOException { ... Attendee attendee = getMigratedRoomDatabase ().attendeeDao (). getAttendedByEmail (ATTENDEE.getEmail ()); assertEquals (ATTENDEE.getId (), attendee.getId ()); assertEquals (ATTENDEE.getName (), attendee.getName ()); assertEquals (attendee.getPhoneNumber (), null); }

Slide 49

Slide 49 text

Show Me The Code I have provided a repo that demonstrates all that have been said. It has two branches: room-kotlin and room-java Check it out: https://github.com/larikraun/RoomDevfestSW17

Slide 50

Slide 50 text

References https://developer.android.com/training/data-storage/room/index.html https://codelabs.developers.google.com/codelabs/android-persistence/index.ht ml#0 https://medium.com/google-developers/7-pro-tips-for-room-fbadea4bfbd1

Slide 51

Slide 51 text

Omolara Adejuwon (@_larikraun) Android, iOS @ LawPavilion THANK YOU #DevFest17 #DevFestSW