android_sqlite数据库从基础到SDK封装

来源:互联网 发布:网络侵犯名誉权 编辑:程序博客网 时间:2024/05/21 08:51

本篇从数据库的基本使用开始,然后利用反射+泛型对数据库操作进行优化,批量插入操作使用事务提高效率,采用链式调用方式优化查询操作。
最后是封装一个高可用,易替换,不受框架影响的数据库操作SDK。

一、SQLite基本使用

1.1 数据库及表的创建

    public class DbHelper extends SQLiteOpenHelper {        private static final int VERSION = 1;        private static final String DB_NAME = "person.db";        public static final String  TABLE_NAME = "person";        private static final String CREATE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS "                + TABLE_NAME +                " (id integer primary key autoincrement, name text, age integer);";        public DbHelper(Context context) {            super(context, DB_NAME, null, VERSION);        }        @Override        public void onCreate(SQLiteDatabase db) {            db.execSQL(CREATE_TABLE_SQL);        }        @Override        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {        }    }

1.2 数据库操作的实现(增、删、改、查)

    public class DbManager {        private static DbManager sDbManager;        private DbHelper mDbHelper;        private DbManager(Context context) {            mDbHelper = new DbHelper(context);        }        public static DbManager getInstance(Context context) {            if (sDbManager == null) {                synchronized (DbManager.class) {                    if (sDbManager == null) {                        sDbManager = new DbManager(context);                    }                }            }            return sDbManager;        }        public long insert(Person person) {            ContentValues values = contentValuesByObj(person);            SQLiteDatabase database = mDbHelper.getWritableDatabase();            return database.insert(DbHelper.TABLE_NAME, null, values);        }        public int delete(String whereClause, String...whereArgs) {            SQLiteDatabase database = mDbHelper.getWritableDatabase();            return database.delete(DbHelper.TABLE_NAME, whereClause, whereArgs);        }        public int update(Person person, String whereClause, String... whereArgs) {            SQLiteDatabase database = mDbHelper.getWritableDatabase();            ContentValues values = contentValuesByObj(person);            return database.update(DbHelper.TABLE_NAME, values, whereClause, whereArgs);        }        public List<Person> queryAll() {            return query(null, null, null, null, null, null, null);        }        public List<Person> query(String[] columns,                          String selection, String[] selectionArgs, String groupBy,                          String having, String orderBy, String limit) {            SQLiteDatabase database = mDbHelper.getWritableDatabase();            Cursor cursor = database.query(DbHelper.TABLE_NAME, columns,                    selection, selectionArgs, groupBy, having, orderBy, limit);            if (cursor != null && cursor.moveToFirst()) {                List<Person> persons = new ArrayList<>();                do {                    int idColumnIndex = cursor.getColumnIndex("id");                    int nameColumnIndex = cursor.getColumnIndex("name");                    int ageColumnIndex = cursor.getColumnIndex("age");                    int id = cursor.getInt(idColumnIndex);                    String name = cursor.getString(nameColumnIndex);                    int age = cursor.getInt(ageColumnIndex);                    Person person = new Person(id, name, age);                    persons.add(person);                } while (cursor.moveToNext());                return persons;            }            return null;        }        private ContentValues contentValuesByObj(Person person) {            ContentValues values = new ContentValues();            if (person.getId() != 0) {                values.put("id", person.getId());            }            values.put("name", person.getName());            values.put("age", person.getAge());            return values;        }    }

1.3 测试

    public class MainActivity extends AppCompatActivity {        @ViewById(R.id.tv)        private TextView mTv;        @ViewById(R.id.input_et)        private EditText mInputEt;        private DbManager mDbManager;        @Override        protected void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);            setContentView(R.layout.activity_main);            ViewUtils.inject(this);            mDbManager = DbManager.getInstance(this);        }        @OnClick(R.id.btn)        private void click() {            Toast.makeText(MainActivity.this, mTv.getText().toString(), Toast.LENGTH_SHORT).show();        }        @OnClick(R.id.add)        private void add() {            Person person = new Person("dapan", new Random().nextInt(30));            long insert = mDbManager.insert(person);            if (insert != -1) {                Toast.makeText(MainActivity.this, "插入成功: " + insert, Toast.LENGTH_SHORT).show();            }        }        @OnClick(R.id.del)        private void del() {            int delete = mDbManager.delete("name=?", "dapan");            Toast.makeText(MainActivity.this, "删除成功: " + delete, Toast.LENGTH_SHORT).show();        }        @OnClick(R.id.update)        private void update() {            Person person = new Person("dapan", 29);            int update = mDbManager.update(person, "id=?", "1");            Toast.makeText(MainActivity.this, "更新成功: " + update, Toast.LENGTH_SHORT).show();        }        @OnClick(R.id.query)        private void query() {            List<Person> persons = mDbManager.queryAll();            if (persons == null || persons.size() <=0) {                Toast.makeText(MainActivity.this, "没有数据! ", Toast.LENGTH_SHORT).show();                return;            }            Toast.makeText(MainActivity.this, "person count: " + persons.size(), Toast.LENGTH_SHORT).show();        }    }

通过以上三步,就可以在我们的应用中使用SQLite数据库了。

这样就能用?的确,如果不考虑易用性,不考虑性能、不考虑封装,使用上面的三步操作,就可以交差了。
但是,我想说,如果真这样做了,那你要对自己的态度负责,为什么呢?实际开发中,不可能只有Person这一张表,有没有考虑过数据量很大的情况?在有多张表时,你可能会这样做:

  1. 修改DbHelper,添加新表的创建语句;
  2. 再写一个XxxDbManager,然后里面的insert(Xxx xxx),这一系列方法都要重写;
  3. 。。。

难用,不易扩展,要是用新的框架替代又是麻烦事,下面,将祭出我的第一个修改版。

二、利用反射+泛型优化contentValues与对象互转操作

初次使用SQLite时,提到,在插入操作时,参数类型是Person,也就是说写死的,如果在换一个,Student,那插入操作就不能用了,有没有什么好的办法呢?当然有,那就是泛型!如果在我们的插入操作,不关注具体数据类型,那就不会有这个问题了,说的容易,做起来呢?也容易!但,前提是,你能把反射用的很6。

好,先来回顾一下,我们之前是如何插入Person数据的:

    public long insert(Person person) {        ContentValues values = contentValuesByObj(person);        SQLiteDatabase database = mDbHelper.getWritableDatabase();        return database.insert(DbHelper.TABLE_NAME, null, values);    }        private ContentValues contentValuesByObj(Person person) {        ContentValues values = new ContentValues();        if (person.getId() != 0) {            values.put("id", person.getId());        }        values.put("name", person.getName());        values.put("age", person.getAge());        return values;    }

还不错,哈哈,为什么这样说呢?因为插入、修改操作,都要将Obj->ContentVaules,我们将这部分抽取成一个方法,这样,也是一种封装嘛,免得,要写重复的代码!

但,这是这样的封装,只对一个类型有用,如果像上面说的增加新表Student,那就不好使了,所以再次封装时,要考虑到扩展性,那我们就从参数类型着手,不管你要插入什么数据,我们支持,就是这么牛X!

来吧,给你反射,好好的玩耍〜〜

在我们的DbManager.java中添加新的contentValuesByObj方法,这里的Obj不是具体的某个对象,是一个泛型T,所以我们的新方法名为:contentValuesByT。

    private <T> ContentValues contentValuesByT(T obj) {        ContentValues values = new ContentValues();        Field[] fields = obj.getClass().getDeclaredFields();        try {            for (Field field : fields) {                field.setAccessible(true);                String name = field.getName();                //name = $change, type = IncrementalChange                if (field.isSynthetic() || "serialVersionUID".equals(name)) {                    continue;                }                Object value = field.get(name);                // 利用反射,获取ContentValues中的put方法,                // getDeclaredMethod:                // 第一个参数:方法名                // 第二个参数:put方法的第一个参数                // 第三个参数:put方法的第二个参数                Method putMethod = ContentValues.class.getDeclaredMethod("put", String.class, value.getClass());                //通过反射,执行方法,就像:values.put(name, value);                putMethod.invoke(values, name, value);            }        } catch (Exception e) {            e.printStackTrace();        }        return values;    }

为什么要这么麻烦的反射ContentValues中的put方法呢?
我们看下它的源码就知道了:

    public void put(String key, String value) {        mValues.put(key, value);    }    public void put(String key, Integer value) {        mValues.put(key, value);    }    public void put(String key, Long value) {        mValues.put(key, value);    }

其它的与此类似,这里只取三个,现在知道,为什么通过反射拿到属性名,然后根据获取对应的值,去反射ContentValues里的put方法了吧?,就是为了获取不同value类型的put方法!

别看这个方法只用在insert/update两个,而且只修改了一行代码,但是,就是这个修改,让我们的数据库与具体数据类型完全解耦,从此不用去管如何将对象转为ContentValues啦!

三、反射+泛型+链式调用=更好的查询操作

上面对我们的insert/update操作,做了一个小的改进,我们继续!

下面开始操刀,处理一下查询操作。查询是这四种操作里面,最复杂的一个,没有之一!它的条件多的令人发指,让我们从易用性的角度优化这个查询操作。show me code!

首先,我们看下SQLiteDataBase中的一个query方法:

    public Cursor query(String table, String[] columns, String selection,            String[] selectionArgs, String groupBy, String having,            String orderBy, String limit) {        return query(false, table, columns, selection, selectionArgs, groupBy,                having, orderBy, limit);    }

没错,就是八个参数!

我们要记住每个位置上的参数代表什么含意,不然,系统就会报错给你看!太可怕了,那我们怎么优化呢?通常情况下,我们是不需要八个参数都设置一遍的。嵌套调用这种方式是行不通的,参数太多,这样要写多少个方法?有一种方式,最适合这种多参数的场景,那就是 链式调用 ,我们为每个参数添加一个方法,如果用户没有调用,设置为默认的null即可,这样,需要什么参数,调用就好。是不是 so easy,再也不怕记错参数位置啦!

我们不仅要考虑查询使用的优化,对于代码,我们也要严格要求自己,如果将这么多的方法写到一个类中,易读性明显变差,所以,我们创建一个新的QueryCenter类,没错,它就是专门处理查询的。

    public class QueryCenter<T> {        private String[] columns;        private String selection;        private String[] selectionArgs;        private String groupBy;        private String having;        private String orderBy;        private String limit;        public QueryCenter<T> setColumns(String[] columns) {            this.columns = columns;            return this;        }        public QueryCenter<T> setSelection(String selection) {            this.selection = selection;            return this;        }        public QueryCenter<T> setSelectionArgs(String[] selectionArgs) {            this.selectionArgs = selectionArgs;            return this;        }        public QueryCenter<T> setGroupBy(String groupBy) {            this.groupBy = groupBy;            return this;        }        public QueryCenter<T> setHaving(String having) {            this.having = having;            return this;        }        public QueryCenter<T> setOrderBy(String orderBy) {            this.orderBy = orderBy;            return this;        }        public QueryCenter<T> setLimit(String limit) {            this.limit = limit;            return this;        }    }

上面只是将参数封装成了方法,下面,我们完善query()方法:

    public QueryCenter(SQLiteDatabase database, T obj) {        this.mDatabase = database;        this.obj = obj;    }    public List<T> queryAll() {        String tableName = DbUtil.getTableName(obj.getClass());        Cursor cursor = mDatabase.query(tableName, null, null, null, null, null, null);        return listByCursor(cursor);    }    public List<T> query() {        String tableName = DbUtil.getTableName(obj.getClass());        Cursor cursor = mDatabase.query(tableName, columns, selection, selectionArgs,                groupBy, having, orderBy, limit);        clearParams(); // 每次查询完了,将所有参数设置为null        return cursor2List(cursor);    }    private List<T> cursor2List(Cursor cursor) {        return null;    }

先根据需求设置参数,然后调用query/queryAll()即可完成查询!查询结果返回给我们一个Cursor,最后处理一下这个Cursor,我们的查询方法就算大功告成啦!开心〜

    private List<T> cursor2List(Cursor cursor) {        List<T> list = new ArrayList<>();        if (cursor != null && cursor.moveToFirst()) {            try {                do {                    T instance = mClazz.newInstance();                    Field[] fields = mClazz.getDeclaredFields();                    for (Field field : fields) {                        field.setAccessible(true);                        String name = field.getName();                        int columnIndex = cursor.getColumnIndex(name);                        // cursor.getString(columnIndex);                        if (columnIndex == -1) {                            continue;                        }                        Method cursorMethod = cursorMethod(field.getType());                        if (cursorMethod != null) {                            Object value = cursorMethod.invoke(cursor, columnIndex);                            if (value == null) {                                continue;                            }                            value = DbUtil.parseTypeValue(field, value);                            field.set(instance, value);                        }                        list.add(instance);                    } // for end                } while (cursor.moveToNext());            } catch (Exception e) {                e.printStackTrace();            }        }        return list;    }    private Method cursorMethod(Class<?> type) throws NoSuchMethodException {        String methodName = getColumnMethodName(type);        Method method = Cursor.class.getDeclaredMethod(methodName, int.class);        return method;    }    private String getColumnMethodName(Class<?> fieldType) {        String typeName = null;        if (fieldType.isPrimitive()) {            //将基本数据类型的首字母大写            typeName = DbUtil.capitalize(fieldType.getName());        } else {            typeName = fieldType.getSimpleName();        }        String methodName = "get" + typeName;        if ("getBoolean".equals(methodName)) {            methodName = "getInt";        } else if ("getChar".equals(methodName) || "getCharacter".equals(methodName)) {            methodName = "getString";        } else if ("getDate".equals(methodName)) {            methodName = "getLong";        } else if ("getInteger".equals(methodName)) {            methodName = "getInt";        }        return methodName;    }

这部分非常关键,我们的思路是,使用newInstance()来实例化对象,利用反射获取columnIndex列的值,最后调用该属性的field.set()方法,将该列的值赋值给instance对象。

四、自动化建表

首先,我们来分析一下,通常情况下,数据库只不过是存储的一张张的表,而每一张表,通常情况下对应一个实体类,从这点出发,我们是不是考虑,将创建数据库表这第一层来个封装呢?我们来试着做一下:

    private static final String CREATE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS "            + TABLE_NAME +            " (id integer primary key autoincrement, name text, age integer);";

从上面的建表语句,我们可知,变化的有两部分,第一:表名–>实体类名;第二:字段名–>实体类中的成员属性。

类名,很容易获得,但,成员属性就难了,我们根本不知道下一张表内会有哪些内容!怎么定?!

在java中有一个“旁门左道”-反射,为什么说它是“旁门左道”呢?因为,它将破坏封装的规则,如果:有一个属性,这是类内部使用的,但,只要通过反射,就能拿到这个属性,就能随意赋值了!一个方法,本来不需要外部知道,即使是private,但通过反射就能执行这个方法!

4.0 前提

在上面分析了建表语句的”变”与”不变”部分,还有一点需要注意,那就是java中的类型与sqlite中的数据类型的差异。比如:int->integer, String->text, sqlite中没有boolean这数据类型。
常见的数据类型之间的关系如下:

 java         sqlite ======================= String       text int          integer boolean      boolean float        float double       double char         varchar long         long

注:因为java和sqlite中的数据类型都是固定的,其他的,自行添加即可(这里主要想说明这种套路!)。

有了上面的这个表,那我们该怎么利用它呢?

4.1 类型转换

网上其实很多文章介绍反射的,我在从android:onClick属性谈运行时注解在Android中的运用中利用反射获取属性上的注解,让“劳力士”帮咱完成了findViewById()操作。这里呢,我们还要让它上,让它帮我们偷取属性,然后合成建表语句。
创建工作类DaoUtil:

    public class DaoUtil {        private DaoUtil() {            throw new UnsupportedOperationException("cannot be instantiated");        }        public static <T> String getTableName(Class<T> clazz) {            return clazz.getSimpleName();        }        public static String getColumnType(String type) {            String value = null;            if (type.contains("String")) {                value = " text";            } else if (type.contains("int")) {                value = " integer";            } else if (type.contains("boolean")) {                value = " boolean";            } else if (type.contains("float")) {                value = " float";            } else if (type.contains("double")) {                value = " double";            } else if (type.contains("char")) {                value = " varchar";            } else if (type.contains("long")) {                value = " long";            } else {                value = " " + type;            }            return value;        }    }

4.2 生成建表语句

    /**     * "CREATE TABLE IF NOT EXISTS person (id integer primary key autoincrement, name text, age integer);";     */    public static <T> String generatorCreateTableSql(T obj) {        StringBuilder sql = new StringBuilder("CREATE TABLE IF NOT EXISTS ");        sql.append(getTableName(obj.getClass()));        sql.append(" (id integer primary key autoincrement, ");        //获取属性,合成表中的必要字段        Field[] fields = obj.getClass().getDeclaredFields();        for (Field field : fields) {            field.setAccessible(true);            String name = field.getName();            String type = field.getType().getSimpleName();            //name = $change, type = IncrementalChange            if (field.isSynthetic() || "serialVersionUID".equals(name)) {                continue;            }            sql.append(name).append(getColumnType(type)).append(", ");        }        sql.replace(sql.length() - 2, sql.length(), ");"); //用");"替掉最后一个", "        return sql.toString();    }

这里,我们直接使用反射获取属性,然后将java中的数据类型转为sqlite中的数据类型,然后拼接生成建表语句。需要注意的是,我在as中,编译后,会生成两个与所生成数据库表无关的字段:

name = $changename = serialVersionUID

我们这里不需要这两个属性参与,遇到直接continue过滤掉。

至此,我们可以自动化生成数据库表了,怎么样,又可以多刷两分钟的微博啦〜

五、跟着源码优化反射操作

通读全篇,我们有三处使用了反射:

  1. 生成建表语句
  2. 对象转ContentValues
  3. 查询结果Cursor转List

=======

5.1 优化contentValuesByObj方法

    private static final Object[] sPutMethodArgs = new Object[2]; // 缓存,避免不必要的创建    private static final Map<String, Method> sPutMethod = new ArrayMap<String, Method>(); //缓存方法    private <T> ContentValues contentValuesByTFinal(T obj) {        ContentValues values = new ContentValues();        Field[] fields = obj.getClass().getDeclaredFields();        try {            for (Field field : fields) {                field.setAccessible(true);                String name = field.getName();                //name = $change, type = IncrementalChange                if (field.isSynthetic() || "serialVersionUID".equals(name)) {                    continue;                }                Object value = field.get(obj);                sPutMethodArgs[0] = name;                sPutMethodArgs[1] = value;                String typeName = field.getType().getSimpleName();                Method putMethod = sPutMethod.get(typeName);                if (putMethod == null) {                    putMethod = ContentValues.class.getDeclaredMethod("put", String.class, value.getClass());                    sPutMethod.put(typeName, putMethod);                }                //通过反射,执行方法,就像:values.put(name, value);                putMethod.invoke(values, sPutMethodArgs);            }        } catch (Exception e) {            e.printStackTrace();        } finally {            sPutMethodArgs[0] = null;            sPutMethodArgs[1] = null;        }        return values;    }  ```这里对方法的参数、反射得到的方法进行了缓存,避免不必要的对象创建过程。这里参考的来自support.v7中的[AppCompatViewInflater.java#createViewFromTag()](http://androidxref.com/7.1.1_r6/xref/frameworks/support/v7/appcompat/src/android/support/v7/app/AppCompatViewInflater.java) 源码。### 5.2 优化批量插入操作这个很简单,就是在插入之前,开启事务。
public void insert(List<Person> list) {    mDbHelper.getWritableDatabase().beginTransaction();    for (Person obj : list) {        insert(obj);    }    mDbHelper.getWritableDatabase().setTransactionSuccessful();    mDbHelper.getWritableDatabase().endTransaction();}
当然,这只是最基础的优化,我们要考虑到,如果只是数据量大,前后关系不是很密切,那应该考虑分批插入等,需要根据实际应用场景进行扩展,当然,等你的应用发展到一定规模,只是这么做还是存在很多风险的,所以,我们要从全局去思考,也许,这是我们走上架构的第一步。。。# 六、全局思考说了这么多,万一我用了这个框架,万一又出问题了,或者领导要求使用比较成熟的其他第三方数据库框架,那怎么办?系统小了还好,删除与此相关的引用,然后使用新的数据库框架即可,但是,要是一个App有很多地方引用,或者多个App都用这个,那太可怕了,所以,从全局思考,我们该如何使用数据库框架来优化我们的应用架构呢?或者说,可以尽可能的减少我们的维护成本呢?很显示,那就是借助在使用任何第三方框架时,先将它封装一层,让第三方框架与我们的应用隔离。在App中,使用我们封装的那一层,比如:db.insert(), db.update(),db.query(),这样一来,就降低了App与第三方框架的耦合,怎么样?现在开始封装我们的SDK吧!这里我们使用一种工厂方式模式,来搭建我们的数据库SDK。![anKataLite/db_sdk框架图](http://img.blog.csdn.net/20170326213514143?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGFwYW43Mjg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)### 6.1 创建统一的操作归范
public interface IDbSupport<T> {    long insert(T obj);    int delete(String whereClause, String... whereArgs);    int update(T obj, String whereClause, String... whereArgs);    QueryCenter getQuery(Class<?> clazz);    void setDbHelperAndClazz(DbHelper mDbHelper, Class<T> clazz);}
### 6.2 默认实现
public class DefaultDbSupport<T> implements IDbSupport<T> {    private DbHelper mDbHelper;    private Class<T> mClazz;    private QueryCenter mQueryCenter;    private SQLiteDatabase getWritableDatabase() {        return mDbHelper.getWritableDatabase();    }    private SQLiteDatabase getReadableDatabase() {        return mDbHelper.getReadableDatabase();    }    @Override    public long insert(T obj) {        ContentValues values = contentValuesByTFinal(obj);        SQLiteDatabase database = getWritableDatabase();        long insert = database.insert(DbUtil.getTableName(obj.getClass()), null, values);        return insert;    }    public void insert(List<T> list) {        getWritableDatabase().beginTransaction();        for (T obj : list) {            insert(obj);        }        getWritableDatabase().setTransactionSuccessful();        getWritableDatabase().endTransaction();    }    @Override    public int delete(String whereClause, String... whereArgs) {        SQLiteDatabase database = getWritableDatabase();        int delete = database.delete(DbUtil.getTableName(mClazz), whereClause, whereArgs);        return delete;    }    @Override    public int update(T obj, String whereClause, String... whereArgs) {        SQLiteDatabase database = getWritableDatabase();        ContentValues values = contentValuesByTFinal(obj);        int update = database.update(DbUtil.getTableName(obj.getClass()), values, whereClause, whereArgs);        return update;    }    private static final Object[] sPutMethodArgs = new Object[2]; // 缓存,避免不必要的创建    private static final Map<String, Method> sPutMethod = new ArrayMap<String, Method>(); //缓存方法    private <T> ContentValues contentValuesByTFinal(T obj) {        ContentValues values = new ContentValues();        Field[] fields = obj.getClass().getDeclaredFields();        try {            for (Field field : fields) {                field.setAccessible(true);                String name = field.getName();                //name = $change, type = IncrementalChange                if (field.isSynthetic() || "serialVersionUID".equals(name)) {                    continue;                }                Object value = field.get(obj);                sPutMethodArgs[0] = name;                sPutMethodArgs[1] = value;                String typeName = field.getType().getSimpleName();                Method putMethod = sPutMethod.get(typeName);                if (putMethod == null) {                    putMethod = ContentValues.class.getDeclaredMethod("put", String.class, value.getClass());                    sPutMethod.put(typeName, putMethod);                }                //通过反射,执行方法,就像:values.put(name, value);                putMethod.invoke(values, sPutMethodArgs);            }        } catch (Exception e) {            e.printStackTrace();        } finally {            sPutMethodArgs[0] = null;            sPutMethodArgs[1] = null;        }        return values;    }    @Override    public QueryCenter getQuery(Class<?> clazz) {        if (mQueryCenter == null) {            mQueryCenter = new QueryCenter(getReadableDatabase(), clazz);        }        return mQueryCenter;    }    public void setDbHelperAndClazz(DbHelper dbHelper, Class<T> clazz) {        this.mDbHelper = dbHelper;        this.mClazz = clazz;    }}
### 6.3 统一入口
public class DbSupportFactory<T> {    private static DbSupportFactory sDbSupportFactory;    private DbHelper mDbHelper;    private DbSupportFactory(Context context, T...obj) {        mDbHelper = new DbHelper(context, obj);    }    public static <T> DbSupportFactory getFactory(Context context, T...obj) {        if (sDbSupportFactory == null) {            synchronized (DbSupportFactory.class) {                if (sDbSupportFactory == null) {                    sDbSupportFactory = new DbSupportFactory(context);                }            }        }        return sDbSupportFactory;    }    public <T> IDbSupport getDbSupport(IDbSupport dbSupport, Class<T> clazz) {        dbSupport.setDbHelperAndClazz(mDbHelper, clazz);        return dbSupport;    }}
### 6.4 使用
dbSupport = DbSupportFactory.getFactory(this, Person.class, Student.class).getDbSupport(new DefaultDbSupport(), Person.class);

“`

比如,现在App中有两张表,Person、Student,我们在获取工厂时,传入相关的class(用于创建表),然后在获取具体DbSupport时,传入要操作的那个表即可。

后记

本来是想在之前的笔记上改一下,分享出来,没想到,加上代码测试,整了一天,才算理清思路,这不是数据库封装的终点,恰好只是抛砖引玉一个开始。更深入的:如何存到SD卡中,我们操作数据库时,用的SQLiteDatabase是什么?当然,摆在眼前,最关键的是,如果像我文中提到的,更换其他数据库框架真的那么容易吗?


如何获取代码?

git clone https://github.com/droid4j/anKataLite.git

本篇对应的标签v0.3

git checkout v0.3

0 0
原创粉丝点击