红橙Darren视频笔记 条件查询 删除 更新,数据库结合http做缓存机制(数据库操作)下

查询 更新 删除的实现

修改IDaoSupport接口部分 删除查询部分 增加返回查询支持类接口 增加删除更新的接口

    // 获取专门查询的支持类 按照语句查询
    QuerySupport<T> querySupport();

    // 按照语句删除
    int delete(String whereClause, String... whereArgs);
    // 按照语句更新
    int update(T obj, String whereClause, String... whereArgs);

DaoSupport是实现部分
删除查询部分 实现上面新增的接口

    private QuerySupport<T> mQuerySupport = null;
    @Override
    public QuerySupport<T> querySupport() {
        if (mQuerySupport == null) {
            mQuerySupport = new QuerySupport<>(mSqLiteDatabase, mClazz);
        }
        return mQuerySupport;
    }

    @Override
    public int delete(String whereClause, String[] whereArgs) {
        return mSqLiteDatabase.delete(DaoUtil.getTableName(mClazz), whereClause, whereArgs);
    }

    @Override
    public int update(T obj, String whereClause, String... whereArgs) {
        ContentValues values = contentValuesByObj(obj);
        return mSqLiteDatabase.update(DaoUtil.getTableName(mClazz),
                values, whereClause, whereArgs);
    }

因为按条件查询 分组 排序等查询的操作稍微复杂 所以单独为查询做了一个支持类 其实也比较简单 主要用的就是builder设计模式

/**
 * Created by hjcai on 2021/3/25.
 * 专门用来查询的类
 * 利用builder设计模式 设置各种参数
 */
public class QuerySupport<T> {

    private static final String TAG = "QuerySupport";
    // 查询的列
    private String[] mQueryColumns;
    // 查询的条件
    private String mQuerySelection;
    // 查询的参数
    private String[] mQuerySelectionArgs;
    // 查询分组
    private String mQueryGroupBy;
    // 查询对结果集进行过滤
    private String mQueryHaving;
    // 查询排序
    private String mQueryOrderBy;
    // 查询可用于分页
    private String mQueryLimit;

    private final Class<T> mClazz;
    private final SQLiteDatabase mSQLiteDatabase;

    public QuerySupport(SQLiteDatabase sqLiteDatabase, Class<T> clazz) {
        this.mClazz = clazz;
        this.mSQLiteDatabase = sqLiteDatabase;
    }

    public QuerySupport<T> columns(String... columns) {
        this.mQueryColumns = columns;
        return this;
    }

    public QuerySupport<T> selectionArgs(String... selectionArgs) {
        this.mQuerySelectionArgs = selectionArgs;
        return this;
    }

    public QuerySupport<T> having(String having) {
        this.mQueryHaving = having;
        return this;
    }

    public QuerySupport<T> orderBy(String orderBy) {
        this.mQueryOrderBy = orderBy;
        return this;
    }

    public QuerySupport<T> limit(String limit) {
        this.mQueryLimit = limit;
        return this;
    }

    public QuerySupport<T> groupBy(String groupBy) {
        this.mQueryGroupBy = groupBy;
        return this;
    }

    public QuerySupport<T> selection(String selection) {
        this.mQuerySelection = selection;
        return this;
    }

    public List<T> query() {
        Cursor cursor = mSQLiteDatabase.query(DaoUtil.getTableName(mClazz), mQueryColumns, mQuerySelection,
                mQuerySelectionArgs, mQueryGroupBy, mQueryHaving, mQueryOrderBy, mQueryLimit);
        clearQueryParams();
        return cursorToListByReflect(cursor);
    }

    private void clearQueryParams() {
        mQueryColumns = null;
        mQuerySelection = null;
        mQuerySelectionArgs = null;
        mQueryGroupBy = null;
        mQueryHaving = null;
        mQueryOrderBy = null;
        mQueryLimit = null;
    }

    // 多次调用反射 查询所有列表
    private List<T> cursorToListByReflect(Cursor cursor) {
        List<T> list = new ArrayList<>();
        while (cursor.moveToNext()) {
            // 通过反射创建T对象 第一次反射 以Person为例 相当于调用Person p = new Person()
            T instance = null;// 要调用此方法 必须有无参构造方法
            try {
                instance = mClazz.newInstance();
                // 反射获取T对象所有的属性 第二次反射
                Field[] fields = mClazz.getDeclaredFields();
                // 遍历field 从数据库取出数据填充到instance
                for (Field field : fields) {
                    Object value = null;
                    field.setAccessible(true);
                    // 以Person为例 它有个属性String name, fieldName 则是 name
                    String fieldName = field.getName();
                    // 查询当前属性在数据库中所在的列 下面则相当于调用cursor.getColumnIndex("name")
                    int index = cursor.getColumnIndex(fieldName);
                    if (index != -1) {
                        // 根据对象T的属性类型推算cursor的方法 如cursor.getString cursor.getInt
                        Method cursorGetColumnMethod = convertType2CursorMethod(field.getType());// 在该方法进行第三次反射
                        // 通过反射获取 value 第四次反射 相当于cursor.getString(cursor.getColumnIndex("name"))//cursor.getColumnIndex("name")在上面已经调用
                        value = cursorGetColumnMethod.invoke(cursor, index);
                        if (value == null) {
                            continue;
                        }
                        // 处理一些特殊的部分
                        if (field.getType() == boolean.class || field.getType() == Boolean.class) {
                            // sqlite不支持bool类型 使用0代表false 1代表true
                            if ("0".equals(String.valueOf(value))) {
                                value = false;
                            } else if ("1".equals(String.valueOf(value))) {
                                value = true;
                            }
                        } else if (field.getType() == char.class || field.getType() == Character.class) {
                            // sqlite不支持char类型 取第0位即可
                            value = ((String) value).charAt(0);
                        } else if (field.getType() == Date.class) {
                            // sqlite不支持Date类型 存储的是时间戳
                            long date = (Long) value;
                            if (date <= 0) {
                                value = null;
                            } else {
                                value = new Date(date);
                            }
                        }
                    } else {
                        Log.e(TAG, "cursorToList: 该属性没有存储在数据库中");
                        continue;
                    }
                    // 反射注入属性的值(以person为例,类似调用person.setName(value))
                    // 第五次反射
                    field.set(instance, value);
                }
            } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e) {
                e.printStackTrace();
            }
            list.add(instance);
        }
        cursor.close();
        Log.e(TAG, "cursorToList: " + list.size());
        return list;
    }

    // 获取cursor的方法
    private Method convertType2CursorMethod(Class<?> type) throws NoSuchMethodException {
        // 根据数据类型 得到不同cursor方法
        String methodName = getColumnMethodName(type);
        // 第三次反射 根据方法名和参数类型调用
        return Cursor.class.getMethod(methodName, int.class);
    }

    // 根据数据类型 得到不同cursor方法 如getInt getString
    private String getColumnMethodName(Class<?> fieldType) {
        String typeName;
        if (fieldType.isPrimitive()) { // 如果是基本数据类
            // 将int boolean float等转换为对象的形式 即首字母大写
            /*
             * @see     java.lang.Boolean#TYPE
             * @see     java.lang.Character#TYPE
             * @see     java.lang.Byte#TYPE
             * @see     java.lang.Short#TYPE
             * @see     java.lang.Integer#TYPE
             * @see     java.lang.Long#TYPE
             * @see     java.lang.Float#TYPE
             * @see     java.lang.Double#TYPE
             * @see     java.lang.Void#TYPE
             */
            typeName = DaoUtil.capitalize(fieldType.getName());
        } else {
            typeName = fieldType.getSimpleName();
        }
        // 上面获取对象T的Java的get方法,如Integer String Boolean 下面需要转成SQLite里面的数据类型
        // 如getBoolen转换为数据库的getInt getChar转换为数据库的getString
        String methodName = "get" + typeName;
        switch (methodName) {
            case "getBoolean":
            case "getInteger":
                methodName = "getInt";
                break;
            case "getChar":
            case "getCharacter":
                methodName = "getString";
                break;
            case "getDate":
                methodName = "getLong";
                break;
        }
        return methodName;
    }
}

其实上面主要代码都是从DaoSupport中挪过来的 新增部分主要是builder设计模式。这里的条件查询有很多种 但实际上也不难,只要常用的几个理解就OK 比如group by,having等复杂的条件查询 暂时不理解也没什么问题,一般用到的情况比较少
当然 我认为还可以增加一个接口用于直接调用SQL语句 对于复杂的数据库操作,还是直接执行sql语句比较清楚
以下有个链接可以方便了解数据库的一些语句
https://www.w3school.com.cn/sql/sql_groupby.asp
最后修改和增加几个测试方法

    private void deleteByArgs(View view) {
        QuerySupport<Person> querySupport = DaoSupportFactory.getFactoryInstance(MainActivity.this).getDao(Person.class).querySupport();
        List<Person> persons = querySupport.query();
        Log.e(TAG, "删除前所有数据: "+persons);
        // 删除所有名字为hjcai的
        DaoSupportFactory.getFactoryInstance(MainActivity.this).getDao(Person.class).delete("name =?","hjcai");

        Log.e(TAG, "删除后所有数据: "+DaoSupportFactory.getFactoryInstance(MainActivity.this).getDao(Person.class).querySupport().query());
    }

    private void update(View view) {
        new Thread(() -> {
            QuerySupport<Person> querySupport = DaoSupportFactory.getFactoryInstance(MainActivity.this).getDao(Person.class).querySupport();
            List<Person> persons = querySupport.orderBy("age").query();
            Log.e(TAG, "更新前: "+persons);
            if ( persons != null && persons.size() >0){
                Person person = persons.get(0);
                // 把所有name=hjcai的 更新为age最小的样子
                DaoSupportFactory.getFactoryInstance(MainActivity.this).getDao(Person.class).update(person,"name = ?","hjcai");
                Log.e(TAG, " 更新后 -> " + querySupport.query());
            }

        }).start();
    }

    private void queryAll(View view) {
        new Thread(() -> {
            QuerySupport<Person> querySupport = DaoSupportFactory.getFactoryInstance(MainActivity.this).getDao(Person.class).querySupport();
//            List<Person> people = querySupport.selection("name = ? AND age = ?").selectionArgs("hjcai","0").query();
//            List<Person> people = querySupport.selection("name = ?").selectionArgs("hjcai").columns("name","age").query();
//            List<Person> people = querySupport.selection("name = ?").selectionArgs("hjcai").query();
//            List<Person> people = querySupport.selection("name = ?").selectionArgs("hjcai").limit("2").query();
//            List<Person> people = querySupport.groupBy("name").query();
            // 查询所有
            List<Person> people = querySupport.query();
            Log.e(TAG, "queryAll: " + people);
        }).start();
    }

网络结合数据库进行缓存

由于内涵段子的服务器已经不再可用,这里只能说说思路 没法测试了
此次demo在之前的数据库和网络相关的文章的demo基础上修改 分别是
https://blog.csdn.net/u011109881/article/details/114703664
https://blog.csdn.net/u011109881/article/details/114948968
https://blog.csdn.net/u011109881/article/details/115216756
修改点
对IHttpEngine接口 怎加缓存参数

    // get请求
    void get(boolean needCache, Context context, String url, Map<String, Object> params, EngineCallBack callBack);

    // post请求
    void post(boolean needCache, Context context, String url, Map<String, Object> params, EngineCallBack callBack);

同样HttpUtils也要加参数

    private boolean mNeedCache;
    // 是否缓存
    public HttpUtils cache(boolean needCache) {
        this.mNeedCache = needCache;
        return this;
    }

    private void get(String url, Map<String, Object> params, EngineCallBack callBack) {
        mHttpEngine.get(mNeedCache, mContext, url, params, callBack);
    }

    private void post(String url, Map<String, Object> params, EngineCallBack callBack) {
        mHttpEngine.post(mNeedCache,mContext, url, params, callBack);
    }

增加缓存数据结构

public class CacheData {
    // 请求链接
    private String mUrlKey;

    // 后台返回数据的Json
    private String mResultJson;

    public CacheData() {

    }

    public CacheData(String urlKey, String resultJson) {
        this.mUrlKey = urlKey;
        this.mResultJson = resultJson;
    }

    public String getResultJson() {
        return mResultJson;
    }
}

剩下的就是修改http的get post方法了 这里以get为例

    @Override
    public void get(boolean needCache, Context context, String urlPara, Map<String, Object> params, EngineCallBack callBack) {
        final String finalUrl = HttpUtils.jointParams(urlPara, params);
        // 先查询数据库看是否有缓存
        final List<CacheData> cacheData = DaoSupportFactory.getFactoryInstance(context).getDao(CacheData.class)
                .querySupport().selection("mUrlKey=?").selection(finalUrl).query();

        if (cacheData != null && cacheData.size() > 0) {
            // 数据库有缓存
            CacheData cacheDatum = cacheData.get(0);
            Log.e(TAG, "读取到缓存" + cacheDatum);
            callBack.onSuccess(cacheDatum.getResultJson());
        }
        Log.e(TAG, "Get请求路径:" + finalUrl);
        Request request = new Request
                .Builder()
                .url(finalUrl)
                .tag(context)
                .build();
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                callBack.onError(e);
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                if (response.body() == null) {
                    Log.e(TAG, "Get返回结果是null");
                    return;
                }
                String resultJson = response.body().string();
                if (cacheData != null && cacheData.size() > 0) {
                    CacheData cacheDatum = cacheData.get(0);
                    if (!TextUtils.isEmpty(resultJson) && cacheDatum != null && resultJson.equals(cacheDatum.getResultJson())) {
                        // 缓存与网络查询结果相等 直接使用缓存
                        return;
                    } else {
                        if (needCache) {
                            //更新缓存
                            DaoSupportFactory.getFactoryInstance(context).getDao(CacheData.class)
                                    .delete("mUrlKey=?", finalUrl);
                            DaoSupportFactory.getFactoryInstance(context).getDao(CacheData.class)
                                    .insert(new CacheData(finalUrl, resultJson));
                        }
                    }
                }
                callBack.onSuccess(resultJson);
                Log.e("Get返回结果:", resultJson);
            }
        });
    }

具体逻辑无非是下图
在这里插入图片描述
最后 存入数据库时一些特殊字符可能会存不进去 可以用md5进行加密然后存入数据库,当然取数据时也需要先将url进行加密才能读取到正确的数据。

    public static String string2MD5(String inStr) {
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (Exception e) {
            System.out.println(e.toString());
            e.printStackTrace();
            return "";
        }
        char[] charArray = inStr.toCharArray();
        byte[] byteArray = new byte[charArray.length];

        for (int i = 0; i < charArray.length; i++)
            byteArray[i] = (byte) charArray[i];
        byte[] md5Bytes = md5.digest(byteArray);
        StringBuilder hexValue = new StringBuilder();
        for (byte md5Byte : md5Bytes) {
            int val = ((int) md5Byte) & 0xff;
            if (val < 16)
                hexValue.append("0");
            hexValue.append(Integer.toHexString(val));
        }
        return hexValue.toString();
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值