Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Getting started with ROOM

Getting started with ROOM

Slide to my talk at Devfest17.

Room is a persistence library. It is one of the architecture components. It is a wrapper around SQLite.

Bcf368379f7010d91cd12d2ffa3427f8?s=128

Omolara Adejuwon

November 18, 2017
Tweet

Transcript

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

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

  3. Room is a persistence library that provides an abstraction layer

    over SQLite.
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. Let’s Get Started

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

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

    annotationProcessor "android.arch.persistence.room:compiler:1.0.0" ... }
  14. 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 omolara@email.com Speaker ... ... ... ... Attendee Table
  15. 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 omolara@email.com Speaker ... ... ... ... Attendee Table
  16. 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 omolara@email.com Speaker ... ... ... ... Attendee Table
  17. 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 omolara@email.com Speaker ... ... ... ... Attendee Table
  18. 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 omolara@email.com Speaker ... ... ... ... Attendee Table
  19. Create the DAO @Dao public interface AttendeeDao { @Insert/@delete/@update void

    insert/delete/update (Attendee attendee); } Annotations for the win
  20. 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
  21. 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<Attendee> getAllAttendees (); } Return a list
  22. Create the DAO @Dao public interface AttendeeDao { @Query("Select *

    from attendeeess where email=:email") Attendee getAttendedByEmail (String email); } Compile Time Error
  23. Create the database class @Database(entities = {Attendee.class}, version = 1)

    public abstract class AppDatabase extends RoomDatabase { public abstract AttendeeDao attendeeDao (); }
  24. 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
  25. Access the DB - Insert new record Attendee attendee =

    new Attendee (1, "Omolara Adejuwon", "me@email.com", "Speaker"); RoomApp.database.attendeeDao ().insert (attendee);
  26. Access the DB - Retrieve All Attendees List<Attendee> attendees =

    RoomApp.database.attendeeDao ().getAllAttendees ();
  27. Access the DB - Retrieve Attendees By Email Attendee attendee

    = RoomApp.database.attendeeDao () .getAttendeesByEmail ("me@email.com");
  28. Migration

  29. defines the actions that should be performed when migrating from

    one specific version to another Migration 1 2 3
  30. Scenarios and actions • Modify schema, no change in version

    - IllegalStateException
  31. Scenarios and actions • Modify schema, no change in version

    - IllegalStateException • Increase version, no provision for migration - IllegalStateException
  32. 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
  33. 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
  34. Fallback to destructive migration? database = Room.databaseBuilder (this, AppDatabase.class, "database_name")

    .fallbackToDestructiveMigration () .build (); Wipe the data. I don’t care
  35. Modify Schema, Provide Migration @Database(entities = {Attendee.class}, version = 2)

    public abstract class AppDatabase extends RoomDatabase { public abstract AttendeeDao attendeeDao (); } Change version number
  36. 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 ();
  37. Testing

  38. Testing DAOs - Initialize DB @Before public void initDb ()

    { database = Room.inMemoryDatabaseBuilder (InstrumentationRegistry.getContext (), AppDatabase.class).build (); }
  39. Testing DAOs - Insert new record @Test public void insert_retrieveAll_hasCorrectSize

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

    () throws Exception { database.attendeeDao ().insert (ATTENDEE); List<Attendee> attendees = database.attendeeDao ().getAllAttendees (); }
  41. Testing DAOs - Verify records @Test public void insert_retrieveAll_hasCorrectSize ()

    throws Exception { database.attendeeDao ().insert (ATTENDEE); List<Attendee> attendees = database.attendeeDao ().getAllAttendees (); assertEquals (1, attendees.size ()); assertEquals (ATTENDEE.getName (), attendees.get (0).getName ()); }
  42. Testing DAOs - Close DB @After public void closeDb ()

    { database.close (); }
  43. Testing Migrations - Define Schema Location defaultConfig { ... javaCompileOptions

    { annotationProcessorOptions { arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] } } }
  44. Testing Migrations - Add schema location to source sets android

    { ... sourceSets { androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) } }
  45. Testing Migrations - Update build.gradle file dependencies { ... androidTestImplementation

    "android.arch.persistence.room:testing:1.0.0" }
  46. Testing Migrations - Create test rule @Rule public MigrationTestHelper mMigrationTestHelper

    = new MigrationTestHelper (InstrumentationRegistry.getInstrumentation (), AppDatabase.class.getCanonicalName (), new FrameworkSQLiteOpenHelperFactory ());
  47. 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); ... }
  48. 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); }
  49. 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
  50. 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

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

    #DevFestSW