首页 > 代码库 > Android SqlDelight和SqlBrite无缝结合使用的Demo例子

Android SqlDelight和SqlBrite无缝结合使用的Demo例子

Android SqlDelight详解和Demo例子: 点我
Android SqlBrite使用介绍和官方demo详解: 点我
Android AutoValue使用和扩展库: 点我

在说到SqlBrite的时候有介绍到它的官方的例子,官方的栗子是没有用到SqlDelight的。这里我在这个例子的基础上添加SqlDelight,实现SqlBrite和SqlDelight的无缝连接。

这里我偷个懒,fork了SqlBrite的源码,然后添加了Sqldelight的栗子的module,然后把Sqldelight和SqlBrite的代码都加了中文注释; 然后再写了了一个Module,在SqlBrite的sample的基础上无缝结合SqlDelight。

sqlbrite_sqldelight_sample的Github地址: https://github.com/tpnet/sqlbrite

结合前和结合后的对比

这里以sqlbrite_sqldelight_sample 和 sqlbrite_sample作为例子:

1. bean类对比

SqlBrite没有结合SqlDelight:

@AutoValue
public abstract class TodoList implements Parcelable {


    public static final String TABLE = "todo_list";

    public static final String ID = "_id";
    public static final String NAME = "name";
    public static final String ARCHIVED = "archived";

    public abstract long id();

    public abstract String name();

    public abstract boolean archived();


    //利用RXjava进行数据的获取,返回bean列表
    public static Func1<Cursor, List<TodoList>> MAP = new Func1<Cursor, List<TodoList>>() {
        @Override
        public List<TodoList> call(final Cursor cursor) {
            try {
                List<TodoList> values = new ArrayList<>(cursor.getCount());

                while (cursor.moveToNext()) {
                    long id = Db.getLong(cursor, ID);
                    String name = Db.getString(cursor, NAME);
                    boolean archived = Db.getBoolean(cursor, ARCHIVED);
                    values.add(new AutoValue_TodoList(id, name, archived));
                }
                return values;
            } finally {
                cursor.close();
            }
        }
    };


    //构建器,用来生成ContentValues
    public static final class Builder {
        private final ContentValues values = new ContentValues();

        public Builder id(long id) {
            values.put(ID, id);
            return this;
        }

        public Builder name(String name) {
            values.put(NAME, name);
            return this;
        }

        public Builder archived(boolean archived) {
            values.put(ARCHIVED, archived);
            return this;
        }

        public ContentValues build() {
            return values; // TODO defensive copy?
        }
    }
}

SqlBrite结合了SqlDelight之后:

@AutoValue
public abstract class TodoList implements TodoListModel,Parcelable {

public abstract class TodoList implements TodoListModel,Parcelable {


    public final static Factory<TodoList> FACTORY = new Factory<>(new  TodoListModel.Creator<TodoList>() {
        @Override
        public TodoList create(long _id, @NonNull String name, @Nullable String archived) {
            return new AutoValue_TodoList(_id, name, archived);

        }

    });


    @AutoValue
    public abstract static class ListsItem implements Parcelable, List_queryModel {

    }

    public static Func1<Cursor, ListsItem> LIST_ITEM_FUNC = new Func1<Cursor, ListsItem>() {
        @Override
        public ListsItem call(Cursor cursor) {
            return LIST_ITEM_MAPPER.map(cursor);
        }
    };

    //查询title映射
    public final static RowMapper<String> ROW_TITLE_MAPPER = FACTORY.title_queryMapper();


    public final static RowMapper<ListsItem> LIST_ITEM_MAPPER = FACTORY.list_queryMapper(new List_queryCreator<ListsItem>() {
        @Override
        public ListsItem create(long _id, @NonNull String name, long item_count) {
            return new AutoValue_TodoList_ListsItem(_id, name, item_count);
        }
    });

}


}

可以看到: 结合后的bean我们写的代码量少了,组合ContentValues这些事情不用我们手动做了,解析cursor不用我们手动做了,各种操作对象可以放在这里。

2. SQLiteOpenHelper类对比

结合前:

final class DbOpenHelper extends SQLiteOpenHelper {
    private static final int VERSION = 1;

    //创建todo_list表的语句
    private static final String CREATE_LIST = ""
            + "CREATE TABLE " + TodoList.TABLE + "("
            + TodoList.ID + " INTEGER NOT NULL PRIMARY KEY,"
            + TodoList.NAME + " TEXT NOT NULL,"
            + TodoList.ARCHIVED + " INTEGER NOT NULL DEFAULT 0"
            + ")";

    //创建todo_item表的语句
    private static final String CREATE_ITEM = ""
            + "CREATE TABLE " + TodoItem.TABLE + "("
            + TodoItem.ID + " INTEGER NOT NULL PRIMARY KEY,"
            + TodoItem.LIST_ID + " INTEGER NOT NULL REFERENCES " + TodoList.TABLE + "(" + TodoList.ID + "),"
            + TodoItem.DESCRIPTION + " TEXT NOT NULL,"
            + TodoItem.COMPLETE + " INTEGER NOT NULL DEFAULT 0"
            + ")";


    //创建 单列索引,加速查询
    private static final String CREATE_ITEM_LIST_ID_INDEX =
            "CREATE INDEX item_list_id ON " + TodoItem.TABLE + " (" + TodoItem.LIST_ID + ")";

    //构造器,创建数据库
    public DbOpenHelper(Context context) {
        super(context, "todo.db", null /* factory */, VERSION);
    }


    //创建数据库的时候回调
    @Override
    public void onCreate(SQLiteDatabase db) {
        //创建对应的表和索引
        db.execSQL(CREATE_LIST);
        db.execSQL(CREATE_ITEM);
        db.execSQL(CREATE_ITEM_LIST_ID_INDEX);

        //插入数据到todo_list表,返回id
        long groceryListId = db.insert(TodoList.TABLE, null, new TodoList.Builder()
                .name("Grocery List")
                .build());

        //根据todo_list表的id,插入数据到todo_item表,
        db.insert(TodoItem.TABLE, null, new TodoItem.Builder()
                .listId(groceryListId)
                .description("Beer")
                .build());
        db.insert(TodoItem.TABLE, null, new TodoItem.Builder()
                .listId(groceryListId)
                .description("Point Break on DVD")
                .build());
        db.insert(TodoItem.TABLE, null, new TodoItem.Builder()
                .listId(groceryListId)
                .description("Bad Boys 2 on DVD")
                .build());

        ....(和上面的套路一样这里省略了)

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

结合后:

final class DbOpenHelper extends SQLiteOpenHelper {
    private static final int VERSION = 1;


    //构造器,创建数据库
    public DbOpenHelper(Context context) {
        super(context, "todo.db", null /* factory */, VERSION);
    }


    //创建数据库的时候回调
    @Override
    public void onCreate(SQLiteDatabase db) {
        //创建对应的表和索引
        db.execSQL(TodoList.CREATE_TABLE);
        db.execSQL(TodoItem.CREATE_TABLE);
        db.execSQL(CREATE_ITEM_LIST_ID_INDEX);

        //初始化插入实体
        TodoList.Insert_list insertList = new TodoListModel.Insert_list(db);
        TodoItem.Insert_item insertItem = new TodoItemModel.Insert_item(db);


        //插入数据到todo_list表,返回id
        insertList.bind("Grocery List", "0");
        long groceryListId = insertList.program.executeInsert();


        //根据todo_list表的id,插入数据到todo_item
        insertItem.bind(groceryListId, "Beer", false);
        insertItem.program.executeInsert();

        insertItem.bind(groceryListId, "Point Break on DVD", false);
        insertItem.program.executeInsert();

        insertItem.bind(groceryListId, "Bad Boys 2 on DVD", false);
        insertItem.program.executeInsert();


        ...(和上面的套路一样,这里省略)


    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

可以看到: 结合后的SQLiteOpenHelper sql语句不用组合放在这里,清晰多了; 创表语句放在sq文件中,在自动生成的model,也可以得到创表的语句; 插增删改查利用bind来实现,更方便了,不用自己组合ContentValue,逻辑清晰,代码简介。

3. 查询对比

结合前:

subscription = db.createQuery(ListsItem.TABLES, ListsItem.QUERY)
                .mapToList(ListsItem.MAPPER)   //映射到ListItem的MAPPER
                .observeOn(AndroidSchedulers.mainThread()) //设置订阅者在主线程进行
                .subscribe(adapter);


static Func1<Cursor, ListsItem> MAPPER = new Func1<Cursor, ListsItem>() {
        @Override
        public ListsItem call(Cursor cursor) {
            long id = Db.getLong(cursor, TodoList.ID);
            String name = Db.getString(cursor, TodoList.NAME);
            int itemCount = Db.getInt(cursor, ITEM_COUNT);
            return new AutoValue_ListsItem(id, name, itemCount);
        }
    };

结合后:

//创建查询
SqlDelightStatement sqLiteStatement = TodoList.FACTORY.list_query();

subscription = db.createQuery(TodoList.TABLE_NAME, sqLiteStatement.statement, sqLiteStatement.args)
        .mapToList(TodoList.LIST_ITEM_FUNC)   //映射到ListItem的MAPPER
        .observeOn(AndroidSchedulers.mainThread()) //设置订阅者在主线程进行
        .subscribe(adapter);


public static Func1<Cursor, ListsItem> MAPPER = new Func1<Cursor, ListsItem>() {
        @Override
        public ListsItem call(Cursor cursor) {
            return LIST_ITEM_MAPPER.map(cursor);
        }
    };

可以看到结合后的Curor解析不用我们手动解析,对应的查询语句和参数可以在SqlDelightStatement获取。

4. 更新添加对比

结合前:

Observable.combineLatest(createClicked, RxTextView.textChanges(name),
                new Func2<String, CharSequence, String>() {
                    @Override
                    public String call(String ignored, CharSequence text) {
                        return text.toString();
                    }
                }) //
                .observeOn(Schedulers.io())
                .subscribe(new Action1<String>() {
                    @Override
                    public void call(String description) {
                        db.insert(TodoItem.TABLE,
                                new TodoItem.Builder().listId(getListId()).description(description).build());
                    }
                });

结合后:

Observable.combineLatest(createClicked, RxTextView.textChanges(name),
                new Func2<String, CharSequence, String>() {
                    @Override
                    public String call(String ignored, CharSequence text) {
                        return text.toString();
                    }
                }) //
                .observeOn(Schedulers.io())
                .subscribe(new Action1<String>() {
                    @Override
                    public void call(String description) {

                        //初始化插入对象
                        TodoItemModel.Insert_item insertItem = new TodoItemModel.Insert_item(db.getWritableDatabase());

                        //填入数据
                        insertItem.bind(getListId(), description, false);
                        //开始插入,这里会Rxjava会异步回调,进行更新列表
                        db.executeInsert(insertItem.table, insertItem.program);

                        //注意这里不要使用下面的插入代码,会导致Rxjava不能异步回调发送数据
                        //insertItem.program.executeInsert();

                    }
                });

可以看到结合后:利用了SqlDelight生成的对应的Model类进行更新插入,不需要自己编写ContentValue,更方便的插入。

当然还有其他的变化。大家可以看看源码。

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    Android SqlDelight和SqlBrite无缝结合使用的Demo例子