手写数据库框架

论坛 期权论坛 脚本     
已经匿名di用户   2022-4-26 15:51   4069   0

目录

前言

android发展至今开源框架很多,数据库框架也有一些比较好的,而对于我们开发者而言网上有很多框架,我们只需要拿过来会用,一般都可以解决项目中遇到的问题,而我们自己写的代码量就比较少了,也很难突破开发瓶颈,开发模式变成了Ctrl+C Ctrl+V,而依赖过来的jar包,有些未必是项目需要用到的,很多冗余代码量较大的时候,间接的导致app变大,所以这个时候就需要我们手写代码来解决这一问题,今天写一下数据库框架。

正文部分

1.首先新建一个AS项目工程,建立一个顶层数据库接口IBaseDao 这里定义一些需要用的方法,最基础的增删改查之类的,让实现类去调用,insert方法是需要插入的对象,当然我们需要插入的对象应该是由用户去决定,所以这里用范型,用户传入插入对象即可

public interface IBaseDao<T>{

    /** 插入操作 */
    long insert(T entity);

    /** 删除操作 */
    int delete(String condition,String[] value);

}

2.再建立一个实现类去实现上面定义的接口,而我们需要操作数据库,这里就会用到android提供的一个数据库操作类

SQLiteDatabase是必不可少的,在框架层操作外层对象的方法用 Class<T> 获取当前的字节码 然后建立一个表名  设置缓存,在初始化方法里建立表,那我们如何去获取到表得名字呢,可以通过java的反射机制,首先建立一个对象,例如User用户信息,User类(这里跳到User代码继续阅读,然后再返回到当前位置)

回到这里时候,已经解决了获取到表名和对象字段的方法,建立表的具体代码逻辑可断点一步步看下去

public class BaseDao<T> implements IBaseDao<T>{
    //操作数据库,持有数据库操作的引用
    private SQLiteDatabase sqLiteDatabase;
    //持有操作数据库所对应的java类型
    private Class<T> entityClass;
    //表名
    private String tableName;
    //标记,用来是否已经存在
    private boolean isInit = false;

    //定义一个缓存空间(key - 字段名 )
    private HashMap<String,Field> cacheMap;

    public boolean init(SQLiteDatabase sqLiteDatabase, Class<T> entityClass) {
        this.sqLiteDatabase = sqLiteDatabase;
        this.entityClass = entityClass;
        //自动建表(只需要建一次)
        if (!isInit){
            //如果没有建过表就建一张新表
            tableName=entityClass.getAnnotation(DBTable.class).value();
            if (!sqLiteDatabase.isOpen()){
                return false;
            }
            //执行自动建表的动作
            String creatTableSql = getCreateTableSql();
            sqLiteDatabase.execSQL(creatTableSql);
            isInit = true;

            //初始化缓存空间
            cacheMap = new HashMap<>();
            initCacheMap();
        }
        return isInit;
    }

    private void initCacheMap() {
        //1.取到所有得列表
        String sql = "select * from " + tableName +" limit 1,0";
        Cursor cursor = sqLiteDatabase.rawQuery(sql,null);
        String[] columnNames = cursor.getColumnNames();
        //2.取所有得成员变量
        Field[] clounmnFields = entityClass.getDeclaredFields();
        //3.通过2层循环让他们对应起来
        for (String columnName: columnNames){
            Field resultField = null;
            for (Field field:clounmnFields){
                String fieldAnnotionName = field.getAnnotation(DBField.class).value();
                if (columnName.equals(fieldAnnotionName)){
                    resultField = field;
                    break;
                }
            }
            if (resultField != null){
                cacheMap.put(columnName,resultField);
            }
        }
    }

    /***
     * 自动创建表
     * @return
     */
    private String getCreateTableSql() {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("create table if not exists ");
        stringBuffer.append(tableName+"(");
        //反射得到所有的成员变量
        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 == 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;
            }
        }
        if (stringBuffer.charAt(stringBuffer.length() - 1) == ','){
            stringBuffer.deleteCharAt(stringBuffer.length() - 1);
        }
        stringBuffer.append(")");
        return stringBuffer.toString();
    }

    /**
     *
     * @param entity
     * @return
     */
    @Override
    public long insert(T entity) {
        //1.准备好ContentValues
        Map<String,String> map = getValues(entity);
        //2.插入内容
        ContentValues values = getContentValues(map);
        //3.执行插入
        long result = sqLiteDatabase.insert(tableName,null,values);
        return result;
    }

    /**
     * 删除操作
     * @param condition
     * @return
     */
    @Override
    public int delete(String condition,String[] value) {
        sqLiteDatabase.delete(tableName,condition,value);
        return 0;
    }


    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(T entity) {
        HashMap<String,String> map = new HashMap<>();
        Iterator<Field> fieldItertor = cacheMap.values().iterator();
        while (fieldItertor.hasNext()){
            Field field = fieldItertor.next();
            field.setAccessible(true);
            //获取变量的值
            try {
                Object object = field.get(entity);
                if (object == null){
                    continue;
                }
                String value = object.toString();
                //获取别名 _id name password
                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;
    }
}

这里是User对象,大家会发现这里有个@DBTable,而这个是用来反射表名使用的,如下所示,@Target是注解代表标示的位置,参数有很多,可以对应使用,这里把他写在类上的,所以使用ElementType.TYPE,第二个再设置它的保留时间,@Retention,这里选择的是RetentionPolicy.RUNTIME,表示运行时,通过DBTable反射机制可以得到表名,而我们需要获取对象信息里所有字段的话,也需要同样方式,这里就继续建立一个DBField,这些工作做完之后,返回上面继续BaseDao类代码

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBField {
    String value();
}
@DBTable("tb_user")
public class User {
    @DBField("id")
    private Integer id;
    @DBField("name")
    private String name;
    @DBField("password")
    private String password;

    public User(Integer id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {

    String value();

}

DBField

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBField {
    String value();
}

最终的调用方式:

BaseDao<User> userDao = BaseDaoFactoty.getOurInstance().getBaseDao(User.class);
userDao.insert(new User(1,".","hahaha"));

demo 地址如下:https://github.com/w947329887/-

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:81
帖子:4969
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP