1. 简述
Android端使用的数据库是SQLite,这种小型的数据库很适合在移动端存储大量的数据,但是官方提供的api确实不太好用,必须掌握到一定程度的sql语句,否则写起来很难。同时,有很多第三方的框架开始流行,比如:greenDao、room 等,只需要简单的几行代码就可以对数据库进行操作,这些第三方框架也都是基于最底层的SQLite 进行封装的。我们不能只满足于使用,最为一名合格的程序员我们要学会自己封装,so 手撸一个数据库框架,学习高级技巧。
自动建表部分,使用:设计模式、泛型、注解、反射这些高级技巧来实现。 轻松的来完成建表操作。
2. 框架设计图
image.png3.分析
- 既然是封装,如何灵活的将 表和字段 建立出来?可以这样设计 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,第一步已经完成,开始第二步
- 如何获取类名 成员变量名 和注解名
下面一段代码就看懂了。
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();
}
}
}
继续,名字已经拿到了,下面开始建表
- 如何自动建表?
对拿到的名字进行,拼接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());
- 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;
}
}
- 如何调用呢?
BaseDaoFactory.getInstance().getBaseDao(Person.class);
这样我们就,创建了一个Person数据库表。一行代码就搞定了。
6.查看数据库
查询数据库是否建好的命令: adb shell --> ls --> su --> ls --> cd sdcard --> ls --> 查看是否有建立的数据库 --> 查询数据库表是否建立好 sqlite3 数据库名 --》 select * from sqlite_master where type='table'; 注意这里不能写错了 否则要重新写
可以看到确实数据库表自动创建了,大功告成。
通过泛型、注解、反射这些技巧。
代码在这里github
网友评论