Android数据库框架---------- 注解加反射,构建简单Sql数据库框架

博客为 有时个哥 原创,如需转载请标明出处:https://blog.csdn.net/ls703/article/details/80605758

有时公司不让使用三方数据库框架,自己又不想一遍一遍的写增删改查。于是出于懒惰,就基于反射加注解,写了一个简单的数据库框架。

一 、先来看看使用。

1) 首相要对要进行增删改查的数据对象进行注解配置,如下以Book类为例:

@Table(name="booktwo")
public class Book {
	
	@PrimaryKey
	@Column(name="b_id")
	public String bookid;
	@Column(name="b_name",length=500)
	public String bookname;
	@Column(name="time_tag",length=500)
	public long time;
	@Column(name="num")
	public Type type;
	@Column
	public boolean isRee;
	
	public Integer time3;
	@Override
	public String toString() {
		return "Book [bookid=" + bookid + ", bookname=" + bookname + ", time=" + time + ", type=" + type + ", isRee="
				+ isRee + ", time3=" + time3 + "]";
	}
	
}

注解就三个,@Table ,@Column,@PrimaryKey, 分别为表, 列, 还有主键。

@Table  是设置表的,name 属性:设置表名;

@Column  是设置列相关的属性, 有name 属性:设置列名; length 属性:设置数据长度; 

@PrimaryKey 是设置此字段为主key。唯一

如果对注解不熟悉的朋友,可先看java注解基础。

可以保存的数据类型包含java基础类型(int,long,String,float, short, enum (枚举)等等);

2)然后再看看框架使用和增删改查的部分简单使用:

初始化设置:

SSLiteConfig config = new SSLiteConfig();
config.setDbName("book.db");
config.setDbVersion(1);
SSLiteDB.getInstance().init(getApplicationContext(), config);

增:

// 单个插入
boolean insert = SSLiteDB.getInstance().getSSlite().insert(book);
//批量插入
SSLiteDB.getInstance().getSSlite().batchInsert(books);

删:

//单个删除
SSLiteDB.getInstance().getSSlite().delete(book);
//批量删除
SSLiteDB.getInstance().getSSlite().batchDelete(books);

改:

//单个修改
SSLiteDB.getInstance().getSSlite().update(book);
//批量修改
SSLiteDB.getInstance().getSSlite().batchUpdate(books);

查:

// 查询
List<Book> findAll = SSLiteDB.getInstance().getSSlite().findAll(Book.class);
看操作的话,用起来很简单。满足了我的懒惰。

3) 再看看使用效率

单条数据操作时间,如下:


批量10001条数据操作效率,如下:


4)看jar整体大小


代码整理数量


二 , 框架代码内部模块

大体是下面的结构模块。


三,思路简单描述

如果我们想实现上面便捷的调用方法,实现增删改查,首先你必须得到对应的数据信息。也就是类的信息。注解可以帮助我们解决这个问题。

所以我们先编写Table ,Culomn, PrimaryPek,三个注解类。因为我们构建sql语句时,是需要知道表名是什么,列名是什么,哪个字段是主键等待。

以下是注解类:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Table{
    String name();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {
	String name() default "";

	int length() default 100;

}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface PrimaryKey {
	  String name() default "";
	  int length() default 100;
}

其中@Target(ElementType.TYPE) 是表示使用在类上,@Target(ElementType.FIELD) 表示使用在字段上。具体的字段意思,上面已经说过了,对注解不太清楚的,可以去找一些java注解方面的东西。

有了注解,然后就是怎样解析注解,然后获取类的相关信息了。

这就需要用到java 的反射了。我们可以通过Book对象的Class,然后利用反射,得到其字段,注解,以及字段的值等等所有信息。

假设Book的class 对象为clazz,则通过 clazz.getAnnotation(Table.class)就可以查看Book类是否有Table的注解。如果返回的是Null,则说明没有,如果不为空,则我们就难道了Book的Table注解类对象。就可以取Table的信息了。比如table.getName();就可以得到表名值了。

对于Culomn注解的获取操作,就多了一步,说先得通过Field[] fields = clazz.getDeclaredFields();获取所有的Book类的字段,然后遍历,通过field.getAnnotation(Column.class);查看是否有Column注解。

PrimaryKey 的获取通Column。

如果对反射不熟悉的可以先看一下java 反射相关的东西。

具体的这块操作已经封装到了AnnoParse类里面了,具体如下,

class AnnoParse {
	private static final String TAG = "AnnoParse";

	/**
	 * 初始化table 的信息
	 * @param clazz
	 */
	public static TableInfo initTableInfo(Class<?> clazz){
	
		Table table = (Table) clazz.getAnnotation(Table.class);
		if(table==null){
			return null;
		}
		TableInfo tableInfo = new TableInfo();
		tableInfo.clazzName = clazz.getName();
		if (TextUtils.isEmpty(table.name())) {
			tableInfo.tableName = clazz.getName();
		} else {
			tableInfo.tableName = table.name();
		}
		
		HashMap<String, ColumnInfo> columnMaps = null;
		HashMap<String, String> feildmaps =  null;
		Field[] fields = clazz.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			Field field = fields[i];
			field.setAccessible(true);
			Column column = field.getAnnotation(Column.class);
			if(column!=null){
				
				// 列信息 
				ColumnInfo columnInfo = new ColumnInfo();
				columnInfo.fieldName = field.getName();
				columnInfo.fieldtype = field.getType().getName();
				
				String columnname = column.name();
				if(columnname==null||"".equals(columnname)){
					columnInfo.columName = field.getName();
				}else{
					columnInfo.columName = columnname;
				}
				
				columnInfo.columLength = column.length();
				
				//数据库 对应的列类型
				columnInfo.dbtype = getType(field.getType(), columnInfo.fieldtype);
				
				if (columnMaps == null) {
					columnMaps = new HashMap<String, ColumnInfo>();
				}
				if (feildmaps == null) {
					feildmaps = new HashMap<String, String>();
				}
				
				columnMaps.put(columnInfo.columName, columnInfo);
				feildmaps.put(columnInfo.fieldName, columnInfo.columName);
				
				PrimaryKey primaryKey = field.getAnnotation(PrimaryKey.class);
				if (primaryKey != null) {
					tableInfo.primaryKey = columnInfo.columName;
					columnInfo.isPrimaryKey = true;
				}
			}
		}
		tableInfo.fieldMap = feildmaps;
		tableInfo.colunmMap = columnMaps;
		return tableInfo;
	}
	
	public static <T> String getTableName(Class<?> clazz) {
		Table table = (Table) clazz.getAnnotation(Table.class);
		if(table==null){
			return null;
		}
		String tableName = null;
		if (TextUtils.isEmpty(table.name())) {
			tableName = clazz.getName();
		} else {
			tableName = table.name();
		}
		return tableName;
	}
	
	
	/**
	 * 通过field的type类型名字,得到存到数据库里对应的数据类型
	 * @param typeName
	 * @return
	 */
	private static DataType getType(Class<?> clazz, String typeName) {
		if (clazz.isEnum()) {
			return DataType.ENUM;
		}else if (long.class.getName().equals(typeName) || Long.class.getName().equals(typeName)) {
			return DataType.LONG;
		} else if (Integer.class.getName().equals(typeName) || int.class.getName().equals(typeName)) {
			return DataType.INTEGER;
		} else if (String.class.getName() == typeName) {
			return DataType.STRING;
		} else if (float.class.getName().equals(typeName) || Float.class.getName().equals(typeName)) {
			return DataType.FLOAT;
		} else if (boolean.class.getName().equals(typeName) || Boolean.class.getName().equals(typeName)) {
			return DataType.BOOLEAN;
		} else if (short.class.getName().equals(typeName) || Short.class.getName().equals(typeName)) {
			return DataType.SHORT;
		} else {
			return DataType.UNKOWN;
		}
	}
}

得到注解的信息之后,就相当于得到了表的信息,所以现在需要检查是否有现在的表,如果没有则需要创建表。创建表就需要我们通过得到的表的信息去拼凑创建表的sql语句了。

对于框架所需要的sql,都封装在了SQLCreator类中。检查 创建表的语句拼凑如下:

private String createTableSql(TableInfo info) {
		if (info == null || info.colunmMap == null || info.colunmMap.keySet() == null) {
			return null;
		}

		StringBuilder sql = new StringBuilder();
		sql.append("CREATE TABLE IF NOT EXISTS ");
		sql.append(info.tableName);
		sql.append(" (");

		Set<String> keySet = info.colunmMap.keySet();
		for (String key : keySet) {
			ColumnInfo columnInfo = info.colunmMap.get(key);
			if (columnInfo == null) {
				continue;
			}
			sql.append(" ");
			sql.append(columnInfo.columName);
			sql.append(" ");
			sql.append(columnInfo.dbtype.getSqlType());

			if (!DataType.INTEGER.getSqlType().equals(columnInfo.dbtype.getSqlType())) {
				if (columnInfo.columLength <= 0) {
					return null;
				}
				sql.append("(");
				sql.append(columnInfo.columLength);
				sql.append(")");
			}

			if (columnInfo.isPrimaryKey) {
				sql.append(" PRIMARY KEY");
			}
			sql.append(",");
		}
		sql.deleteCharAt(sql.length() - 1);
		sql.append(");");
		LLog.d("Create tables sql -> " + sql.toString());
		return sql.toString();

	}

得到创建表的sql语句后,使用sqlite的相关代码执行就可以了,源码里的一些接口设计以及预留扩展就先不说了。

东西也算比较简单,是和练手,也可自己修改在代码中使用,减少代码冗余。

这里为了支持多个表,并且为了表的灵活创建,以及为了操作方便,在做增删改查操作时,都需要执行创建表的sql语句,语句中已经做了判断表是否存在,如果不存在则创建表。

具体的增删改查操作请查看源码。

SSLite源码地址: https://github.com/ls0822/SSliteDB

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值