博客爲 有時個哥 原創,如需轉載請標明出處: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