红橙Darren视频笔记 缓存方案 缓存到数据库(数据库操作) 上

1.涉及知识

反射 工厂模式 单例
关于工厂模式:抽象工厂用于创建相对复杂的对象,适用于对象包含很多零件或生产大批对象的情景
关于泛型:
泛型方法

public class Test {
	public static <T> T init(String s, Class<T> clazz){
		s = clazz.getSimpleName();
	    System.out.println("s "+s);
		return null;
	}
	
	class Person{
	}

	public static void main(String[] args) {
		init(new String(), Person.class);
	}

}

输出

s Person
public static <T> T init(String s, Class<T> clazz)

<T>是声明泛型. 第二个T代表返回对象类型为T .Class<T> clazz代表传入参数是T的class
泛型类的使用

public interface IDaoSupport<T>{
}

一般泛型类/接口在类名之后就会加上 这里是泛型的声明 之后即可在类中是泛型T

2.缓存方案

我们知道访问数据库 访问网络都是比较耗时的,但是一般由于数据量较大,不可能全部缓存到本地,因此访问网络和数据库有些情况还是必不可少的,那么我们应该尽量减少从用户操作到用户看到返回结果之间的时间
为了提高搜索效率 ,有一些优化缓存的方案
在这里插入图片描述

当然现在一些http框架还会自带缓存,比如OKHttp 针对不同的网络框架,可能有不同的缓存方案。但是我们设计的缓存方案,应该是尽量通用的。
另外,缓存也可以设置定期清除的策略,避免空间被无用数据占用太多的空间
我们本节的代码主要是上面黑色箭头的第一步:查看本地是否有缓存的部分,说的简单一点,其实就是数据库的CURD
下面我们就进入数据库操作的学习

3.类结构

在这里插入图片描述

4.代码展示

Model类

public class Person {

    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

数据库支持类接口

public interface IDaoSupport<T> {
    // 初始化数据库表(Class<T> clazz代表T所对应的类)
    void init(SQLiteDatabase sqLiteDatabase, Class<T> clazz);

    // 数据库插入数据
    public long inert(T t);

    public void deleteAll();

}

辅助类

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

    // 获取数据库表名
    public static String getTableName(Class<?> clazz) {
        return clazz.getSimpleName();
    }

    // 将java中的基本数据类型翻译为SQL语句中的数据类型
    // 此方法只适用于属性中的基本类型 如果存在复杂类型(如User Student)等 则不适用该方法
    public static String getColumnType(String type) {
        String value = null;
        if (type.contains("String")) {
            value = " text";
        } else if (type.contains("int")) {
            value = " integer";
        } else if (type.contains("boolean")) {
            value = " boolean";
        } else if (type.contains("float")) {
            value = " float";
        } else if (type.contains("double")) {
            value = " double";
        } else if (type.contains("char")) {
            value = " varchar";
        } else if (type.contains("long")) {
            value = " long";
        } else {
            Log.e(TAG, "getColumnType: invalid type!");
        }
        return value;
    }
}

数据库支持类实现类

class DaoSupport<T> implements IDaoSupport<T> {
    // 数据库对象
    private SQLiteDatabase mSqLiteDatabase;
    // 数据库需要操作的 表中存储的 对象类型
    private Class<T> mClazz;
    private String TAG = "DaoSupport";

    public DaoSupport(SQLiteDatabase sqLiteDatabase, Class<T> clazz) {
        init(sqLiteDatabase, clazz);
    }

    @Override
    public void init(SQLiteDatabase sqLiteDatabase, Class<T> clazz) {
        this.mSqLiteDatabase = sqLiteDatabase;
        this.mClazz = clazz;
        // 创建表的sql语句
        /*create table if not exists Person (id integer primary key autoincrement, name text, age integer, flag boolean)*/
        StringBuilder sb = new StringBuilder();
        sb.append("create table if not exists ")
                .append(DaoUtil.getTableName(mClazz))// 表名为类的名字
                .append("(id integer primary key autoincrement, ");// id 为 int的主键 自增长
        // 通过反射获取类中的属性
        Field[] fields = mClazz.getDeclaredFields();
        for (Field field : fields) {//遍历属性
            field.setAccessible(true);// 设置权限
            String name = field.getName();
            String type = field.getType().getSimpleName();// int String boolean
            //  type需要进行转换 int --> integer, String text;
            sb.append(name).append(DaoUtil.getColumnType(type)).append(", ");
        }
        // 将插入语句的最后两位", "替换为")"
        sb.replace(sb.length() - 2, sb.length(), ")");
        String createTableSql = sb.toString();
        Log.e(TAG, "建表语句--> " + createTableSql);
        // 执行建表语句
        mSqLiteDatabase.execSQL(createTableSql);
    }

    // 插入数据库 类型为任意类型
    @Override
    public long inert(T t) {
        /*
        通常我们可能直接调用
        ContentValues values = new ContentValues();
        values.put("name","wz");
        values.put("author","xx");
        values.put("price",1.0);
        db.insert("Book",null,values);

        但是这样其实不方便 如果我们删除或增加Book.java的字段 那么这段代码需要修改
        并且 对于不同的对象 我们还需要写不同的插入逻辑,利用反射 就可以直接绕开这两个问题
        因此 这里我们使用了反射
        */
        ContentValues values = contentValuesByObj(t);

        return mSqLiteDatabase.insert(DaoUtil.getTableName(mClazz), null, values);
    }

    @Override
    public void deleteAll() {
        mSqLiteDatabase.execSQL("DROP TABLE " + DaoUtil.getTableName(mClazz));
        //mSqLiteDatabase.execSQL("DELETE FROM " + DaoUtil.getTableName(mClazz));
    }

    // 查询

    // 修改

    // 删除

    // obj 转成 ContentValues
    // ContentValues实际作用类似与hashMap 只不过它的value只能push基本类型
    private ContentValues contentValuesByObj(T obj) {
        ContentValues contentValues = new ContentValues();
        // 通过反射获取mClazz定义的filed(以Person为例 返回的是age 和 name字段)
        Field[] fields = mClazz.getDeclaredFields();
        for (Field field : fields) {
            Method putMethod;
            try {
                // 设置权限,私有和共有都可以访问
                field.setAccessible(true);
                // 获取field的名称(如age)
                String key = field.getName();
                // 获取field的value(如30)
                Object value = field.get(obj);

                // 虽然使用反射会有一点性能的影响 但是影响很小
                // 而且源码里面  activity实例的创建 View创建反射等都使用了反射
                // 因此这里也会使用反射 获取put方法
                // (如ContentValues.class.getDeclaredMethod("put",String.class, java.lang.Integer))
                // 代表希望调用的是put(String key, Integer value)的方法
                putMethod = ContentValues.class.getDeclaredMethod("put",
                        String.class, value.getClass());
                // 通过反射执行ContentValues的putXXX方法
                // 相当于调用类似 contentValues.put("age",30);
                putMethod.invoke(contentValues, key, value);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return contentValues;
    }
}

工厂类

public class DaoSupportFactory {
    private static final String TAG = "DaoSupportFactory";
    // 单例对象
    private static DaoSupportFactory mFactoryInstance;

    private final SQLiteDatabase mSqLiteDatabase;

    private DaoSupportFactory(Context context) {
        // 把数据库放到内存卡里面  TODO 没有判断是否有存储卡 没有动态申请权限
        // path:/storage/emulated/0/Android/data/com.example.learneassyjoke/files/nhdz/database
        File dbRoot = new File(context.getExternalFilesDir(null)
                .getAbsolutePath() + File.separator + "nhdz" + File.separator + "database");
        if (!dbRoot.exists()) {
            if (!dbRoot.mkdirs()) {
                Log.e(TAG, "DaoSupportFactory: 创建db路径失败");
            }
            ;
        }

        File dbFile = new File(dbRoot, "nhdz.db");
        // 打开或者创建一个数据库 并存储数据库操作的引用
        Log.e(TAG, "DaoSupportFactory: 创建db路径==>" + dbRoot);
        mSqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(dbFile, null);
    }

    public static DaoSupportFactory getFactoryInstance(Context context) {
        if (mFactoryInstance == null) {
            synchronized (DaoSupportFactory.class) {
                if (mFactoryInstance == null) {
                    mFactoryInstance = new DaoSupportFactory(context);
                }
            }
        }
        return mFactoryInstance;
    }

    // 获取DAO对象 用于操作数据库
    public <T> IDaoSupport<T> getDao(Class<T> clazz) {
        return new DaoSupport<>(mSqLiteDatabase, clazz);
    }
}

测试插入数据

        IDaoSupport<Person> daoSupport = DaoSupportFactory.getFactoryInstance(MainActivity.this).getDao(Person.class);
        // 最少的知识原则
        new Thread(() -> {
            long startTime = System.currentTimeMillis();
            int totalNum = 1000;
            for (int i = 0; i < totalNum; i++) {
                daoSupport.inert(new Person("hjcai", i));
            }
            long endTime = System.currentTimeMillis();
            Log.e(TAG, " insert " + totalNum + " cost time -> " + (endTime - startTime));
        }).start();

测试删表

IDaoSupport<Person> daoSupport = DaoSupportFactory.getFactoryInstance(MainActivity.this).getDao(Person.class);
        daoSupport.deleteAll();

5.主要调用流程

在activity中调用DaoSupportFactory.getFactoryInstance:触发DaoSupportFactory单例的创建 其构造方法中会在指定目录创建数据库nhdz.db(如果有必要)并获取sqlite的操作引用
接着调用getDao:将之前获取的sqlite传入IDaoSupport的实现类DaoSupport DaoSupport自动调用初始化方法,而后利用反射创建表Person,其中的数据字段均由反射得来
测试方法DaoSupport inert则先通过反射将具体的Person实例转换为ContentValues对象(ContentValues实际上就是一个Map 里面有一个个键值对)然后调用数据库操作的引用的insert方法将数据插入,当然我们也可以使用execSQL来执行插入操作
deleteAll则是直接执行删除语句

6.测试结果

2021-03-17 20:23:59.857 8159-8211/com.example.learneassyjoke E/MyActivity:  insert 1000 cost time -> 5206
2021-03-17 20:24:31.277 8227-8251/com.example.learneassyjoke E/MyActivity:  insert 1000 cost time -> 5216
2021-03-17 20:24:44.300 8264-8288/com.example.learneassyjoke E/MyActivity:  insert 1000 cost time -> 5976
2021-03-17 20:25:00.142 8300-8324/com.example.learneassyjoke E/MyActivity:  insert 1000 cost time -> 5776

可以看到平均插入1000条数据耗时5.7s左右,耗时还是比较大的
下一节将讨论如何进行数据库插入的优化

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值