Room记录搜索记录逻辑思路

记录数据使用ROOM,传递使用ViewModel LiveDataBus,这篇文章主要记录 搜索记录 本地 线上,上传失败,记录本地,网络回复统一上传等逻辑的操作。

目录

首先是设计数据表:

定义DAO操作接口

定义数据库类

Mvvm 模式中封装使用Room


首先阐述下搜索记录的要求,后面会逐一针对这些要求使用Room本身能力解决。

  1. 最多记录10条;
  2. 搜索分为点位搜索,和普通搜索。
  3. 列表展示输入搜索的文字,要求文字不可有重复。
  4. 记录分为本地记录和在线记录。

首先是设计数据表:

@Entity(indices = {@Index(value = {"searchKeyword", "searchType"}, unique = true)})
public class SearchHistoryEntity {@PrimaryKey(autoGenerate = true)public int id;public String userId; // 可以为null表示未登录用户public String searchKeyword;public long searchTime;@TypeConverters(Converters.class)public SearchType searchType; // 使用枚举类型public double latitude; // 纬度public double longitude; // 经度public boolean loginStatus; // false: 未登录, true: 已登录}
  • @Entity 表示这是一个 Room 数据库中的表。
  • indices 参数定义了一个组合索引 searchKeyword 和 searchType,并且设置为唯一索引。这意味着在表中,searchKeyword 和 searchType 的组合必须是唯一的。

serchType 是枚举类型,为了区分 普通搜索和点位搜索。

设置searchKeyword 和 searchType 组合必须是唯一的。

以上可以满足第二、三条要求。

定义DAO操作接口

@Dao
public interface SearchHistoryDao {// 插入新的搜索记录,如果发生冲突则替换@Insert(onConflict = OnConflictStrategy.REPLACE)long insertPerson(SearchHistoryEntity entity);@Query("DELETE FROM SearchHistoryEntity WHERE id = (SELECT id FROM SearchHistoryEntity ORDER BY searchTime ASC LIMIT 1)")void deleteOldestSearchHistory();// 删除所有的搜索历史记录@Query("DELETE FROM SearchHistoryEntity")void deleteAllSearchHistory();// 查询所有历史记录@Query("SELECT * FROM SearchHistoryEntity ORDER BY searchTime DESC")public List<SearchHistoryEntity> selectHis();// 查询所有已登录的数据@Query("SELECT * FROM SearchHistoryEntity WHERE userId = :userId")List<SearchHistoryEntity> getSearchHistoryByUserId(String userId);// 查询所有未登录的数据@Query("SELECT * FROM SearchHistoryEntity WHERE loginStatus = 0 ORDER BY searchTime DESC")List<SearchHistoryEntity> getAllLoggedOutData();// 删除所有已登录的数据@Query("DELETE FROM SearchHistoryEntity WHERE loginStatus = 1")void deleteAllLoggedInData();}

这个类其实就是操作数据的工具类,里面最主要需要解释的只有

  • @Insert(onConflict = OnConflictStrategy.REPLACE): 该注解表明该方法用于向数据库插入数据。onConflict 参数设置为 OnConflictStrategy.REPLACE,这意味着如果发生冲突(例如已经存在具有相同主键的行),则用新数据替换现有行。

这样是第二天 第三条的补充,在插入相同搜索的记录的时候覆盖原有记录。

定义数据库类

@Database(entities = {SearchHistoryEntity.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {public abstract SearchHistoryDao searchHistoryDao();private static volatile AppDatabase INSTANCE;private static final int NUMBER_OF_THREADS = 4;static final ExecutorService databaseWriteExecutor = Executors.newFixedThreadPool(NUMBER_OF_THREADS);static AppDatabase getDatabase(final Context context) {if (INSTANCE == null) {synchronized (AppDatabase.class) {if (INSTANCE == null) {INSTANCE = Room.databaseBuilder(context.getApplicationContext(),AppDatabase.class, "app_database").addCallback(sRoomDatabaseCallback).build();}}}return INSTANCE;}private static RoomDatabase.Callback sRoomDatabaseCallback =new RoomDatabase.Callback() {@Overridepublic void onCreate(@NonNull SupportSQLiteDatabase db) {super.onCreate(db);// 创建触发器:确保最多保留10条记录db.execSQL("CREATE TRIGGER limit_search_history_count " +"AFTER INSERT ON SearchHistoryEntity " +"WHEN (SELECT COUNT(*) FROM SearchHistoryEntity) > 10 " +"BEGIN " +"DELETE FROM SearchHistoryEntity WHERE id = (SELECT id FROM SearchHistoryEntity ORDER BY searchTime ASC LIMIT 1); " +"END");}};
}

这里是一个模版代码,其中唯一注意的sRoomDatabaseCallback ,添加一个触发器,当数据超过10条后,按照添加顺序删除最早添加的数据。

其实和DAO接口中定义的 deleteOldestSearchHistory 方法功能是一样的,但是显然触发器的方式更省心。不需要我们每次添加都要去获取一次存储了多少条数据。再去删除。

满足第一条

以上就是Room完整的模版代码,下面才是最主要的第四个条件。

我项目框架是Mvvm,所以操作数据库的代码就放到了 Repository 中,其实没必要,但是咱们尊许模式搞一遍,不在意的话直接使用  db = AppDatabase.getDatabase(this); 即可

Mvvm 模式中封装使用Room

  1. 创建 AppDataBase 抽象类
@TypeConverters(value = {Converters.class})
@Database(entities = {SearchHistoryEntity.class}, version = 1)
public abstract class AppDataBase extends RoomDatabase {public abstract SearchHistoryDao searchHistoryDao();}

对了,差点忘记 Converters 了,在数据库中使用枚举  JSONObject 需要为其创建一个转换器

public class Converters {@TypeConverterpublic static SearchType toSearchType(String value) {return value == null ? null : SearchType.valueOf(value);}@TypeConverterpublic static String fromSearchType(SearchType searchType) {return searchType == null ? null : searchType.name();}
}
  1. 创建 单利 DbUtil 帮助类
public class DbUtil {private AppDataBase appDataBase;private static DbUtil instance;private Context context;private String dbName;public static DbUtil getInstance() {if (instance == null) {instance = new DbUtil();}return instance;}public void init(Context context,String dbName) {this.context =context.getApplicationContext();this.dbName = dbName;appDataBase = null;}public AppDataBase getAppDataBase() {if (appDataBase == null) {if (TextUtils.isEmpty(dbName)) {throw new NullPointerException("dbName is null");}appDataBase = Room.databaseBuilder(context, AppDataBase.class, dbName).allowMainThreadQueries().enableMultiInstanceInvalidation()
//                    .addMigrations(MIGRATION_1_2).addCallback(sRoomDatabaseCallback).build();}return appDataBase;}/*** 数据库版本 1->2 user表格新增了age列*/static final Migration MIGRATION_1_2 = new Migration(1, 2) {@Overridepublic void migrate(SupportSQLiteDatabase database) {}};/*** 数据库版本 2->3 新增book表格*/static final Migration MIGRATION_2_3 = new Migration(2, 3) {@Overridepublic void migrate(SupportSQLiteDatabase database) {}};private static RoomDatabase.Callback sRoomDatabaseCallback =new RoomDatabase.Callback() {@Overridepublic void onCreate(@NonNull SupportSQLiteDatabase db) {super.onCreate(db);db.execSQL("CREATE TRIGGER limit_search_history_count " +"AFTER INSERT ON SearchHistoryEntity " +"WHEN (SELECT COUNT(*) FROM SearchHistoryEntity) > 30 " +"BEGIN " +"DELETE FROM SearchHistoryEntity WHERE id = (SELECT id FROM SearchHistoryEntity ORDER BY searchTime ASC LIMIT 1); " +"END");}};}

MIGRATION_1_2 就是数据升级的操作,这里不做介绍。

  1. 创建 Repository
public class HomeSearchRepository extends BaseModel {private SearchHistoryDao searchHistoryDao;public HomeSearchRepository() {searchHistoryDao = DbUtil.getInstance().getAppDataBase().searchHistoryDao();}/*** Description:插入查询历史(name 相同,忽略策略)* author:clp* ModificationTime: 2024/12/26 13:06*/public long insertHis(SearchHistoryEntity entity) {return searchHistoryDao.insertPerson(entity);}
}
  1. 创建 ViewModel
ublic class HomeSearchViewModel extends BaseViewModel<HomeSearchRepository> {public MutableLiveData<List<SearchHistoryEntity>> searchHistories = new MutableLiveData<>();public HomeSearchViewModel(@NonNull Application application) {super(application);initAroundSearchDatas();gson = new Gson();}/*** 保存搜索历史** @param entity 搜索的内容*/public void insertHistory(SearchHistoryEntity entity) {if (isLogin) {uploadHistory(entity,true);} else {if (model.insertHis(entity) != -1) {searchHistories.postValue(model.getAllLoggedOutData());}}}
}

    isLogin 判断是否登录,

    登录状态 走接口上传,未登录状态 直接存入数据库,存入成功 postValue 到Activity 接收。

    Activity代码就不贴出来了。也就是

    ViewModel.observe(this, new Observer<List<SearchHistoryEntity>>() {@Overridepublic void onChanged(List<SearchHistoryEntity> searchHistoryEntities) {if (searchHistoryEntities != null) {mSearchHistoryAdapter.setNewData(searchHistoryEntities);}}
    });

    如果对Mvvm 框架使用感兴趣可以参考我上篇文章 Mvvm + viewModel

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/24403.html

    如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    Java进阶(vue基础)

    目录 1.vue简单入门 ?1.1.创建一个vue程序 1.2.使用Component模板(组件&#xff09; 1.3.引入AXOIS ?1.4.vue的Methods&#xff08;方法&#xff09; 和?compoted&#xff08;计算&#xff09; 1.5.插槽slot 1.6.创建自定义事件? 2.Vue脚手架安装? 3.Element-UI的…

    密码学基础

    第1节 密码学概述 密码是人类在信息活动中的一项伟大发明&#xff0c;是保护秘密信息的工具。它诞生于公元前两千余年的埃及&#xff0c;迄今已有四千多年的历史。在出现年代有可查证记录的科学技术中&#xff0c;密码是历史最为悠久的科学技术之一。 百度百科里对密码的解释&…

    Java入门级小案例:网页版简易计算器

    网页版简易计算器 目录 网页版简易计算器需求&#xff1a;代码实现&#xff1a;效果显示 需求&#xff1a; 用HTML、CSS、JS进行书写一个具备一定功能的简易计算器。 代码实现&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta cha…

    【Uniapp-Vue3】导入uni-id用户体系

    在uniapp官网的uniCloud中下载uni-id用户体系 或者直接进入加载&#xff0c;下载地址&#xff1a;uni-id-pages - DCloud 插件市场 进入以后下载插件&#xff0c;打开HbuilderX 选中项目&#xff0c;点击确定 点击跳过 点击合并 右键uniCloud文件夹下的database文件夹&#x…

    Python 入门教程(2)搭建环境 | 2.3、VSCode配置Python开发环境

    文章目录 一、VSCode配置Python开发环境1、软件安装2、安装Python插件3、配置Python环境4、包管理5、调试程序 前言 Visual Studio Code&#xff08;简称VSCode&#xff09;以其强大的功能和灵活的扩展性&#xff0c;成为了许多开发者的首选。本文将详细介绍如何在VSCode中配置…

    Spring Boot电影评论网站系统设计与实现

    随着互联网和娱乐产业的发展&#xff0c;电影评论网站逐渐成为人们分享观影体验、交流影评的重要平台。本文将介绍一个基于Spring Boot框架开发的电影评论网站系统的功能设计与实现方案。 功能模块概述 该电影评论网站系统分为管理员模块和用户模块两大核心部分&#xff0c;以…

    RT-Thread+STM32L475VET6——TF 卡文件系统

    文章目录 前言一、板载资源二、具体步骤1.打开CubeMX进行USB配置1.1 使用外部高速时钟&#xff0c;并修改时钟树1.2 打开SPI1&#xff0c;参数默认即可(SPI根据自己需求调整&#xff09;1.3 打开串口&#xff0c;参数默认1.4 生成工程 2.配置SPI2.1 打开SPI驱动2.2 声明使用SPI…

    LabVIEW形状误差测量系统

    在机械制造领域&#xff0c;形状与位置公差&#xff08;GD&T&#xff09;直接影响装配精度与产品寿命。国内中小型机加工企业因形状误差导致的返工率高达12%-18%。传统测量方式存在以下三大痛点&#xff1a; ​ 设备局限&#xff1a;机械式千分表需人工读数&#xff0c;精度…

    【c语言】字符函数和字符串函数(1)

    一、字符分类函数 c语言中有部分函数是专门做字符分类的&#xff0c;也就是一个字符是属于什么类型的字符&#xff0c;这些函 数的使用要包含一个头文件ctype.h中。 其具体如下图所示&#xff1a; 这些函数的使用方式都类似&#xff0c;下面我们通过一个函数来看其…

    【Python LeetCode 专题】动态规划

    斐波那契类型70. 爬楼梯746. 使用最小花费爬楼梯198. 打家劫舍740. 删除并获得点数矩阵62. 不同路径方法一:二维 DP方法二:递归(`@cache`)64. 最小路径和63. 不同路径 II120. 三角形最小路径和221. 最大正方形字符串139. 单词拆分5. 最长回文子串516. 最长回文子序列72. 编…

    Linux相关知识(文件系统、目录树、权限管理)和Shell相关知识(字符串、数组)

    仅供自学&#xff0c;请去支持javaGuide原版书籍。 1.Linux 1.1.概述 Linux是一种类Unix系统。 严格来讲&#xff0c;Linux 这个词本身只表示 Linux内核&#xff0c;单独的 Linux 内核并不能成为一个可以正常工作的操作系统。所以&#xff0c;就有了各种 Linux 发行版&#…

    第九节: Vue 3 中的 provide 与 inject:优雅的跨组件通信

    文章目录 前言什么是 provide 和 inject&#xff1f;provide 的基本使用inject 的基本使用provide 提供响应式数据数据provide 提供修改数据的方法provide 提供只读响应数据provide 使用symbol作为注入名inject 默认值总结 前言 在 Vue 3 中&#xff0c;provide 和 inject 是一…

    prometheus+node_exporter+grafana监控K8S信息

    prometheusnode_exportergrafana监控K8S 1.prometheus部署2.node_exporter部署3.修改prometheus配置文件4.grafana部署 1.prometheus部署 包下载地址&#xff1a;https://prometheus.io/download/ 将包传至/opt 解压 tar xf prometheus-2.53.3.linux-amd64.tar.gz 移动到…

    C/C++流星雨

    系列文章 序号直达链接1C/C李峋同款跳动的爱心2C/C跳动的爱心3C/C经典爱心4C/C满屏飘字5C/C大雪纷飞6C/C炫酷烟花7C/C黑客帝国同款字母雨8C/C樱花树9C/C奥特曼10C/C精美圣诞树11C/C俄罗斯方块小游戏12C/C贪吃蛇小游戏13C/C孤单又灿烂的神14C/C闪烁的爱心15C/C哆啦A梦16C/C简单…

    理解 MHA、GQA、MQA 和 MLA:多头注意力的变种及其应用

    在深度学习、自然语言处理&#xff08;NLP&#xff09;和计算机视觉&#xff08;CV&#xff09;中&#xff0c;多头注意力&#xff08;Multi-Head Attention, MHA&#xff09;是 Transformer 结构的核心。近年来&#xff0c;MHA 产生了多个变体&#xff0c;如 GQA&#xff08;G…

    Crack SmartGit

    感谢大佬提供的资源 一、正常安装SmartGit 二、下载crackSmartGit crackSmartGit 发行版 - Gitee.com 三、使用crackSmartGit 1. 打开用户目录&#xff1a;C:\Users%用户名%\AppData\Roaming\syntevo\SmartGit。将crackSmartGit.jar和license.zip拷贝至 用户目录。 2. 用户…

    将CUBE或3DL LUT转换为PNG图像

    概述 在大部分情况下&#xff0c;LUT 文件通常为 CUBE 或 3DL 格式。但是我们在 OpenGL Shader 中使用的LUT&#xff0c;通常是图像格式的 LUT 文件。下面&#xff0c;我将教大家如何将这些文件转换为 PNG 图像格式。 条形LUT在线转换&#xff08;不是8x8网络&#xff09;&am…

    关于命令行下的 git( git add、git commit、git push)

    文章目录 关于 gitgit 的概念git 操作&#xff08;git add、git commit、git push 三板斧&#xff09;安装 git新建仓库及配置git clone.gitignoregit addgit commitgit push其他 git 指令git pull&#xff08;把远端的东西拉到本地进行同步&#xff09;其他指令 关于 git git…

    一文讲解Redis中的数据一致性问题

    一文讲解Redis中的数据一致性问题 在技术派实战项目中&#xff0c;我们采用的是先写 MySQL&#xff0c;再删除 Redis 的方式来保证缓存和数据库的数据一致性。 我举例说明一下。 对于第一次查询&#xff0c;请求 B 查询到的缓存数据是 10&#xff0c;但 MySQL 被请求 A 更新为…

    论文笔记(七十二)Reward Centering(五)

    Reward Centering&#xff08;五&#xff09; 文章概括摘要附录B 理论细节C 实验细节D 相关方法的联系 文章概括 引用&#xff1a; article{naik2024reward,title{Reward Centering},author{Naik, Abhishek and Wan, Yi and Tomar, Manan and Sutton, Richard S},journal{arX…