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

Get a Room, or have a Delight

Get a Room, or have a Delight

These are the slides for a presentation I gave at Droidcon Krakow 2017 and DevFest Hamburg 2017. It compares two persistence libraries for Android, Google's Room and Square's SqlDelight.

Serj Lotutovici

December 04, 2017
Tweet

More Decks by Serj Lotutovici

Other Decks in Programming

Transcript

  1. @SerjLtt Why? • Object mapping libraries come with different caveats:

    • The db is a black box • Less flexible than SQL
  2. @SerjLtt Why? • Object mapping libraries come with different caveats:

    • The db is a black box • Less flexible than SQL • Reflection*
  3. @SerjLtt Why? • Object mapping libraries come with different caveats:

    • The db is a black box • Less flexible than SQL • Reflection* • Large runtime component
  4. @SerjLtt Why? • Object mapping libraries come with different caveats:

    • The db is a black box • Less flexible than SQL • Reflection* • Large runtime component • Both Room and SqlDelight rely on code generation
  5. @SerjLtt Why? • Object mapping libraries come with different caveats:

    • The db is a black box • Less flexible than SQL • Reflection* • Large runtime component • Both Room and SqlDelight rely on code generation • Both Room and SqlDelight provide a healthy level of type safety.
  6. @SerjLtt Why? • Object mapping libraries come with different caveats:

    • The db is a black box • Less flexible than SQL • Reflection* • Large runtime component • Both Room and SqlDelight rely on code generation • Both Room and SqlDelight provide a healthy level of type safety. • Power of SQL without the boilerplate*
  7. @SerjLtt [ { "name": "abs.io", "description": "Simple URL shortener for

    ActionBarSherlock using node.js and express.", "owner": { "login": "JakeWharton", "avatar_url": "https://avatars0.githubusercontent.com/u/66577?v=4" } }, { "name": "AutoValueAnnotations", "description": "A standalone packaging of the annotations from Google's AutoValue library.", "owner": { "login": "JakeWharton", "avatar_url": "https://avatars0.githubusercontent.com/u/66577?v=4" } }, { "name": "butterknife", "description": "Bind Android views and callbacks to fields and methods.", "owner": { "login": "JakeWharton", "avatar_url": "https://avatars0.githubusercontent.com/u/66577?v=4" } } ]
  8. @SerjLtt [ { "name": "abs.io", "description": "Simple URL shortener for

    ActionBarSherlock using node.js and express.", "owner": { "login": "JakeWharton", "avatar_url": "https://avatars0.githubusercontent.com/u/66577?v=4" } }, { "name": "AutoValueAnnotations", "description": "A standalone packaging of the annotations from Google's AutoValue library.", "owner": { "login": "JakeWharton", "avatar_url": "https://avatars0.githubusercontent.com/u/66577?v=4" } }, { "name": "butterknife", "description": "Bind Android views and callbacks to fields and methods.", "owner": { "login": "JakeWharton", "avatar_url": "https://avatars0.githubusercontent.com/u/66577?v=4" } } ]
  9. @SerjLtt [ { "name": "abs.io", "description": "Simple URL shortener for

    ActionBarSherlock using node.js and express.", "owner": { "login": "JakeWharton", "avatar_url": "https://avatars0.githubusercontent.com/u/66577?v=4" } }, { "name": "AutoValueAnnotations", "description": "A standalone packaging of the annotations from Google's AutoValue library.", "owner": { "login": "JakeWharton", "avatar_url": "https://avatars0.githubusercontent.com/u/66577?v=4" } }, { "name": "butterknife", "description": "Bind Android views and callbacks to fields and methods.", "owner": { "login": "JakeWharton", "avatar_url": "https://avatars0.githubusercontent.com/u/66577?v=4" } } ]
  10. @SerjLtt @AutoValue public abstract class Repository { public static Repository

    create(@NonNull String name, @Nullable String description, @NonNull User user) { return new AutoValue_Repository(name, user, description); } @NonNull public abstract String name(); @NonNull public abstract User owner(); @Nullable public abstract String description(); }
  11. @SerjLtt @AutoValue public abstract class Repository { public static Repository

    create(@NonNull String name, @Nullable String description, @NonNull User user) { return new AutoValue_Repository(name, user, description); } @NonNull public abstract String name(); @NonNull public abstract User owner(); @Nullable public abstract String description(); } @AutoValue public abstract class User { public static User create(@NonNull String login, @Nullable String avatarUrl) { return new AutoValue_User(login, avatarUrl); } @NonNull public abstract String login(); @Nullable public abstract String avatarUrl(); }
  12. @SerjLtt @AutoValue public abstract class Repository { public static Repository

    create(@NonNull String name, @Nullable String description, @NonNull User user) { return new AutoValue_Repository(name, user, description); } @NonNull public abstract String name(); @NonNull public abstract User owner(); @Nullable public abstract String description(); } @AutoValue public abstract class User { public static User create(@NonNull String login, @Nullable String avatarUrl) { return new AutoValue_User(login, avatarUrl); } @NonNull public abstract String login(); @Nullable public abstract String avatarUrl(); }
  13. @SerjLtt Room public final class RoomRepository { @NonNull public final

    String name; @Nullable public final String description; @NonNull public final RoomUser owner; public RoomRepository(@NonNull String name, @Nullable String description, @NonNull RoomUser owner) { this.name = name; this.description = description; this.owner = owner; } }
  14. @SerjLtt Room @Entity(tableName = "repository") public final class RoomRepository {

    @NonNull public final String name; @Nullable public final String description; @NonNull public final RoomUser owner; public RoomRepository(@NonNull String name, @Nullable String description, @NonNull RoomUser owner) { this.name = name; this.description = description; this.owner = owner; } }
  15. @SerjLtt Room @Entity(tableName = "repository") public final class RoomRepository {

    @PrimaryKey @NonNull public final String name; @Nullable public final String description; @NonNull public final RoomUser owner; public RoomRepository(@NonNull String name, @Nullable String description, @NonNull RoomUser owner) { this.name = name; this.description = description; this.owner = owner; } }
  16. @SerjLtt Room @Entity(tableName = "repository") public final class RoomRepository {

    @PrimaryKey @NonNull public final String name; @Nullable public final String description; @NonNull public final RoomUser owner; public RoomRepository(@NonNull String name, @Nullable String description, @NonNull RoomUser owner) { this.name = name; this.description = description; this.owner = owner; } }
  17. @SerjLtt Room @Entity(tableName = "repository") public final class RoomRepository {

    @PrimaryKey @NonNull public final String name; @Nullable public final String description; @NonNull public final RoomUser owner; public RoomRepository(@NonNull String name, @Nullable String description, @NonNull RoomUser owner) { this.name = name; this.description = description; this.owner = owner; } }
  18. @SerjLtt Room @Entity(tableName = "repository") public final class RoomRepository {

    @PrimaryKey @NonNull public final String name; @Nullable public final String description; @NonNull public final RoomUser owner; public RoomRepository(@NonNull String name, @Nullable String description, @NonNull RoomUser owner) { this.name = name; this.description = description; this.owner = owner; } }
  19. @SerjLtt Room @Entity(tableName = "repository") public final class RoomRepository {

    @PrimaryKey @NonNull public final String name; @Nullable public final String description; @Embedded @NonNull public final RoomUser owner; public RoomRepository(@NonNull String name, @Nullable String description, @NonNull RoomUser owner) { this.name = name; this.description = description; this.owner = owner; } }
  20. @SerjLtt Room @Entity(tableName = "repository") public final class RoomRepository {

    @PrimaryKey @NonNull public final String name; @Nullable public final String description; @Embedded @NonNull public final RoomUser owner; public RoomRepository(@NonNull String name, @Nullable String description, @NonNull RoomUser owner) { this.name = name; this.description = description; this.owner = owner; } }
  21. @SerjLtt Room @Entity(tableName = "repository") public final class RoomRepository {

    @PrimaryKey @NonNull public final String name; @Nullable public final String description; @Embedded @NonNull public final RoomUser owner; public RoomRepository(@NonNull String name, @Nullable String description, @NonNull RoomUser owner) { this.name = name; this.description = description; this.owner = owner; } } public final class RoomUser { @NonNull public final String login; @Nullable public final String avatarUrl; public RoomUser(@NonNull String login, @Nullable String avatarUrl) { this.login = login; this.avatarUrl = avatarUrl; } }
  22. @SerjLtt Room @Entity(tableName = "repository") public final class RoomRepository {

    @PrimaryKey @NonNull public final String name; @Nullable public final String description; @Embedded @NonNull public final RoomUser owner; public RoomRepository(@NonNull String name, @Nullable String description, @NonNull RoomUser owner) { this.name = name; this.description = description; this.owner = owner; } } public final class RoomUser { @NonNull public final String login; @Nullable public final String avatarUrl; public RoomUser(@NonNull String login, @Nullable String avatarUrl) { this.login = login; this.avatarUrl = avatarUrl; } }
  23. @SerjLtt Room @Dao public interface RoomService { @Query("SELECT * FROM

    repository”) Single<List<RoomRepository>> repositories(); }
  24. @SerjLtt Room @Dao public interface RoomService { @Query("SELECT * FROM

    repository") Single<List<RoomRepository>> repositories(); } public abstract class MyDatabase extends RoomDatabase { public abstract RoomService service(); }
  25. @SerjLtt Room @Dao public interface RoomService { @Query("SELECT * FROM

    repository”) Single<List<RoomRepository>> repositories(); } @Database( entities = { RoomRepository.class}, version = 1 ) public abstract class MyDatabase extends RoomDatabase { public abstract RoomService service(); }
  26. @SerjLtt Room @Dao public interface RoomService { @Query("SELECT * FROM

    repository”) Single<List<RoomRepository>> repositories(); } @Database( entities = { RoomRepository.class}, version = 1 ) public abstract class MyDatabase extends RoomDatabase { /** Creates a production instance of MyDatabase. */ public static MyDatabase create(Context context) { return Room.databaseBuilder(context.getApplicationContext(), MyDatabase.class, "local.db") .build(); } public abstract RoomService service(); }
  27. @SerjLtt Room @Dao public interface RoomService { @Query("SELECT * FROM

    repository”) Single<List<RoomRepository>> repositories(); } @Database( entities = { RoomRepository.class}, version = 1 ) public abstract class MyDatabase extends RoomDatabase { /** Creates a production instance of MyDatabase. */ public static MyDatabase create(Context context) { return Room.databaseBuilder(context.getApplicationContext(), MyDatabase.class, "local.db") .build(); } /** Creates a test instance of MyDatabase. */ public static MyDatabase createForTests(Context context) { return Room.inMemoryDatabaseBuilder(context, MyDatabase.class) .allowMainThreadQueries() .build(); } public abstract RoomService service(); }
  28. @SerjLtt Room @Database( entities = { RoomRepository.class}, version = 1

    ) public abstract class MyDatabase extends RoomDatabase { /** Creates a production instance of MyDatabase. */ public static MyDatabase create(Context context) { return Room.databaseBuilder(context.getApplicationContext(), MyDatabase.class, "local.db") .build(); } /** Creates a test instance of MyDatabase. */ public static MyDatabase createForTests(Context context) { return Room.inMemoryDatabaseBuilder(context, MyDatabase.class) .allowMainThreadQueries() .build(); } public abstract RoomService service(); }
  29. @SerjLtt Room @Database( entities = { RoomRepository.class}, version = 1

    ) public abstract class MyDatabase extends RoomDatabase { /** Creates a production instance of MyDatabase. */ public static MyDatabase create(Context context) { return Room.databaseBuilder(context.getApplicationContext(), MyDatabase.class, “local.db") .build(); } /** Creates a test instance of MyDatabase. */ public static MyDatabase createForTests(Context context) { return Room.inMemoryDatabaseBuilder(context, MyDatabase.class) .allowMainThreadQueries() .build(); } public abstract RoomService service(); }
  30. @SerjLtt Room @Database( entities = { RoomRepository.class}, version = 1

    ) public abstract class MyDatabase extends RoomDatabase { /** Creates a production instance of MyDatabase. */ public static MyDatabase create(Context context) { return Room.databaseBuilder(context.getApplicationContext(), MyDatabase.class, "local.db") .addMigrations(new Migration(1, 2) { @Override public void migrate(@NonNull SupportSQLiteDatabase database) { database.execSQL("ALTER TABLE repository\n" + " ADD COLUMN isFork INTEGER DEFAULT 0;"); } }) .build(); } /** Creates a test instance of MyDatabase. */ public static MyDatabase createForTests(Context context) { return Room.inMemoryDatabaseBuilder(context, MyDatabase.class) .allowMainThreadQueries() .build(); } public abstract RoomService service(); }
  31. @SerjLtt Room @Database( entities = { RoomRepository.class}, version = 1

    ) public abstract class MyDatabase extends RoomDatabase { /** Creates a production instance of MyDatabase. */ public static MyDatabase create(Context context) { return Room.databaseBuilder(context.getApplicationContext(), MyDatabase.class, "local.db") .addMigrations(new Migration(1, 2) { @Override public void migrate(@NonNull SupportSQLiteDatabase database) { database.execSQL("ALTER TABLE repository\n" + " ADD COLUMN isFork INTEGER DEFAULT 0;"); } }) .build(); } /** Creates a test instance of MyDatabase. */ public static MyDatabase createForTests(Context context) { return Room.inMemoryDatabaseBuilder(context, MyDatabase.class) .allowMainThreadQueries() .build(); } public abstract RoomService service(); }
  32. @SerjLtt Room @Database( entities = { RoomRepository.class}, version = 1

    ) public abstract class MyDatabase extends RoomDatabase { /** Creates a production instance of MyDatabase. */ public static MyDatabase create(Context context) { return Room.databaseBuilder(context.getApplicationContext(), MyDatabase.class, "local.db") .addMigrations(MIGRATION_1_2) .build(); } /** Creates a test instance of MyDatabase. */ public static MyDatabase createForTests(Context context) { return Room.inMemoryDatabaseBuilder(context, MyDatabase.class) .allowMainThreadQueries() .build(); } public abstract RoomService service(); }
  33. @SerjLtt Room @Database( entities = { RoomRepository.class}, version = 1

    ) public abstract class MyDatabase extends RoomDatabase { /** Creates a production instance of MyDatabase. */ public static MyDatabase create(Context context) { return Room.databaseBuilder(context.getApplicationContext(), MyDatabase.class, "local.db") .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4) .build(); } /** Creates a test instance of MyDatabase. */ public static MyDatabase createForTests(Context context) { return Room.inMemoryDatabaseBuilder(context, MyDatabase.class) .allowMainThreadQueries() .build(); } public abstract RoomService service(); }
  34. @SerjLtt SqlDelight /* * Repository.sq */ CREATE TABLE repository (

    name TEXT NOT NULL PRIMARY KEY, description TEXT, owner TEXT NOT NULL, );
  35. @SerjLtt SqlDelight /* * Repository.sq */ CREATE TABLE repository (

    name TEXT NOT NULL PRIMARY KEY, description TEXT, owner TEXT NOT NULL, FOREIGN KEY (owner) REFERENCES user(login) );
  36. @SerjLtt SqlDelight /* * Repository.sq */ CREATE TABLE repository (

    name TEXT NOT NULL PRIMARY KEY, description TEXT, owner TEXT NOT NULL, FOREIGN KEY (owner) REFERENCES user(login) ); /* * User.sq */ CREATE TABLE user ( login TEXT NOT NULL PRIMARY KEY, avatarUrl TEXT );
  37. @SerjLtt SqlDelight /* * Repository.sq */ CREATE TABLE repository (

    name TEXT NOT NULL PRIMARY KEY, description TEXT, owner TEXT NOT NULL, FOREIGN KEY (owner) REFERENCES user(login) ); /* * User.sq */ CREATE TABLE user ( login TEXT NOT NULL PRIMARY KEY, avatarUrl TEXT ); select_user: SELECT * FROM user WHERE user.login = ?;
  38. @SerjLtt SqlDelight /* * Repository.sq */ CREATE TABLE repository (

    name TEXT NOT NULL PRIMARY KEY, description TEXT, owner TEXT NOT NULL, FOREIGN KEY (owner) REFERENCES user(login) ); select_all: SELECT * FROM repository JOIN user ON repository.owner = user.login; /* * User.sq */ CREATE TABLE user ( login TEXT NOT NULL PRIMARY KEY, avatarUrl TEXT ); select_user: SELECT * FROM user WHERE user.login = ?;
  39. @SerjLtt SqlDelight public interface UserModel { String TABLE_NAME = "user";

    String LOGIN = "login"; String AVATARURL = "avatarUrl"; String CREATE_TABLE = "" + "CREATE TABLE user (\n" + " login TEXT NOT NULL PRIMARY KEY,\n" + " avatarUrl TEXT\n" + ")"; @NonNull String login(); @Nullable String avatarUrl(); interface Creator<T extends UserModel> { T create(@NonNull String login, @Nullable String avatarUrl); } final class Mapper<T extends UserModel> implements RowMapper<T> { private final Factory<T> userModelFactory; public Mapper(Factory<T> userModelFactory) { this.userModelFactory = userModelFactory; } @Override public T map(@NonNull Cursor cursor) { return userModelFactory.creator.create( cursor.getString(0), cursor.isNull(1) ? null : cursor.getString(1) ); } } final class Marshal { protected final ContentValues contentValues = new ContentValues(); Marshal(@Nullable UserModel copy) { if (copy != null) { this.login(copy.login()); this.avatarUrl(copy.avatarUrl()); } } public ContentValues asContentValues() { return contentValues; } public Marshal login(String login) { contentValues.put("login", login); return this; } public Marshal avatarUrl(String avatarUrl) { contentValues.put("avatarUrl", avatarUrl); return this; } } final class Factory<T extends UserModel> { public final Creator<T> creator; public Factory(Creator<T> creator) { this.creator = creator; } /** * @deprecated Use compiled statements (https://github.com/square/sqldelight#compiled-statements) */ @Deprecated public Marshal marshal() { return new Marshal(null); } /** * @deprecated Use compiled statements (https://github.com/square/sqldelight#compiled-statements) */ @Deprecated public Marshal marshal(UserModel copy) { return new Marshal(copy); } public SqlDelightStatement select_user(@NonNull String login) { List<String> args = new ArrayList<String>(); int currentIndex = 1; StringBuilder query = new StringBuilder(); query.append("SELECT *\n" + "FROM user\n" + "WHERE user.login = "); query.append('?').append(currentIndex++); args.add(login); return new SqlDelightStatement(query.toString(), args.toArray(new String[args.size()]), Collections.<String>singleton("user")); } /** * @deprecated Use {@link Insert_user} */ @Deprecated public SqlDelightStatement insert_user(@NonNull String login, @Nullable String avatarUrl) { List<String> args = new ArrayList<String>(); int currentIndex = 1; StringBuilder query = new StringBuilder(); query.append("INSERT INTO user(login, avatarUrl)\n" + "VALUES("); query.append('?').append(currentIndex++); args.add(login); query.append(", "); if (avatarUrl == null) { query.append("null"); } else { query.append('?').append(currentIndex++); args.add(avatarUrl); } query.append(")"); return new SqlDelightStatement(query.toString(), args.toArray(new String[args.size()]), Collections.<String>singleton("user")); } public Mapper<T> select_userMapper() { return new Mapper<T>(this); } } final class Insert_user extends SqlDelightCompiledStatement.Insert { public Insert_user(SQLiteDatabase database) { super("user", database.compileStatement("" + "INSERT INTO user(login, avatarUrl)\n" + "VALUES(?, ?)")); } public interface RepositoryModel { String TABLE_NAME = "repository"; String NAME = "name"; String DESCRIPTION = "description"; String OWNER = "owner"; String CREATE_TABLE = "" + "CREATE TABLE repository (\n" + " name TEXT NOT NULL PRIMARY KEY,\n" + " description TEXT,\n" + " owner TEXT NOT NULL,\n" + " FOREIGN KEY (owner) REFERENCES user(login)\n" + ")"; @NonNull String name(); @Nullable String description(); @NonNull String owner(); interface Select_allModel<T1 extends RepositoryModel, T2 extends UserModel> { @NonNull T1 repository(); @NonNull T2 user(); } interface Select_allCreator<T1 extends RepositoryModel, T2 extends UserModel, T extends Select_allModel<T1, T2>> { T create(@NonNull T1 repository, @NonNull T2 user); } final class Select_allMapper<T1 extends RepositoryModel, T2 extends UserModel, T extends Select_allModel<T1, T2>> implements RowMapper<T> { private final Select_allCreator<T1, T2, T> creator; private final Factory<T1> repositoryModelFactory; private final UserModel.Factory<T2> userModelFactory; public Select_allMapper(Select_allCreator<T1, T2, T> creator, Factory<T1> repositoryModelFactory, UserModel.Factory<T2> userModelFactory) { this.creator = creator; this.repositoryModelFactory = repositoryModelFactory; this.userModelFactory = userModelFactory; } @Override @NonNull public T map(@NonNull Cursor cursor) { return creator.create( repositoryModelFactory.creator.create( cursor.getString(0), cursor.isNull(1) ? null : cursor.getString(1), cursor.getString(2) ), userModelFactory.creator.create( cursor.getString(3), cursor.isNull(4) ? null : cursor.getString(4) ) ); } } interface Creator<T extends RepositoryModel> { T create(@NonNull String name, @Nullable String description, @NonNull String owner); } final class Mapper<T extends RepositoryModel> implements RowMapper<T> { private final Factory<T> repositoryModelFactory; public Mapper(Factory<T> repositoryModelFactory) { this.repositoryModelFactory = repositoryModelFactory; } @Override public T map(@NonNull Cursor cursor) { return repositoryModelFactory.creator.create( cursor.getString(0), cursor.isNull(1) ? null : cursor.getString(1), cursor.getString(2) ); } } final class Marshal { protected final ContentValues contentValues = new ContentValues(); Marshal(@Nullable RepositoryModel copy) { if (copy != null) { this.name(copy.name()); this.description(copy.description()); this.owner(copy.owner()); } } public ContentValues asContentValues() { return contentValues; } public Marshal name(String name) { contentValues.put("name", name); return this; } public Marshal description(String description) { contentValues.put("description", description); return this; } public Marshal owner(String owner) { contentValues.put("owner", owner); return this; } } final class Factory<T extends RepositoryModel> { final class Factory<T extends RepositoryModel> { public final Creator<T> creator; public Factory(Creator<T> creator) { this.creator = creator; } /** * @deprecated Use compiled statements (https://github.com/square/sqldelight#compiled-statements) */ @Deprecated public Marshal marshal() { return new Marshal(null); } /** * @deprecated Use compiled statements (https://github.com/square/sqldelight#compiled-statements) */ @Deprecated public Marshal marshal(RepositoryModel copy) { return new Marshal(copy); } public SqlDelightStatement select_all() { return new SqlDelightStatement("" + "SELECT *\n" + "FROM repository\n" + "JOIN user ON repository.owner = user.login", new String[0], Collections.<String>unmodifiableSet(new LinkedHashSet<String>(Arrays.asList("repository","user")))); } /** * @deprecated Use {@link Insert_reporitory} */ @Deprecated public SqlDelightStatement insert_reporitory(@NonNull String name, @Nullable String description, @NonNull String owner) { List<String> args = new ArrayList<String>(); int currentIndex = 1; StringBuilder query = new StringBuilder(); query.append("INSERT INTO repository(name, description, owner)\n" + "VALUES("); query.append('?').append(currentIndex++); args.add(name); query.append(", "); if (description == null) { query.append("null"); } else { query.append('?').append(currentIndex++); args.add(description); } query.append(", "); query.append('?').append(currentIndex++); args.add(owner); query.append(")"); return new SqlDelightStatement(query.toString(), args.toArray(new String[args.size()]), Collections.<String>singleton("repository")); } public <T2 extends UserModel, R extends Select_allModel<T, T2>> Select_allMapper<T, T2, R> select_allMapper(Select_allCreator<T, T2, R> creator, UserModel.Factory<T2> userModelFactory) { return new Select_allMapper<T, T2, R>(creator, this, userModelFactory); } } final class Insert_reporitory extends SqlDelightCompiledStatement.Insert { public Insert_reporitory(SQLiteDatabase database) { super("repository", database.compileStatement("" + "INSERT INTO repository(name, description, owner)\n" + "VALUES(?, ?, ?)")); } public void bind(@NonNull String name, @Nullable String description, @NonNull String owner) { program.bindString(1, name); if (description == null) { program.bindNull(2); } else { program.bindString(2, description); } program.bindString(3, owner); } } }
  40. @SerjLtt SqlDelight @AutoValue public abstract class SqlDelightRepository implements RepositoryModel {

    } @AutoValue public abstract class SqlDelightUser implements UserModel { }
  41. @SerjLtt SqlDelight @AutoValue public abstract class SqlDelightRepository implements RepositoryModel {

    public static final Factory<SqlDelightRepository> FACTORY = new Factory<>(new Creator<SqlDelightRepository>() { @Override public SqlDelightRepository create(@NonNull String name, @Nullable String description, @NonNull String owner) { return new AutoValue_SqlDelightRepository(name, description, owner); } }); public static final Mapper<SqlDelightRepository> MAPPER = new Mapper<>(FACTORY); } @AutoValue public abstract class SqlDelightUser implements UserModel { }
  42. @SerjLtt SqlDelight @AutoValue public abstract class SqlDelightRepository implements RepositoryModel {

    public static final Factory<SqlDelightRepository> FACTORY = new Factory<>(new Creator<SqlDelightRepository>() { @Override public SqlDelightRepository create(@NonNull String name, @Nullable String description, @NonNull String owner) { return new AutoValue_SqlDelightRepository(name, description, owner); } }); public static final Mapper<SqlDelightRepository> MAPPER = new Mapper<>(FACTORY); } @AutoValue public abstract class SqlDelightUser implements UserModel { public static final Factory<SqlDelightUser> FACTORY = new Factory<>(new Creator<SqlDelightUser>() { @Override public SqlDelightUser create(@NonNull String login, @Nullable String avatarUrl) { return new AutoValue_SqlDelightUser(login, avatarUrl); } }); public static final Mapper<SqlDelightUser> MAPPER = new Mapper<>(FACTORY); }
  43. @SerjLtt SqlDelight public class SqlDelightOpenHelper extends SQLiteOpenHelper { @Override public

    void onCreate(SQLiteDatabase db) { } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
  44. @SerjLtt SqlDelight public class SqlDelightOpenHelper extends SQLiteOpenHelper { @Override public

    void onCreate(SQLiteDatabase db) { db.execSQL(SqlDelightUser.CREATE_TABLE); db.execSQL(SqlDelightRepository.CREATE_TABLE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
  45. @SerjLtt SqlDelight public class SqlDelightService { Observable<List<SqlDelightRepository>> repositories() { SqlBrite

    sqlBrite = new SqlBrite.Builder().build(); BriteDatabase briteDatabase = sqlBrite.wrapDatabaseHelper(sqlDelightOpenHelper, Schedulers.io()); } }
  46. @SerjLtt SqlDelight public class SqlDelightService { Observable<List<SqlDelightRepository>> repositories() { SqlBrite

    sqlBrite = new SqlBrite.Builder().build(); BriteDatabase briteDatabase = sqlBrite.wrapDatabaseHelper(sqlDelightOpenHelper, Schedulers.io()); SqlDelightStatement statement = SqlDelightRepository.FACTORY.select_all(); } }
  47. @SerjLtt SqlDelight public class SqlDelightService { Observable<List<SqlDelightRepository>> repositories() { SqlBrite

    sqlBrite = new SqlBrite.Builder().build(); BriteDatabase briteDatabase = sqlBrite.wrapDatabaseHelper(sqlDelightOpenHelper, Schedulers.io()); SqlDelightStatement statement = SqlDelightRepository.FACTORY.select_all(); } }
  48. @SerjLtt SqlDelight /* * Repository.sq */ CREATE TABLE repository (

    name TEXT NOT NULL PRIMARY KEY, description TEXT, owner TEXT NOT NULL, FOREIGN KEY (owner) REFERENCES user(login) ); select_all: SELECT * FROM repository JOIN user ON repository.owner = user.login; /* * User.sq */ CREATE TABLE user ( login TEXT NOT NULL PRIMARY KEY, avatarUrl TEXT ); select_user: SELECT * FROM user WHERE user.login = ?;
  49. @SerjLtt SqlDelight /* * Repository.sq */ CREATE TABLE repository (

    name TEXT NOT NULL PRIMARY KEY, description TEXT, owner TEXT NOT NULL, FOREIGN KEY (owner) REFERENCES user(login) ); select_all: SELECT * FROM repository JOIN user ON repository.owner = user.login; /* * User.sq */ CREATE TABLE user ( login TEXT NOT NULL PRIMARY KEY, avatarUrl TEXT ); select_user: SELECT * FROM user WHERE user.login = ?;
  50. @SerjLtt SqlDelight public class SqlDelightService { Observable<List<SqlDelightRepository>> repositories() { SqlBrite

    sqlBrite = new SqlBrite.Builder().build(); BriteDatabase briteDatabase = sqlBrite.wrapDatabaseHelper(sqlDelightOpenHelper, Schedulers.io()); SqlDelightStatement statement = SqlDelightRepository.FACTORY.select_all(); } }
  51. @SerjLtt SqlDelight public class SqlDelightService { Observable<List<SqlDelightRepository>> repositories() { SqlBrite

    sqlBrite = new SqlBrite.Builder().build(); BriteDatabase briteDatabase = sqlBrite.wrapDatabaseHelper(sqlDelightOpenHelper, Schedulers.io()); SqlDelightStatement statement = SqlDelightRepository.FACTORY.select_all(); return briteDatabase.createQuery(statement.tables, statement.statement) .mapToList(new Function<Cursor, SqlDelightRepository>() { @Override public SqlDelightRepository apply(Cursor cursor) throws Exception { return SqlDelightRepository.MAPPER.map(cursor); } }); } }
  52. @SerjLtt SqlDelight public class SqlDelightService { Observable<List<SqlDelightRepository>> repositories() { SqlBrite

    sqlBrite = new SqlBrite.Builder().build(); BriteDatabase briteDatabase = sqlBrite.wrapDatabaseHelper(sqlDelightOpenHelper, Schedulers.io()); SqlDelightStatement statement = SqlDelightRepository.FACTORY.select_all(); return briteDatabase.createQuery(statement.tables, statement.statement) .mapToList(new Function<Cursor, SqlDelightRepository>() { @Override public SqlDelightRepository apply(Cursor cursor) throws Exception { return SqlDelightRepository.MAPPER.map(cursor); } }); } }
  53. @SerjLtt SqlDelight public class SqlDelightService { Observable<List<SqlDelightRepository>> repositories() { SqlBrite

    sqlBrite = new SqlBrite.Builder().build(); BriteDatabase briteDatabase = sqlBrite.wrapDatabaseHelper(sqlDelightOpenHelper, Schedulers.io()); SqlDelightStatement statement = SqlDelightRepository.FACTORY.select_all(); return briteDatabase.createQuery(statement.tables, statement.statement) .mapToList(new Function<Cursor, SqlDelightRepository>() { @Override public SqlDelightRepository apply(Cursor cursor) throws Exception { return SqlDelightRepository.MAPPER.map(cursor); } }); } }
  54. @SerjLtt SqlDelight public class SqlDelightService { Observable<List<SqlDelightRepository>> repositories() { SqlBrite

    sqlBrite = new SqlBrite.Builder().build(); BriteDatabase briteDatabase = sqlBrite.wrapDatabaseHelper(sqlDelightOpenHelper, Schedulers.io()); SqlDelightStatement statement = SqlDelightRepository.FACTORY.select_all(); return briteDatabase.createQuery(statement.tables, statement.statement) .mapToList(new Function<Cursor, SqlDelightRepository>() { @Override public SqlDelightRepository apply(Cursor cursor) throws Exception { return SqlDelightRepository.MAPPER.map(cursor); } }); } }
  55. @SerjLtt SqlDelight @AutoValue public abstract class SqlDelightRepository implements RepositoryModel {

    public static final Factory<SqlDelightRepository> FACTORY = new Factory<>(new Creator<SqlDelightRepository>() { @Override public SqlDelightRepository create(@NonNull String name, @Nullable String description, @NonNull String owner) { return new AutoValue_SqlDelightRepository(name, description, owner); } }); public static final Mapper<SqlDelightRepository> MAPPER = new Mapper<>(FACTORY); } @AutoValue public abstract class SqlDelightUser implements UserModel { public static final Factory<SqlDelightUser> FACTORY = new Factory<>(new Creator<SqlDelightUser>() { @Override public SqlDelightUser create(@NonNull String login, @Nullable String avatarUrl) { return new AutoValue_SqlDelightUser(login, avatarUrl); } }); public static final Mapper<SqlDelightUser> MAPPER = new Mapper<>(FACTORY); }
  56. @SerjLtt SqlDelight public class SqlDelightService { Observable<List<SqlDelightRepository>> repositories() { SqlBrite

    sqlBrite = new SqlBrite.Builder().build(); BriteDatabase briteDatabase = sqlBrite.wrapDatabaseHelper(sqlDelightOpenHelper, Schedulers.io()); SqlDelightStatement statement = SqlDelightRepository.FACTORY.select_all(); return briteDatabase.createQuery(statement.tables, statement.statement) .mapToList(new Function<Cursor, SqlDelightRepository>() { @Override public SqlDelightRepository apply(Cursor cursor) throws Exception { return SqlDelightRepository.MAPPER.map(cursor); } }); } }
  57. @SerjLtt In a nutshell Room ✓Easy & simple setup ✓Very

    well documented (+100500 blog posts)
  58. @SerjLtt In a nutshell Room ✓Easy & simple setup ✓Very

    well documented (+100500 blog posts) ✓Easy migrations
  59. @SerjLtt In a nutshell Room ✓Easy & simple setup ✓Very

    well documented (+100500 blog posts) ✓Easy migrations ✓Supports basic SQL CRUD
  60. @SerjLtt In a nutshell Room ✓Easy & simple setup ✓Very

    well documented (+100500 blog posts) ✓Easy migrations ✓Supports basic SQL CRUD ✓Works with Kotlin :)
  61. @SerjLtt In a nutshell Room ✓Easy & simple setup ✓Very

    well documented (+100500 blog posts) ✓Easy migrations ✓Supports basic SQL CRUD ✓Works with Kotlin :) SqlDelight
  62. @SerjLtt In a nutshell Room ✓Easy & simple setup ✓Very

    well documented (+100500 blog posts) ✓Easy migrations ✓Supports basic SQL CRUD ✓Works with Kotlin :) SqlDelight
  63. @SerjLtt In a nutshell Room ✓Easy & simple setup ✓Very

    well documented (+100500 blog posts) ✓Easy migrations ✓Supports basic SQL CRUD ✓Works with Kotlin :) SqlDelight • Cumbersome setup
  64. @SerjLtt In a nutshell Room ✓Easy & simple setup ✓Very

    well documented (+100500 blog posts) ✓Easy migrations ✓Supports basic SQL CRUD ✓Works with Kotlin :) SqlDelight • Cumbersome setup • Still in development
  65. @SerjLtt In a nutshell Room ✓Easy & simple setup ✓Very

    well documented (+100500 blog posts) ✓Easy migrations ✓Supports basic SQL CRUD ✓Works with Kotlin :) SqlDelight • Cumbersome setup • Still in development • Doesn’t simplify migrations
  66. @SerjLtt In a nutshell Room ✓Easy & simple setup ✓Very

    well documented (+100500 blog posts) ✓Easy migrations ✓Supports basic SQL CRUD ✓Works with Kotlin :) SqlDelight • Cumbersome setup • Still in development • Doesn’t simplify migrations • Can be used only with AutoValue*
  67. @SerjLtt In a nutshell, but Room • Doesn’t support VIEWS,

    INDEXES, TRIGGERS • Doesn’t work with AutoValue
  68. @SerjLtt In a nutshell, but Room • Doesn’t support VIEWS,

    INDEXES, TRIGGERS • Doesn’t work with AutoValue • Tooling is very limited.
  69. @SerjLtt In a nutshell, but Room • Doesn’t support VIEWS,

    INDEXES, TRIGGERS • Doesn’t work with AutoValue • Tooling is very limited. SqlDelight
  70. @SerjLtt In a nutshell, but Room • Doesn’t support VIEWS,

    INDEXES, TRIGGERS • Doesn’t work with AutoValue • Tooling is very limited. SqlDelight
  71. @SerjLtt In a nutshell, but Room • Doesn’t support VIEWS,

    INDEXES, TRIGGERS • Doesn’t work with AutoValue • Tooling is very limited. SqlDelight • Awesome tooling
  72. @SerjLtt In a nutshell, but Room • Doesn’t support VIEWS,

    INDEXES, TRIGGERS • Doesn’t work with AutoValue • Tooling is very limited. SqlDelight • Awesome tooling • Kotlin coming in 1.0.0*
  73. @SerjLtt In a nutshell, but Room • Doesn’t support VIEWS,

    INDEXES, TRIGGERS • Doesn’t work with AutoValue • Tooling is very limited. SqlDelight • Awesome tooling • Kotlin coming in 1.0.0* • Full SQL support