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

Leave your room behind

Leave your room behind

SQLDelight vs Room

Our own story towards SQLite
What is a good android SQLite abstraction?
Schema generation
Statements
Migrations
RxJava, Paging libraries
Future

Avatar for Andrii Rakhimov

Andrii Rakhimov

April 06, 2019
Tweet

More Decks by Andrii Rakhimov

Other Decks in Programming

Transcript

  1. Agenda ➔ Our own story towards SQLite ➔ What is

    a good android SQLite abstraction? ➔ Schema generation ➔ Statements ➔ Migrations ➔ RxJava, Paging libraries ➔ Future
  2. Shared Preferences <?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map> <string name="countries">

    [{"id":1,"name":"Azərbaycan","code":"AZ","default_language":"az_AZ","alias":"azerbaijan","d omain":"lalafo.az","time_zone":"Asia/Baku"...}]</string> <boolean name="innertube_safety_mode_enabled" value="false" /> <long name="delayed_event_last_dispatch_time_ms" value="1551718329606" /> </map>
  3. Shared Preferences + Simple + Works well below ~200 of

    values - Slow, file will be rewritten each time - Slow, manual sorting, filtering of data - Data duplication - Threading - Scales badly
  4. SQLDelight ➔ Simple enough ➔ Embraces SQL ➔ Typesafe ➔

    Full Kotlin ➔ IDE support ➔ Multiplatform
  5. Proper SQLite abstraction ➔ Extensible, i.e. can easily swap platform

    or Android SQLite implementation ➔ Simple ➔ Testable
  6. Schema generation and types CREATE TABLE some_types ( some_long INTEGER

    NOT NULL PRIMARY KEY, -- Stored as INTEGER in db, retrieved as Long some_double REAL, -- Stored as REAL in db, retrieved as Double some_string TEXT, -- Stored as TEXT in db, retrieved as String some_blob BLOB, -- Stored as BLOB in db, retrieved as ByteArray some_int INTEGER AS Int, -- Stored as INTEGER in db, retrieved as Int some_short INTEGER AS Short, -- Stored as INTEGER in db, retrieved as Short some_float REAL AS Float -- Stored as REAL in db, retrieved as Float );
  7. Custom Column Types import ...Message; CREATE TABLE messages( _id TEXT

    NOT NULL PRIMARY KEY, message BLOB AS Message ); class SerializeAdapter<T : Any> : ColumnAdapter<T, ByteArray> { override fun decode(bytes: ByteArray) = SerializeUtils.deserialize(bytes) as T override fun encode(t: T): ByteArray = SerializeUtils.serialize(t) } val db = LalafoDb.invoke( AndroidSqliteDriver(helper), Messages.Adapter(SerializeAdapter<Message>()) )
  8. Enums Import ....SendStatus; CREATE TABLE messages( _id TEXT NOT NULL

    PRIMARY KEY, status TEXT AS SendStatus ); val db = LalafoDb.invoke( AndroidSqliteDriver(helper), Messages.Adapter(EnumColumnAdapter()) )
  9. SQLite statements getMessages: SELECT * FROM messages WHERE chat_id =

    ? ORDER BY creation_time DESC LIMIT 60 val messages = lalafoDb.messagesQueries.getMessages(chatId) val list: List<Messages> = messages.executeAsList()
  10. SQLite statements removeMessage: DELETE FROM messages WHERE _id = ?;

    lalafoDb.messagesQueries.removeMessage(messageId)
  11. Driver in real app @Provides @Singleton static SupportSQLiteOpenHelper provideSQLiteOpenHelper(App context)

    { SupportSQLiteOpenHelper.Configuration configuration = SupportSQLiteOpenHelper.Configuration.builder(context) .name(DbCallback.DB_NAME) .callback(new DbCallback(context)) .build(); SupportSQLiteOpenHelper.Factory factory = new FrameworkSQLiteOpenHelperFactory(); return factory.create(configuration); } @Provides @Singleton static LalafoDb provideDb(SupportSQLiteOpenHelper helper) { return LalafoDb.Companion.invoke( new AndroidSqliteDriver(helper), new Categories.Adapter(new SerializeAdapter<>()), ... new Messages.Adapter(new EnumColumnAdapter<>(SendStatus.values()) )...
  12. Migrations DROP TABLE IF EXISTS chats; DROP TABLE IF EXISTS

    messages; CREATE TABLE chats( thread_id TEXT NOT NULL PRIMARY KEY, updated INTEGER NOT NULL, ... ); CREATE TABLE messages( _id TEXT NOT NULL PRIMARY KEY, creation_time INTEGER NOT NULL, ... );
  13. RxJava getMessages: SELECT * FROM messages WHERE chat_id = ?

    ORDER BY creation_time DESC LIMIT 60 val messagesStream = lalafoDb.messagesQueries .getMessages(chatId) .asObservable(Schedulers.io()) .mapToList()
  14. Android Paging countMessages: SELECT count(*) FROM messages; getMessages: SELECT *

    FROM messages LIMIT :limit OFFSET :offset; val messagesQueries = lalafoDb.messagesQueries val dataSource = QueryDataSourceFactory( queryProvider = messagesQueries::getMessage, countQuery = messagesQueries.countMessages() ).create()
  15. Do not blindly take what Google propose. For us SQL-first,

    full Kotlin and multiplatform are big advantages. What about You?