Android 数据库框架设计

数据库的作用:

在Android开发中,数据库在小中型的app中使用并不广泛,但对于数据管理的方便性是其他数据存储结构工具无法取代的,特点:数据集中管理,控制冗余,提高数据的利用率和一致性,有利于程序的开发和维护。

数据库的设计:

数据库设计的三大范式:

第一范式:原子性

第二范式:唯一性

第三范式:避免冗余性

Android数据库架构分析

如何自动创建数据库、自动创建数据库表

通过创建自定义注解,将表名应用为类名,数据库字段设为类的全局变量,创建bean对象,运用反射得到类名和字段,创建sql语句,自动创建数据可

// 得到User对应表名

@DbTable("tb_user")

public class User {

    // 得到User对象对应列名

    @DbField("u_id")

    private Integer id;

    private String name;

    private String password;

    private Integer status;

    public Integer getStatus() {

        return status;

    }

    public void setStatus(Integer status) {

        this.status = status;

    }

    public User(){}

    public User(Integer id, String name, String password) {

        this.id = id;

        this.name = name;

        this.password = password;

    }

    public Integer getId() {

        return id;

    }

    public void setId(Integer id) {

        this.id = id;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public String getPassword() {

        return password;

    }

    public void setPassword(String password) {

        this.password = password;

    }

    @Override

    public String toString() {

        return "User{" +

                "id=" + id +

                ", name='" + name + '\'' +

                ", password='" + password + '\'' +

                '}';

    }

}

创建db

public class BaseDaoFactory {

    private static final BaseDaoFactory instance = new BaseDaoFactory();

    public static BaseDaoFactory getInstance(){

        return instance;

    }

    private SQLiteDatabase sqLiteDatabase;

    private String sqlitePath;

    // 设计要给数据库连接池,new 容器,只要new个一次,下次就不会再创建了。考虑多线程的问题

    protected Map<String,BaseDao> map = Collections.synchronizedMap(new HashMap<String, BaseDao>());

    protected BaseDaoFactory(){

        sqlitePath = "data/data/com.neteasedb/ne.db";

        sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(sqlitePath,null);

    }

    // 生产basedao对象

    public <T extends BaseDao<M>,M> T getBaseDao(Class<T> daoClass,Class<M> entityClass){

        BaseDao baseDao = null;

        if(map.get(daoClass.getSimpleName()) != null){

            return (T)map.get(daoClass.getSimpleName());

        }

        try {

            baseDao = daoClass.newInstance();

            baseDao.init(sqLiteDatabase,entityClass);

            map.put(daoClass.getSimpleName(),baseDao);

        } catch (IllegalAccessException e) {

            e.printStackTrace();

        } catch (InstantiationException e) {

            e.printStackTrace();

        }

        return (T)baseDao;

    }

}

如何让用户在使用的时候非常的方便、 数据库的查询,修改,删除

创建SQLiteDatabase持有数据库的引用,创建一个缓存空间,避免频繁引用反射,耗费内存。

public class BaseDao<T> implements IBaseDao<T>{

    // 持有数据库操作的引用

    private SQLiteDatabase sqLiteDatabase;

    // 表名

    private String tableName;

    // 操作数据库所对应的java类型

    private Class<T> entityClass;

    // 标识,用来标识是否已经做过初始化

    private boolean isInit = false;

    // 定义一个缓存空间(key 字段名 value 成员变量)

    private HashMap<String,Field> cacheMap;

    public boolean init(SQLiteDatabase sqLiteDatabase,Class<T> entityClass){

        this.sqLiteDatabase = sqLiteDatabase;

        this.entityClass = entityClass;

        if(!isInit){

            // 根据传入的Class进行数据表的创建 本例子中对应的是User对象;

            DbTable dt = entityClass.getAnnotation(DbTable.class);

            if(dt != null && !"".equals(dt.value())){

                tableName = dt.value();

            }else{

                tableName = entityClass.getName();

            }

            if(!sqLiteDatabase.isOpen()){

                return false;

            }

            String createTableSql = getCreateTableSql();

            sqLiteDatabase.execSQL(createTableSql);

            cacheMap = new HashMap<>();

            initCacheMap();

            isInit = true;

        }

        return isInit;

    }

    private void initCacheMap() {

        // 取得所有的列名

        String sql = "select * from "+tableName+" limit 1,0";

        Cursor cursor = sqLiteDatabase.rawQuery(sql,null);

        String[] columnNames = cursor.getColumnNames();

        // 获取所有的成员变量

        Field[] colunmnFields = entityClass.getDeclaredFields();

        // 将字段访问权限打开

        for(Field field : colunmnFields){

            field.setAccessible(true);

        }

        for(String columnName:columnNames){

            Field columnField=null;

            for(Field field:colunmnFields){

                String fieldName=null;

                if(field.getAnnotation(DbField.class)!=null){

                    fieldName=field.getAnnotation(DbField.class).value();

                }else{

                    fieldName=field.getName();

                }

                if(columnName.equals(fieldName)){

                    columnField=field;

                    break;

                }

            }

            if(columnField!=null){

                cacheMap.put(columnName,columnField);

            }

        }

    }

    private String getCreateTableSql() {

        StringBuilder stringBuilder = new StringBuilder();

        stringBuilder.append("create table if not exists ");

        stringBuilder.append(tableName+"(");

        // 反射得到所有的成员变量

        Field[] fields = entityClass.getDeclaredFields();

        for(Field field : fields){

            Class type = field.getType();

            DbField dbField = field.getAnnotation(DbField.class);

            if(dbField != null && !"".equals(dbField.value())){

                if(type == String.class){

                    stringBuilder.append(dbField.value()+" TEXT,");

                }else if(type== Integer.class){

                    stringBuilder.append(dbField.value()+" INTEGER,");

                }else if(type== Long.class){

                    stringBuilder.append(dbField.value()+" BIGINT,");

                }else if(type== Double.class){

                    stringBuilder.append(dbField.value()+" DOUBLE,");

                }else if(type==byte[].class){

                    stringBuilder.append(dbField.value()+" BLOB,");

                }else{

                    //不支持的类型号

                    continue;

                }

            }else{

                if(type== String.class){

                    stringBuilder.append(field.getName()+" TEXT,");

                }else if(type== Integer.class){

                    stringBuilder.append(field.getName()+" INTEGER,");

                }else if(type== Long.class){

                    stringBuilder.append(field.getName()+" BIGINT,");

                }else if(type== Double.class){

                    stringBuilder.append(field.getName()+" DOUBLE,");

                }else if(type==byte[].class){

                    stringBuilder.append(field.getName()+" BLOB,");

                }else{

                    //不支持的类型号

                    continue;

                }

            }

        }

        if(stringBuilder.charAt(stringBuilder.length()-1)==','){

            stringBuilder.deleteCharAt(stringBuilder.length()-1);

        }

        stringBuilder.append(")");

        return stringBuilder.toString();

    }

    @Override

    public long insert(T entity) {

        // user 对象 转换为contentvalues  new User(id 1,name = "netease","password");

        Map<String,String> map = getValues(entity);

        ContentValues values = getContentValues(map);

        return sqLiteDatabase.insert(tableName,null,values);

    }

    @Override

    public long update(T entity, T where) {

        // 将传进来的对象 将成员变量和成员变量的值 转为map

        Map map = getValues(entity);

        ContentValues values = getContentValues(map);

        Map whereMap = getValues(where);

        Condition condition = new Condition(whereMap);

        return sqLiteDatabase.update( tableName,values,condition.whereCause,condition.whereArgs);

    }

    @Override

    public int delete(T where) {

        Map map = getValues(where);

        Condition condition = new Condition(map);

        return sqLiteDatabase.delete(tableName,condition.whereCause,condition.whereArgs);

    }

    @Override

    public List<T> query(T where) {

        return query(where,null,null,null);

    }

    @Override

    public List<T> query(T where, String orderBy, Integer startIndex, Integer limit) {

        Map map = getValues(where);

        // select * from tableName  limit 0,10;

        String limitString = null;

        if(startIndex != null && limit != null){

            limitString = startIndex+" , "+limit;

        }

//        String seclections = "where 1=1 and id=? and name=?";

//        String selectionArgs = String[]{,""};

        // select * from tableName where id=? and name=?

        Condition condition = new Condition(map);

        Cursor cursor = sqLiteDatabase.query(tableName,null,condition.whereCause,condition.whereArgs,null,null,

                orderBy,limitString);

        // 定义一个解析游标的方法

        List<T> result = getResult(cursor,where);

        return result;

    }

    private List<T> getResult(Cursor cursor, T obj) {

        ArrayList list = new ArrayList();

        Object item = null;// User user = null;

        while (cursor.moveToNext()){

            try {

                item = obj.getClass().newInstance(); // user = new User(); user.setId(cursor.getId);

                Iterator iterator = cacheMap.entrySet().iterator();// 成员变量

                while (iterator.hasNext()){

                    Map.Entry entry = (Map.Entry)iterator.next();

                    // 获取列名

                    String columnName = (String)entry.getKey();

                    // 以列名拿到列名在游标中的位置

                    Integer columnIndex = cursor.getColumnIndex(columnName);

                    // 获取成员变量的类型

                    Field field = (Field) entry.getValue();

                    Class type = field.getType();

                    // cursor.getString(columnIndex);

                    if(columnIndex != -1){

                        if(type == String.class){

                            // User user = new User

                            // user.setId(1);  id.set(user,1);

                            field.set(item,cursor.getString(columnIndex));

                        }else if(type== Double.class){

                            field.set(item,cursor.getDouble(columnIndex));

                        }else if(type== Integer.class){

                            field.set(item,cursor.getInt(columnIndex));

                        }else if(type== Long.class){

                            field.set(item,cursor.getLong(columnIndex));

                        }else if(type==byte[].class){

                            field.set(item,cursor.getBlob(columnIndex));

                        }else{

                            continue;

                        }

                    }

                }

                list.add(item);

            } catch (IllegalAccessException e) {

                e.printStackTrace();

            } catch (InstantiationException e) {

                e.printStackTrace();

            }

        }

        cursor.close();

        return list;

    }

    private class Condition{

        private String whereCause;

        private String[] whereArgs;

        public Condition(Map<String,String> whereMap){

            ArrayList list = new ArrayList();

            StringBuilder stringBuilder = new StringBuilder();

            stringBuilder.append("1=1");

            // 获取所有的字段名

            Set keys = whereMap.keySet();

            Iterator iterator = keys.iterator();

            while (iterator.hasNext()){

                String key = (String)iterator.next();

                String value = whereMap.get(key);

                if(value != null){

                    stringBuilder.append(" and "+key+" =?");

                    list.add(value);

                }

            }

            this.whereCause = stringBuilder.toString();

            this.whereArgs = (String[])list.toArray(new String[list.size()]);

        }

    }

    private ContentValues getContentValues(Map<String, String> map) {

        ContentValues contentValues = new ContentValues();

        Set keys = map.keySet();

        Iterator<String> iterator = keys.iterator();

        while (iterator.hasNext()){

            String key = iterator.next();

            String value = map.get(key);

            if(value != null){

                contentValues.put(key,value);

            }

        }

        return contentValues;

    }

    private Map<String, String> getValues(T entity) {

        HashMap<String,String> map = new HashMap<>();

        // 得到所有的成员变量,user的成员变量

        Iterator<Field> fieldIterator = cacheMap.values().iterator();

        while (fieldIterator.hasNext()){

            Field field = fieldIterator.next();

            field.setAccessible(true);

            // 获取成员变量的值

            try {

                Object object = field.get(entity);

                if(object == null){

                    continue;

                }

                String value = object.toString();

                // 获取列名

                String key = null;

                DbField dbField = field.getAnnotation(DbField.class);

                if( dbField != null && !"".equals(dbField.value())){

                    key = dbField.value();

                }else{

                    key = field.getName();

                }

                if(!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)){

                    map.put(key,value);

                }

            } catch (IllegalAccessException e) {

                e.printStackTrace();

            }

        }

        return map;

    }

}

数据库分库

应用场景:大型app中,当有多个账号需要切换时。

有点:数据隔离,互不打扰。便于数据的管理。

缺点:会消耗内存,不过相比于有点,这点内存还是可以接受的

数据库的升级

原理 利用xml文件进行升级 

步骤 

1.先把数据备份,重命名保存在当前位置,防止数据丢失,以便升级后将数据重新加入到升级后的表中

2.重新创建数据库表

3.将重命名之后的表里的数据导入到新建的表里

4.将之前备份的表删除

5.利用xml升级必须知道之前的表结构和了解xml,所以只能针对自己的项目进行升级

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容