前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >优雅地烘焙 DBService

优雅地烘焙 DBService

作者头像
HelloVass
发布于 2018-09-12 02:25:21
发布于 2018-09-12 02:25:21
71100
代码可运行
举报
文章被收录于专栏:Hellovass 的博客Hellovass 的博客
运行总次数:0
代码可运行

写在最前

记得大二那年第一次接触 GreenDao 这个神奇数据库,惊叹道,哇,原来代码还能这么写啊,不用自己手撸 SQLiteDatabase,不用写那些麻烦的 SQL 语句,编程还真是一件 “轻松” 的事情呢2333。然而,时隔多年,当我再次看到自己曾经留在项目里的那段代码时,相顾无言,惟有泪千行…

自己挖的坑,哭着也要填完

在此,向川神和小飞飞道歉,哈哈,让你们一直用着我当年的拙劣之作,实在抱歉,你们那时候一定很想打死我吧!

新的类图,更强的封装

目录结构看起来是这样的

思路

数据库版本

greenDao3.x,3.x版本的 greenDao 采用注解+apt 的方式生成 PO(数据持久化对象),和以前的定义 Generator 方式相比,真的简洁了不少。

设计模式&法则

  • 抽象工厂
  • 依赖倒置

产品类族

DBService

仅包含一个 AbsDaoDelegate 的引用,通过建造者模式创建它的实例。源码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public final class DBService<T, K> {

  private AbsDaoDelegate<T, K> mDaoDelegate;

  public DBService(Builder<T, K> builder) {

    mDaoDelegate = builder.mDaoDelegate;
  }

  public AbsDaoDelegate<T, K> getDaoDelegate() {

    return mDaoDelegate;
  }

  public static final class Builder<T, K> {

    private AbsDaoDelegate<T, K> mDaoDelegate;

    public Builder<T, K> setDaoDelegate(AbsDaoDelegate<T, K> daoDelegate) {

      mDaoDelegate = daoDelegate;
      return this;
    }

    private void checkEmptyFields() {

      if (mDaoDelegate == null) {

        throw new IllegalArgumentException("core dao can't be null");
      }
    }

    public DBService<T, K> build() {

      checkEmptyFields();
      return new DBService<>(this);
    }
  }
}

注意,DBService 是一个泛型类,泛型 T 代表了我们定义的 PO(数据持久化对象),泛型 K 代表了 PO 的主键类型。不要急,稍后会给出如何定义 PO 的栗子。

AbsDaoDelegate

  • 实现了 DataSource 接口,提供了开发中需要的 60% 的方法
  • 代理模式,本身不做 CUID 操作,全部交由 greenDao 生成的 XXDao 来完成

源码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public abstract class AbsDaoDelegate<T, K> implements DataSource<T, K> {

  protected AbstractDao<T, K> mAbstractDao;

  public AbsDaoDelegate(AbstractDao<T, K> abstractDao) {

    mAbstractDao = abstractDao;
  }

  @Override public void add(T data) {

    mAbstractDao.insert(data);
  }

  @Override public void addAll(List<T> dataList) {

    mAbstractDao.insertInTx(dataList);
  }

  @Override public void delete(T data) {

    mAbstractDao.delete(data);
  }

  @Override public void deleteByKey(K k) {

    mAbstractDao.deleteByKey(k);
  }

  @Override public void deleteByKeys(List<K> keyList) {

    mAbstractDao.deleteByKeyInTx(keyList);
  }

  @Override public void deleteByCondition(WhereCondition whereCondition) {

    mAbstractDao.queryBuilder()
        .where(whereCondition)
        .buildDelete()
        .executeDeleteWithoutDetachingEntities();
  }

  @Override public void deleteAll(List<T> dataList) {

    mAbstractDao.deleteInTx(dataList);
  }

  @Override public void update(T t) {

    mAbstractDao.update(t);
  }

  @Override public void updateAll(List<T> dataList) {

    mAbstractDao.updateInTx(dataList);
  }

  @Override public void insertOrReplace(T t) {

    mAbstractDao.insertOrReplace(t);
  }

  @Override public void insertOrReplace(List<T> dataList) {

    mAbstractDao.insertOrReplaceInTx(dataList);
  }

  @Override public T queryByKey(K k) {

    return mAbstractDao.load(k);
  }

  @Override public List<T> queryByPage(int pageNum) {

    return mAbstractDao.queryBuilder().offset(10 * (pageNum - 1)).limit(10).list();
  }

  @Override public List<T> queryByCondition(WhereCondition whereCondition, Property sort) {

    return mAbstractDao.queryBuilder().where(whereCondition).orderDesc(sort).list();
  }

  @Override public List<T> queryAll() {

    return mAbstractDao.loadAll();
  }

  @Override public void clear() {

    mAbstractDao.deleteAll();
  }
}

工厂类族

抽象工厂 DBServiceFactory

  • 定义了创建 DBService 的方法,至于如何创建工厂,交给具体工厂(实现了抽象工厂的类)来决定

源码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface DBServiceFactory<T, K> {

  DBService<T, K> createDBService();
}

DBServiceStore

  • 定义了创建 DBService 的方法,看起来和 DBServiceFactory 有点儿像,但意义不同。商店并不负责造产品,只是根据用户传入的工厂来提供对应的产品 DBService,自己并不会负责去造产品。如果是这样的话,那商店未免也太辛苦了不是吗?

源码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public abstract class DBServiceStore {

  public abstract <T, K> DBService<T, K> createDBService(DBServiceFactory<T, K> factory);
}

DBHelper

和 2.x 时代的写法相比并没有太大的区别,这里就直接贴代码了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public final class DBHelper {

  private static DBHelper sDBHelper;

  private DaoMaster mDaoMaster;

  private DaoSession mDaoSession;

  public static DBHelper getInstance(Context context, String dbName) {

    if (sDBHelper == null) {
      
      sDBHelper = new DBHelper(context, dbName);
    }

    return sDBHelper;
  }

  public DaoSession getDaoSession() {

    return mDaoSession;
  }

  private DBHelper(Context context, String dbName) {

    DaoMaster.DevOpenHelper devOpenHelper =
        new DaoMaster.DevOpenHelper(context.getApplicationContext(), dbName, null);
    mDaoMaster = new DaoMaster(devOpenHelper.getWritableDatabase());
    mDaoSession = mDaoMaster.newSession();
  }
}

PO 定义

也就是类图中 User 的声明,greenDao 3.x 之后,我们可以使用注解来声明一个 PO,然后 build 一下,greenDao 会自动在 greendao 这个包目录(包目录可以根据自己的喜好指定)下生成我们需要的 DaoMaster、DaoSession、UserDao。 私以为,这些由 apt 自动生成的 class ,我们最好不要改动!!!

使用

前面讲了这么多,不过是介绍我的封装思路而已,那么实际项目中怎么使用呢?其实写这篇博客也是给自己做备忘2333…

PeroDBServiceStore

implements 自 DBServiceStore,代表 DBService 商店,我们需要 XXDBService 的时候,只需要调用 createDBService 方法,将具体的工厂 XXDBServiceFactory 作为参数传入即可。

源码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class PeroDBServiceStore extends DBServiceStore {

  private PeroDBServiceStore() {

  }

  private static class PeroDBServiceStoreSingletonHolder {

    private static final PeroDBServiceStore sInstance = new PeroDBServiceStore();
  }

  public static PeroDBServiceStore getInstance() {

    return PeroDBServiceStoreSingletonHolder.sInstance;
  }

  @Override public <T, K> DBService<T, K> createDBService(DBServiceFactory<T, K> factory) {

    return factory.createDBService();
  }
}

具体工厂

implements 自 DBServiceFactory,生产具体产品,然后提供给商店,最后提供给我们这些消费者

  1. 根据需要定义 PO
  2. 根据 PO 定义 XXDaoDelegate
  3. 根据 XXDaoDelegate 定义 XXDBService
  4. 根据 XXDBService 定义 XXDBServiceFactory

当然,很多时候可以简化,比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class UserDBServiceFactory implements DBServiceFactory<User, String> {

  private UserDBServiceFactory() {

  }

  public static UserDBServiceFactory create() {

    return new UserDBServiceFactory();
  }

  @Override public DBService<User, String> createDBService() {

    return new DBService.Builder<User, String>				   ().setDaoDelegate(provideDaoDelegate()).build();
  }

  private AbsDaoDelegate<User, String> provideDaoDelegate() {

    return new AbsDaoDelegate<User, String>(
        DBHelper.getInstance(App.getInstance(), "pero_db").getDaoSession().getUserDao()) {

    };
  }
}

这里,我只定义了一个 UserDBServiceFactory,其它的都是用匿名对象实现了。当然,这是为了简化代码,大部分情况下都是可以满足的,毕竟 DataSource 接口定义了很多常用的方法,比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface DataSource<T, K> {

  void add(T data);

  void addAll(List<T> dataList);

  void delete(T data);

  void deleteByKey(K k);

  void deleteByKeys(List<K> keyList);

  void deleteByCondition(WhereCondition whereCondition);

  void deleteAll(List<T> dataList);

  void update(T t);

  void updateAll(List<T> dataList);

  void insertOrReplace(T t);

  void insertOrReplace(List<T> dataList);

  T queryByKey(K k);

  List<T> queryByPage(int pageNum);

  List<T> queryByCondition(WhereCondition whereCondition, Property sort);

  List<T> queryAll();

  void clear();
}

这时候,我们 save 一个 User 的时候可以这么写了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
PeroDBServiceStore.getInstance().createDBService(UserDBServiceFactory.create())
  		.getDaoDelegate()
        .add(user); // save user to db

// 因为懒,省略其他的栗子了

疑问&解答

兄弟,那我需要更骚的操作怎么办?

比如,我需要根据 User 的某个属性(比如用户 Id)来获取数据,怎么破?

解答

额,这个骚操作,在 DataSource 里确实不怎么好定义(至少我当时没想好),可以破。在 XXDBServiceFactory 里定义 XXDaoDelegate,实现你的骚操作。调用的时候,这么来:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
PeroDBServiceStore.getInstance().createDBService(UserDBServiceFactory.create())
  		.getDaoDelegate()
  		.queryByCondition(UserDao.Properties.UserId.eq(userId),UserDao.Properties.createdAt);

可能不够优雅 233333,总之我看着是有点儿不爽,哪天有空再改改…

写在最后

五一假期,在空间看到学弟组织了一波钱塘江烧烤+KTV,撸着烤串,唱歌歌,就这么唱出自己的大学人生,仔细想想,就是有这样的骚操作呢!可惜,还是少了几个妹纸啊233333

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-04-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
拆轮子系列之理解GreenDao框架源码
如果Android项目中要使用GreenDAO框架,需要先创建一个Java项目用于生成实体类和DAO类,然后在Android项目中使用这些类,在此过程中分别需要对Android项目添加GreenDAO的核心包依赖和对Java项目添加generator包依赖,所以解析GreenDAO的源码需要解析两部分,而这里只解析GreenDAO核心包在Android项目中的工作原理,generator包中的原理很简单,总的来说有四个作用:就是用于生成实体类、DAO类、建立多表之间的关联以及配置实体类的接口和序列化功能
见得乐
2022/09/08
1.1K0
Android greenDAO 3.2.2简单使用
然后编译会在自定义的文件夹包名(比如上面的‘com.yechaoa.test.dao’)下生成3个文件:
yechaoa
2022/06/10
5900
Android十八章:是时候在你项目用上greenDAO3GreenDao
GreenDao3.1.0使用案例包含(增删查改,升级数据库),3.+版本比2.+更加便捷生成DaoMaster和DaoSession 本文项目地址
ppjun
2018/09/05
5910
GreenDao 工具类 --- 使用 Json 快速生成 Bean、表及其结构,"炒鸡"快!
作者:林冠宏 / 指尖下的幽灵 腾讯云+社区:https://cloud.tencent.com/developer/user/1148436/activities 掘金:https://jue
林冠宏-指尖下的幽灵
2018/01/03
1.7K0
GreenDao 工具类 --- 使用 Json 快速生成 Bean、表及其结构,"炒鸡"快!
开源数据库框架greenDAO
最近在对开发项目的性能进行优化。由于项目里涉及了大量的缓存处理和数据库运用,需要对数据库进行频繁的读写、查询等操作。因此首先想到了对整个项目的数据库框架进行优化。 原先使用android本身内置的sqllite,也就是用的最基本的SQLiteOpenHelper方法,这种方法对自己来说比较方便易懂。但是在使用过程中感觉很繁琐,从建表到对表的增删改查等操作,如果表对象的属性很多,就需要使用大量的代码来执行建表、插入等。在代码执行中还需要对数据库和游标的进行及时关闭(开启使用,用完关闭),而且还需要部分sq
xiangzhihong
2018/01/29
2.3K0
RxCache 整合 Android 的持久层框架 greenDAO、Room一. 背景二. 持久层三. 使用四. 总结
RxCache 是一个支持 Java 和 Android 的 Local Cache 。
fengzhizi715
2018/10/25
1K0
RxCache 整合 Android 的持久层框架 greenDAO、Room一. 背景二. 持久层三. 使用四. 总结
GreenDao,clearIdentityScope报错Error:Execution failed for task ':app:compileDebugJavaWithJavac'. > Com
Error:(41, 22) 错误: 找不到符号 符号:   方法 clearIdentityScope() 位置: 类型为DaoConfig的变量 dataDaoConfig
zhangjiqun
2024/12/16
800
GreenDao,clearIdentityScope报错Error:Execution failed for task ':app:compileDebugJavaWithJavac'. > Com
GreenDAO快速入门
之前在自己做项目的时候,用到了GreenDAO数据库,其实对于数据库辅助工具库从OrmLite,到litePal再到GreenDAO,总是在不停的切换,但是没有真正去了解他们的差异。只停留在会使用的阶段。说起来也是惭愧。本文的重点也是在于如何快速使用。不会进行较深的探究。
g小志
2018/09/11
9260
GreenDAO快速入门
android数据保存之greendao
有时我们的数据属于保存到数据库,对于Android应用和IOS应用,我们一般都会使用SQLite这个嵌入式的数据库作为我们保存数据的工具。由于我们直接操作数据库比较麻烦,而且管理起来也非常的麻烦,以前
xiangzhihong
2018/02/02
1.7K0
android数据保存之greendao
Android项目实战(五十一):浅谈GreenDao
HX_User类会变成下面情况,注意 属性值的get和set方法不要自己写,是make project之后自动生成的
听着music睡
2018/09/20
4750
Android项目实战(五十一):浅谈GreenDao
GreenDao 3.0解析
大家应该对ORM框架有了初步的了解,下面我们就来使用GreenDao,我们就通过GreenDao的官网,http://greenrobot.org/greendao/ ,来学习如何对它进行使用。
老马的编程之旅
2022/06/22
6440
GreenDao 3.0解析
Android ORM 框架之 greenDAO
前言 我相信,在平时的开发过程中,大家一定会或多或少地接触到 SQLite。然而在使用它时,我们往往需要做许多额外的工作,像编写 SQL 语句与解析查询结果等。所以,适用于 Android 的ORM 框架也就孕育而生了,现在市面上主流的框架有 OrmLite、SugarORM、Active Android、Realm 与 GreenDAO。而今天的主角便是 greenDAO,下面,我将详解地介绍如何在 Android Studio 上使用 greenDAO,并结合代码总结一些使用过程中的心得。 关于 gre
xiangzhihong
2018/02/01
1.2K0
Android ORM 框架之 greenDAO
Android GreenDao的基本使用
Android 本地数据库有很多,Sqlite、Dbutils、LitePa、GreenDao等,其他的几个数据库框架都用过了,GreenDao还未在项目中用过,所以在这里记录下GreenDao的基本使用。
黄林晴
2019/01/10
2K0
Android集成GreenDao数据库
数据持久化就是指将那些内存中的瞬时数据保存到存储设备中,保证即使在手机或电脑关机的情况下,这些数据仍然不会丢失。保存在内存中的数据是处于瞬时状态的,而保存在存储设备中的数据是处于持久状态的,持久化技术则提供了一种机制可以让数据在瞬时状态和持久状态之间进行转换。 目前,Android系统中提供了3种方式的数据持久化技术,即文件存储、SharedPreferences存储以及数据库存储。当然,除了这3种方式之外,你还可以将数据保存在手机的SD卡中,不过使用文件、Shared Preferences或数据库来保存数据会相对更简单一些,而且比起将数据保存在SD卡中会更加地安全。Shared Preferences通常用在轻量级的数据存储场景中,比如账号/密码的存储,而数据库则用在数据量比较大的场景中,比如聊天数据的存储。
xiangzhihong
2022/10/28
5940
GreenDao教程1
最近项目重构,涉及到了数据库和文件下载,发现GreenDao这个框架还是不错的。直接面向对象的,可以通过对对象的操作,实现数据的存储。
用户3030674
2018/09/14
8390
GreenDao教程1
GreenDao 兼容升级,保留旧数据的---全方面解决方案
作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguanh/ GitHub : https://github.com/af913337456/ 腾讯云专栏: https://cloud.tencent.com/developer/user/1148436/activities GreenDaoCompatibleUpdateHelper
林冠宏-指尖下的幽灵
2018/02/11
1.4K0
★ Android基础篇 Android 数据存储与性能
最近到了一家公司,跟一个同事做项目,比如常规的一些操作用SharedPreferences就很好搞定,他跟我说SharedPreferences 会影响性能说了一堆… 难道别的存储方式就不耗费性能吗?不消耗内存吗?
全栈程序员站长
2021/04/07
1.2K0
如何开发以太坊钱包 - 导入账号及账号管理
从用户需求上来讲,导入用户已经存在的账号是有必要的。 不过从安全性考虑,当你之前使用的是一个非官方、非开源的钱包产品时(尤其是小众钱包),或者之前没有对私钥、助记词、Keysotre文件小心保存时。
Tiny熊
2019/04/09
2.6K0
如何开发以太坊钱包 - 导入账号及账号管理
Android数据库开源框架GreenDao分析
前段时间写Demo的时候遇到了数据库的并发问题 Android数据库多线程并发操作异常 ,然后研究了一下 Android中的数据库连接池 。在看相关代码的时候阅读了我们项目使用的数据库框架GreenDao 。哈哈,挺有意思的^ _ ^。
静默加载
2020/05/29
1.5K0
GreenDao查询,Querying
查询接口返回符合指定条件的实体对象集合.你可以使用SQL组织你的查询语句,或者采用更好的方法,使用greenDao的QueryBuilder API.greenDao的查询也支持延迟加载结果,当结果集很大的时候,它会节省内存和提高性能.
zhangjiqun
2024/12/16
690
推荐阅读
相关推荐
拆轮子系列之理解GreenDao框架源码
更多 >
LV.0
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文