美文网首页Android开发经验谈Android开发Android技术知识
移动架构-理解数据库框架设计 part1 - 自动建表

移动架构-理解数据库框架设计 part1 - 自动建表

作者: 289346a467da | 来源:发表于2018-03-18 22:24 被阅读90次

    1. 简述

    Android端使用的数据库是SQLite,这种小型的数据库很适合在移动端存储大量的数据,但是官方提供的api确实不太好用,必须掌握到一定程度的sql语句,否则写起来很难。同时,有很多第三方的框架开始流行,比如:greenDao、room 等,只需要简单的几行代码就可以对数据库进行操作,这些第三方框架也都是基于最底层的SQLite 进行封装的。我们不能只满足于使用,最为一名合格的程序员我们要学会自己封装,so 手撸一个数据库框架,学习高级技巧。

    自动建表部分,使用:设计模式、泛型、注解、反射这些高级技巧来实现。 轻松的来完成建表操作。

    2. 框架设计图

    image.png

    3.分析

    1. 既然是封装,如何灵活的将 表和字段 建立出来?可以这样设计 BaseDao.init(User.class) 我们可以通过类来进行建表,类名作为表名,成员变量作为字段名,当然如果我们不想用类名可以通过注解的方式命名表名和字段名。这样就可以去除每次写创建表的sql语句了。

    OK,分析完毕后,开始第一步,如何自定义注解?
    很简单 通过设置

    @Target 注解能标记在那些地方 METHOD - 方法 FIELD - 字段成员变量 TYPE - class 类型 ANNOTATION_TYPE - 其他注解类上面 CONSTRUCTOR - 构造方法 LOCAL_VARIABLE - 局部变量
    @Retention //保留时间 - SOURCE - 源码其存在 CLASS - 编译成class时存在 RUNTIME - 运行时开始存在

    /**
     * 自建数据库表名的注解
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface DbTable {
        String value();
    }
    
    /**
     * 数据库字段名的注解
     */
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface DbField {
        String value();
    }
    

    如何使用注解?

    /**
     * 如果没有注解 就用类名和字段名 建数据库表  , 如果用注解标注则用注解的名字
     */
    @DbTable("db_user")
    public class User {
        @DbField("_id")
        public Integer id;
        public String name;
        public String password;
    }
    

    OK,第一步已经完成,开始第二步

    1. 如何获取类名 成员变量名 和注解名
      下面一段代码就看懂了。
     protected boolean init(SQLiteDatabase sqLiteDatabase, Class<T> entityClass) {
            this.sqLiteDatabase = sqLiteDatabase;
            this.entityClass = entityClass;
                // 取表名
                if (entityClass.getAnnotation(DbTable.class) == null) {
                    // 反射获取类名做表名
                    tbName = entityClass.getSimpleName();
                } else {
                    // 获取注解名做为表名
                    tbName = entityClass.getAnnotation(DbTable.class).value();
                }
            }
        }
    

    继续,名字已经拿到了,下面开始建表

    1. 如何自动建表?
      对拿到的名字进行,拼接sql语句
     private String getCreateTable() {
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append("create table if not exists " + tbName + "(");
            Field[] fields = entityClass.getDeclaredFields();// 反射获取所有的成员变量
            for (Field field : fields) {// 遍历成员变量
                Class<?> type = field.getType();// 拿到成员变量
                if (field.getAnnotation(DbField.class) != null) { //如果成员变量有注解 则数据表的字段名用 注解名
                    if (type == String.class) {
                        stringBuffer.append(field.getAnnotation(DbField.class).value() + " TEXT,");
                    } else if (type == Integer.class) {
                        stringBuffer.append(field.getAnnotation(DbField.class).value() + " INTEGER,");
                    } else if (type == Long.class) {
                        stringBuffer.append(field.getAnnotation(DbField.class).value() + " BIGINT,");
                    } else if (type == Double.class) {
                        stringBuffer.append(field.getAnnotation(DbField.class).value() + " DOUBLE,");
                    } else if (type == byte[].class) {
                        stringBuffer.append(field.getAnnotation(DbField.class).value() + " BLOB,");
                    } else {
                        continue;
                    }
                } else {
                    if (type == String.class) {
                        stringBuffer.append(field.getName() + " TEXT,");
                    } else if (type == Integer.class) {
                        stringBuffer.append(field.getName() + " INTEGER,");
                    } else if (type == Long.class) {
                        stringBuffer.append(field.getName() + " BIGINT,");
                    } else if (type == Double.class) {
                        stringBuffer.append(field.getName() + " DOUBLE,");
                    } else if (type == byte[].class) {
                        stringBuffer.append(field.getName() + " BLOB,");
                    } else {
                        continue;
                    }
                }
            }
            if (stringBuffer.charAt(stringBuffer.length() - 1) == ',') {
                stringBuffer.deleteCharAt(stringBuffer.length() - 1);
            }
            stringBuffer.append(")");
            return stringBuffer.toString();
        }
    

    建表

      // 自动建表
                //如果数据库没有打开 返回false
                if (!sqLiteDatabase.isOpen()) {
                    return false;
                }
                //数据库打开 执行建表操作
                // create table if not  exists tb_user(id Integer,name varchar(20))
                sqLiteDatabase.execSQL(getCreateTable());
    
    1. new一个数据库操作工厂,对外开放方法
    public class BaseDaoFactory {
        private static final BaseDaoFactory instance = new BaseDaoFactory();
    
        public static BaseDaoFactory getInstance() {
            return instance;
        }
    
        private SQLiteDatabase sqLiteDatabase;
    
        //建立数据库的路径 建议写到SD 卡中 如果app 被用户删除了 下次安装的时候数据还在
        private String dbPath;
    
        private static final String TAG = "BaseDaoFactory";
        private BaseDaoFactory() {
            // 可以先判断有没有Sd 卡
    
            // 写到项目中
            dbPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/lib_prim.db";
            Log.e(TAG, "BaseDaoFactory: "+dbPath);
            //打开或创建数据库
            sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(dbPath, null);
        }
    
        /**
         * 用来生产basedao 对象
         */
        public <T> BaseDao<T> getBaseDao(Class<T> entityClass) {
            BaseDao baseDao = null;
            try {
                baseDao = BaseDao.class.newInstance();
                baseDao.init(sqLiteDatabase, entityClass);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return baseDao;
        }
    }
    
    1. 如何调用呢?
      BaseDaoFactory.getInstance().getBaseDao(Person.class);
      这样我们就,创建了一个Person数据库表。一行代码就搞定了。

    6.查看数据库
    查询数据库是否建好的命令: adb shell --> ls --> su --> ls --> cd sdcard --> ls --> 查看是否有建立的数据库 --> 查询数据库表是否建立好 sqlite3 数据库名 --》 select * from sqlite_master where type='table'; 注意这里不能写错了 否则要重新写

    image.png image.png image.png

    可以看到确实数据库表自动创建了,大功告成。

    通过泛型、注解、反射这些技巧。

    代码在这里github

    相关文章

      网友评论

        本文标题:移动架构-理解数据库框架设计 part1 - 自动建表

        本文链接:https://www.haomeiwen.com/subject/tdyyqftx.html