美文网首页数据库GreenDao
Android 基础(34)GreenDao解析

Android 基础(34)GreenDao解析

作者: perry_Fan | 来源:发表于2019-04-29 22:47 被阅读0次

Realm是项目一直使用的ORM数据库框架。网络上的众多项目均采用GreenDao开发,因此学习实践一下。

一.官方文档:

http://greenrobot.org/greendao/documentation/以及Github的链接https://github.com/greenrobot/greenDAO

基本原理

官方文档中还介绍了ObjectBox作为Sqlite的替代方案,性能通过官方给出的图片还是差距显著的。

二. 集成
buildscript {
    repositories {
        jcenter()
        mavenCentral() // add repository
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.1'
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // add plugin
    }
}
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao' // apply plugin
 
dependencies {
    implementation 'org.greenrobot:greendao:3.2.2' // add library
}
greendao {
    schemaVersion 1 //数据库版本号
    daoPackage 'com.fph.greendaodemo.db'// 设置DaoMaster、DaoSession、Dao 包名
    targetGenDir 'src/main/java'//设置DaoMaster、DaoSession、Dao目录
}

实体类

@Entity
public class User {
    @NotNull
    private String name;

    //id 字段必须是Long类型,默认自增
    @Id
    private Long id;

//... Getter/Setter等方法 通过点击 build/make project 之后来生成
}

生成的还有DaoSession、DaoMaster 和 UserDao类
初始化写在Application中

public class MyApplication extends Application {

    private DaoSession daoSession;
    private static MyApplication myApplication;

    @Override
    public void onCreate() {
        super.onCreate();
        initGreenDao();
        myApplication = this;
    }

    /**
     * 初始化 GreenDao
     */
    private void initGreenDao() {
        DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "test.db");
        SQLiteDatabase db = helper.getWritableDatabase();
        DaoMaster master = new DaoMaster(db);
        daoSession = master.newSession();
    }

    public DaoSession getDaoSession() {
        return daoSession;
    }

    public static MyApplication getApplication() {
        return myApplication;
    }
}

完成上面的工作后,写增、删、改、查的测试类

public class UserUtils {
    private static final String TAG = UserUtils.class.getSimpleName();

    private static UserUtils instance;
    private DaoSession daoSession;
    private UserDao userDao;

    private UserUtils() {
        initDao();
    }

    public static UserUtils getInstance() {
        if (null == instance) {
            synchronized (UserUtils.class) {
                if (null == instance) {
                    instance = new UserUtils();
                }
            }
        }
        return instance;
    }

    //获取DAO
    private void initDao() {
        if (null == daoSession) {
            daoSession = MyApplication.getApplication().getDaoSession();
        }
        if (null == userDao) {
            userDao = daoSession.getUserDao();
        }
    }

    //插入 若数据库中存在此id,则会报错
    public void insert(User user) {
        if (null != userDao && null != user) {
            userDao.insert(user);
        } else {
            return;
        }
    }

    //插入 若数据库中存在此id,则会更新数据
    public void insertOrReplace(User user) {
        if (null != userDao && null != user) {
            userDao.insertOrReplace(user);
        } else {
            return;
        }
    }

    //删除
    public void delete(User user) {
        if (null != userDao && null != user) {
            userDao.delete(user);
        } else {
            return;
        }
    }

    //根据id删除
    public void deleteByUserId(String id) {
        if (null != userDao && !TextUtils.isEmpty(id)) {
            userDao.deleteByKey(Long.valueOf(id));
        } else {
            return;
        }
    }

    //更新
    public void update(User user) {
        if (null != userDao && null != user) {
            userDao.update(user);
        } else {
            return;
        }
    }

    //查询所有
    public List<User> query() {
        if (null != userDao) {
            return userDao.loadAll();
        } else {
            return null;
        }
    }

    //根据id查询
    public User queryById(String id) {
        if (null != userDao && !TextUtils.isEmpty(id)) {
            return userDao.loadByRowId(Long.valueOf(id));
        } else {
            return null;
        }
    }

    //根据条件查询
    public List<User> queryByArgs(String selection, String selectionArg) {
        if (null != userDao && !TextUtils.isEmpty(selection)) {
            return userDao.queryRaw(selection, selectionArg);
        } else {
            return null;
        }
    }

实践地址可见:https://github.com/VersaceSilva/AndroidReview/tree/master/GreenDaoTest

三. 文档探究
1. 建模实体

要在项目中使用greenDAO,您需要创建一个表示应用程序中持久数据的实体模型。然后,基于此模型,greenDAO为DAO类生成Java代码。模型本身是使用带注释的Java类定义的。


建模实体
// In the build.gradle file of your app project:
android {
...
}
greendao {
    schemaVersion 2
    // daoPackage "com.example.model" // set package of generated classes
}

1.1 greendao配置元素支持许多配置选项:

  • schemaVersion:数据库模式的当前版本。这是使用的 * OpenHelpers类模式版本之间迁移。如果更改实体/数据库架构,则必须增加此值。默认为1。
  • daoPackage:生成的DAO,DaoMaster和DaoSession的包名。 默认为源实体的包名称。
  • targetGenDir:应存储生成的源的位置。 默认为构建目录( build / generated / source / greendao)中生成的源文件夹。
    generateTests: 设置为true以自动生成单元测试。
  • targetGenDirTests: 应存储生成的单元测试的基本目录。默认为 src / androidTest / java。

1.2 另外,可以使用@Entity配置一些细节:

@Entity(
        // If you have more than one schema, you can tell greenDAO
        // to which schema an entity belongs (pick any string as a name).
        schema = "myschema",
        
        // Flag to make an entity "active": Active entities have update,
        // delete, and refresh methods.
        active = true,
        
        // Specifies the name of the table in the database.
        // By default, the name is based on the entities class name.
        nameInDb = "AWESOME_USERS",
        
        // Define indexes spanning multiple columns here.
        indexes = {
                @Index(value = "name DESC", unique = true)
        },
        
        // Flag if the DAO should create the database table (default is true).
        // Set this to false, if you have multiple entities mapping to one table,
        // or the table creation is done outside of greenDAO.
        createInDb = false,
 
        // Whether an all properties constructor should be generated.
        // A no-args constructor is always required.
        generateConstructors = true,
 
        // Whether getters and setters for properties should be generated if missing.
        generateGettersSetters = true
)
public class User {
  ...
}

1.3 基本属性如下

@Entity
public class User {
    @Id(autoincrement = true)
    private Long id;
 
    @Property(nameInDb = "USERNAME")
    private String name;
 
    @NotNull
    private int repos;
 
    @Transient
    private int tempUsageCount;
 
    ...
}
2. Session

(生成的) DaoSession类是greenDAO的核心之一。 DaoSession为开发人员提供了对基本实体操作和DAO的访问,以实现更完整的操作集。

daoMaster = new DaoMaster(db);
daoSession = daoMaster.newSession();

数据库连接属于 DaoMaster,因此多个Session引用相同的数据库连接。因此,可以非常快速地创建新Session。但是,每个会话分配内存,通常是实体的Session“缓存”。

如果有两个查询返回相同的数据库对象,那么您使用了多少个Java对象:一个或两个?它完全取决于身份范围。

这种副作用是某种实体“缓存”。如果实体对象仍在内存中(greenDAO在此处使用弱引用),则不再构造实体。此外,greenDAO不执行数据库查询来更新实体值。相反,该对象从会话缓存“立即”返回,该速度比一个或两个更快

如果要清除整个会话的标识范围,不要返回“缓存”对象:

daoSession.clear();

要清除单个DAO的标识​​范围:

noteDao = daoSession.getNoteDao();
noteDao.detachAll();
3. 查询

查询返回符合特定条件的实体。在greenDAO中,您可以使用原始SQL制定查询,或使用QueryBuilder API 更轻松地制定查询。
此外,查询支持延迟加载结果,这可能会在大型结果集上运行时节省内存和性能。
编写SQL可能很困难并且容易出现错误,这些错误仅在运行时才会被注意到。该QueryBuilder类可以让你建立你的实体,而不SQL自定义查询,并有助于在编译时已检测错误。
3.1 QueryBuilder
简单条件示例:查询名字为“Joe”的所有用户,按姓氏排序:

List<User> joes = userDao.queryBuilder()
  .where(Properties.FirstName.eq("Joe"))
  .orderAsc(Properties.LastName)
  .list();

嵌套条件示例:获取1970年10月或之后出生的名为“Joe”的用户。
假设我们将用户的生日作为年,月和日的单独属性。然后,我们可以表达的状态在一个更正式的方式: 首先名称是“乔” AND (一年的出生是更大的比1970年OR (一年的出生是1970年和月的诞生是等于到或大于比10 )) (对10月10日)。

QueryBuilder<User> qb = userDao.queryBuilder();
qb.where(Properties.FirstName.eq("Joe"),
qb.or(Properties.YearOfBirth.gt(1970),
qb.and(Properties.YearOfBirth.eq(1970), Properties.MonthOfBirth.ge(10))));
List<User> youngJoes = qb.list();

3.2 Order
您可以order查询结果。基于姓氏和出生年份的人的例子:

// order by last name
queryBuilder.orderAsc(Properties.LastName);
 
// in reverse
queryBuilder.orderDesc(Properties.LastName);
 
// order by last name and year of birth
queryBuilder.orderAsc(Properties.LastName).orderDesc(Properties.YearOfBirth);

greenDAO使用的默认排序规则是 COLLATE NOCASE ,但可以使用stringOrderCollat​​ion () 进行自定义 。有关 影响结果顺序的其他方法,请参阅QueryBuilder类文档。

3.3 限制,偏移和分页
有时您只需要查询的子集,例如要在用户界面中显示的前10个元素。当您拥有大量实体并且不能仅使用“where”语句限制结果时,这尤其有用(并且足智多谋)。 QueryBuilder < T >具有定义限制和偏移的方法:
limit(int):限制查询返回的结果数。
offset(int):结合limit (int )设置查询结果的偏移量 。跳过第一个 偏移结果,结果总数将受 限制(int)限制。您不能在没有limit (int )的情况下使用offset 。

3.4 自定义类型作为参数
通常,greenDAO透明地映射查询中使用的类型。例如, boolean映射到 具有0或1值的INTEGER,而 Date映射到 (long )INTEGER 值。
自定义类型是一个例外:在构建查询时,您始终必须使用数据库值类型。例如,如果使用转换器将枚举类型映射到 int值,则应在查询中使用 int值。
更多详情见:http://greenrobot.org/greendao/documentation/queries/

4. Join

非平凡的查询通常需要几种实体类型(表)的数据。在SQL世界中,您可以通过使用连接条件“连接”两个或多个表来实现这一点。

让我们考虑一个实体 User,它与Address实体有一对多的关系 。然后,我们要查询的生活在“SeaSame Street”的用户:我们将不得不加入 地址与实体 用户使用用户ID和实体上定义一个WHERE条件 地址实体:

QueryBuilder<User> queryBuilder = userDao.queryBuilder();
queryBuilder.join(Address.class, AddressDao.Properties.userId)
  .where(AddressDao.Properties.Street.eq("Sesame Street"));
List<User> users = queryBuilder.list();

连接需要目标实体类作为每个实体的参数和连接属性。在该示例中,仅定义了Address实体的join属性 ,因为默认情况下使用主键属性。换句话说,查询会产生用户,这些用户的 地址实体的 userId等于 用户实体的ID,并且还具有特定的street。

相关文章

网友评论

    本文标题:Android 基础(34)GreenDao解析

    本文链接:https://www.haomeiwen.com/subject/pfejwqtx.html