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

1. 简述

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

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

2. 框架设计图

移动架构-理解数据库框架设计 part1 - 自动建表_第1张图片
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 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  BaseDao getBaseDao(Class 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'; 注意这里不能写错了 否则要重新写

移动架构-理解数据库框架设计 part1 - 自动建表_第2张图片
image.png
移动架构-理解数据库框架设计 part1 - 自动建表_第3张图片
image.png
移动架构-理解数据库框架设计 part1 - 自动建表_第4张图片
image.png

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

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

代码在这里github

你可能感兴趣的:(移动架构-理解数据库框架设计 part1 - 自动建表)