本文主要介绍一下类似华为使用的面向对象的数据库框架,效果如图:
就是在使用数据库的时候可以直接通过传入一个实体类的方式去创建一张表,并且进行赋值操作,当然里边会用到对实体类的反射机制和自定义的注解模式,核心代码如下:
创建数据库顶层接口
/**
* 数据库顶层接口
* @author YTF
*/
public interface IBaseData<T> {
/**
*插入操作
* 我们这里要实现的就是可以直接插入一个实体类就可以自动在指定位置创建对应的数据库表文件
*/
long insert(T entity);
// update,delete,select等均可在此处扩展
}
顶层接口IBaseData的实现类
package com.database.ytf.db_database.sqlite;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.text.TextUtils;
import com.database.ytf.db_database.sqlite.annotations.DbField;
import com.database.ytf.db_database.sqlite.annotations.DbTable;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* 顶层接口IBaseData的实现类,就也是Dao层
*/
public class BaseDao<T> implements IBaseData {
// 持有一个操作数据库的引用
private SQLiteDatabase sqLiteDatabase;
// 持有一个操作数据可对应的Java类型
private Class<T> entityClass;
// 表名(由框架来控制)
private String tabName;
// 标记是否已经创建过数据库,保证只创建一次
private boolean flag;
// 缓存,key==字段名,value==成员变量
private HashMap<String,Field> cacheMap;
/**
* 数据库初始化操作(自动建表)
* BaseDao是有框架层使用,不是调用者使用,所以不用构造方法
*
* @param sqLiteDatabase
* @param entityClass
* @return
*/
public boolean init(SQLiteDatabase sqLiteDatabase, Class<T> entityClass) {
this.sqLiteDatabase = sqLiteDatabase;
this.entityClass = entityClass;
if (!flag) {//保证只初始化一次
// 自动建表 拿到表名
tabName = entityClass.getAnnotation(DbTable.class).value();
if (!sqLiteDatabase.isOpen()) {
return false;
}
// 开始建表 creat table if not exists tb_user(_id integer ,name varchar(20),pwd varchar(20))
String sql = getCreatTabSql();
sqLiteDatabase.execSQL(sql);
cacheMap=new HashMap<>();
// 初始化缓存
initacheMap();
flag=true;
}
return flag;
}
private void initacheMap() {
// 找到所有的字段名
String sql="select * from "+tabName+" limit 1,0";
Cursor cursor=sqLiteDatabase.rawQuery(sql,null);
String[] columnNames=cursor.getColumnNames();
// 找到所有的成员变量,使用反射
Field[] columnFields=entityClass.getDeclaredFields();
// 通过循环把对应关系存入到缓存
for (String columnName:
columnNames) {
Field resultField=null;
for (Field field:
columnFields) {
String fieldAnnotionName=field.getAnnotation(DbField.class).value();
if (columnName.equals(fieldAnnotionName)){
resultField=field;
break;
}
}
if (resultField!=null){
cacheMap.put(columnName,resultField);
}
}
}
@Override
public long insert(Object entity) {
// ContentValues values=new ContentValues();
// values.put("_id",110);
// values.put("name","ytf");
// 先要得到键值对并缓存成员变量名字,避免大量反射影响性能
Map<String,String> map=getValues( entity);
ContentValues values= getContentValues(map);
long result=sqLiteDatabase.insert(tabName,null,values);
return result;
}
private ContentValues getContentValues(Map<String, String> map) {
ContentValues contentValues=new ContentValues();
Set keys=map.keySet();
Iterator<String> iterator=keys.iterator();
while (iterator.hasNext()){
String key=iterator.next();
String value=map.get(key);
if (value!=null){
contentValues.put(key,value);
}
}
return contentValues;
}
private Map<String,String> getValues(Object entity) {
HashMap<String,String> map=new HashMap<>();
Iterator<Field> fieldIterator=cacheMap.values().iterator();
while (fieldIterator.hasNext()){
Field field=fieldIterator.next();
field.setAccessible(true);
// 获取成员变量的值
Object obj=null;
try {
obj=field.get(entity);//取到变量中的值
if (obj==null){
continue;
}
String value=obj.toString();
// 取列名
String key=field.getAnnotation(DbField.class).value();
if (!TextUtils.isEmpty(key)&&!TextUtils.isEmpty(value)){
map.put(key,value);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return map;
}
public String getCreatTabSql() {
StringBuffer stringBuffer = new StringBuffer();
// creat table if not exists tb_user(_id integer ,name varchar(20),pwd varchar(20))
stringBuffer.append("create table if not exists ");
stringBuffer.append(tabName + "(");
// 通过反射拿到调用层实体类中的所有字段(成员变量)
Field[] fields = entityClass.getDeclaredFields();
for (Field field :
fields) {
Class type = field.getType();
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 == Double.class) {
stringBuffer.append(field.getAnnotation(DbField.class).value() + "DOUBLE,");
} else if (type == Long.class) {
stringBuffer.append(field.getAnnotation(DbField.class).value() + "LONG,");
} else if (type == byte[].class) {//图片格式
stringBuffer.append(field.getAnnotation(DbField.class).value() + "BLOB,");
} else {
// 不支持的类型
continue;
}
}
// 去掉最后一个逗号
if (stringBuffer.charAt(stringBuffer.length() - 1) == ',') {
stringBuffer.deleteCharAt(stringBuffer.length() - 1);
}
stringBuffer.append(")");
return stringBuffer.toString();
}
}
#
package com.database.ytf.db_database.sqlite.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解
* @author YTF
* java 源文件 --》class --运行runtime
* @Target 规定该注解要在哪里使用,这里是要在类中使用,所以使用TYPE
* @Retention 规定该注解的存活时间,这里设置为运行时RUNTIME(因为建表操作是在程序运行时)
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbField {
// 定义从实体类中拿到变量名字符串的方法
String value();
}
package com.database.ytf.db_database.sqlite.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解
* @author YTF
* java 源文件 --》class --运行runtime
* @Target 规定该注解要在哪里使用,这里是要在类中使用,所以使用TYPE
* @Retention 规定该注解的存活时间,这里设置为运行时RUNTIME(因为建表操作是在程序运行时)
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbTable {
// 定义从实体类中拿到变量名字符串的方法
String value();
}
使用自定义注解的实体类
package com.database.ytf.db_database;
import com.database.ytf.db_database.sqlite.annotations.DbField;
import com.database.ytf.db_database.sqlite.annotations.DbTable;
/**
* 对是实体类使用自定义注解,通过反射拿到实体类中的变量
* 将变量自动创建到表中作为表的字段名
*/
@DbTable("tb_user")//表名
public class User {
@DbField("_id")//字段名
private Integer id;
@DbField("name")
private String name;
@DbField("pwd")
private String password;
public User(Integer id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
提供给调用层使用的对外接口
package com.database.ytf.db_database.sqlite;
import android.database.sqlite.SQLiteDatabase;
/**
* @author YTF
* 提供给调用层使用的对外接口
*/
public class BaseDaoFactory {
private SQLiteDatabase sqLiteDatabase;
private String sqliteDataBasePath;
private static final BaseDaoFactory instance=new BaseDaoFactory();
public static BaseDaoFactory getInstance(){
return instance;
}
private BaseDaoFactory(){
// 初始化数据库路径
sqliteDataBasePath="data/data/com.database.ytf.db_database/ytf.db";
sqLiteDatabase= SQLiteDatabase.openOrCreateDatabase(sqliteDataBasePath,null);
}
public <T> BaseDao<T> getBaseDao(Class<T> entityClass){
BaseDao baseDao=null;
try {
baseDao=BaseDao.class.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
baseDao.init(sqLiteDatabase,entityClass);
return baseDao;
}
}
使用示例:
public void click(View view) {
BaseDao<User> dao= BaseDaoFactory.getInstance().getBaseDao(User.class);
dao.insert(new User(100,"ytf","123456"));
Toast.makeText(this, "建表成功", Toast.LENGTH_SHORT).show();
}
效果验证:
写在最后
深夜发文,打字不易,写的不好,烦请见谅。
GitHub源码地址:源码