(安卓)ObjectBox数据库笔记
[TOC]
1.Gradle设置
1.1 启用mavenCentral
根工程里的build.gradle
repositories {
mavenCentral()
}
1.2 根build.gradle文件里添加 (project level)
buildscript {
ext.objectboxVersion = '2.9.1'
dependencies {
classpath "io.objectbox:objectbox-gradle-plugin:$objectboxVersion"
}
}
1.3 app 里的 build.gradle 添加 (module level)
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' // Only for Kotlin projects.
apply plugin: 'kotlin-kapt' // Only for Kotlin projects.
apply plugin: 'io.objectbox' // Apply last. after applying Android plugin
1.4 添加依赖
dependencies {
implementation "io.objectbox:objectbox-java:$objectboxVersion"
implementation "io.objectbox:objectbox-android:$objectboxVersion"
}
1.5 配置 支持增量注释处理
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [ "objectbox.incremental" : "true" ]
}
}
}
}
1.6 Objectbox使用流程
1.6.1 定义实体类,然后 Build > Make Project
如果gradle配置正确,将会在app\build\generated\ap_generated_sources\debug\out下生成相应的文件,例如MyObjectBox等。
同时,会自动生成 app/objectbox-models/default.json文件,请把这一个文件加入版本控制,不要删除。不要手动修改里面的内容。
1.6.2 创建 BoxStore
在Application里初始化BoxStore后,可以使用BoxStore对数据表进行增删改查操作。
public class ObjectBox {
private static BoxStore boxStore;
public static void init(Context context) {
boxStore = MyObjectBox.builder()
.androidContext(context.getApplicationContext())
.build();
}
public static BoxStore get() { return boxStore; }
}
2.实体类相关注解
@Entity
public class User {
@Id
public long id;
public String name;
//如果字段是私有的,则必须有一个标准的getters方法。
private String name2;
public String getName2() {
return this.name;
}
}
@Entity:
注解到class上,代表是一个数据库表,实体也必须有一个无参数的构造函数。
@Id :
注解到成员属性上,代表是自增id,必须是long型(java kotlin 里是long型 dart里是int型),@Id 注解的成员属性不能是私有的。默认情况下,ID属性是惟一的,并且有索引。向表里新增数据时,如果不指定id,则新对象的id由ObjectBox分配
@Index:
注解到成员属性上,代表索引,可以在查询时,提高性能。@Index目前不支持String[], byte[], float和double。查询时,如果是字符串,则使用区分大小写的条件
StringIdEntity entity = box.query() .equal(StringIdEntity_.uid, uid, StringOrder.CASE_SENSITIVE) .build().findUnique()
@Transient
注解到成员属性上,代表这一个属性不被持久化到表里。java里的静态属性也不会持久化到表里。
@NameInDb("username")
指定相应成员属性在数据表里对应的数据库字段。使成员属性名和数据库名可以不一样。
@Unique
唯一约束,如果put时,将要向表里增加的记录和表里已经有一个表记录的字段值相同,则会抛异常UniqueViolationException
try {
box.put(new User("Sam Flynn"));
} catch (UniqueViolationException e) {
// A User with that name already exists.
}
3.Objectbox数据的增删改查
3.1 增 put
notesBox = ObjectBox.get().boxFor(Note.class);
private void addNote() {
...
Note note = new Note();
note.setText(noteText);
note.setComment(comment);
note.setDate(new Date());
notesBox.put(note);
Log.d(App.TAG, "Inserted new note, ID: " + note.getId());
...
}
3.2删除 remove
notesBox.remove(note);
Log.d(App.TAG, "Deleted note, ID: " + note.getId());
//关系表查询时,会查出关联的数据,但是删除时,关联的数据并不会直接删除。
3.3改
直接使用put ,但是id值不能为null
note.setText("This note has changed.");
notesBox.put(note);
3.4查
Query query = userBox.query().equal(User_.firstName, "Joe").build();
List joes = query.find();
query.close();
常用的查询条件:
equal:值相等
greater:大于
startsWith:以xx为开始
3.4.1 多条件查询 and or
// equal AND (less OR oneOf)
Query query = box.query(
User_.firstName.equal("Joe")
.and(User_.age.less(12)
.or(User_.stamp.oneOf(new long[]{1012}))))
.order(User_.age)
.build();
3.4.2 Ordering 排序
还可以将标志传递给order()以按降序排序、区分大小写排序或特别处理空值。例如,要对上面的结果进行降序和区分大小写的排序
.order(User_.lastName, QueryBuilder.DESCENDING | QueryBuilder.CASE_SENSITIVE)
3.4.3 debugFlags
要查看ObjectBox实际执行了什么查询语句
// Set the LOG_QUERY_PARAMETERS debug flag
BoxStore store = MyObjectBox.builder()
.debugFlags(DebugFlags.LOG_QUERY_PARAMETERS)
.build();
// Execute a query
query.find();
//控制台可以看到以下输出
Parameters for query #2:
(firstName ==(i) "Joe"
AND age < 12)
3.4.4 find操作查询
// return all entities matching the query
List joes = query.find();
// return only the first result or null if none
User joe = query.findFirst();
// return the only result or null if none, throw if more than one result 如果有多个结果,它会抛出一个异常。
User joe = query.findUnique();
3.4.5复用query和查询参数。
如果您经常运行相同的查询,您应该缓存query对象并重用它。要使Query更具可重用性,您甚至可以在构建Query之后更改所添加的每个条件的值或查询参数,
相关方法setParameter
// build a query
Query query = userBox.query().equal(User_.firstName, "").build();
// change firstName parameter to "Joe", get results
List joes = query.setParameter(User_.firstName, "Joe").find();
...
// change firstName parameter to "Jake", get results
List jakes = query.setParameter(User_.firstName, "Jake").find();
3.4.6 分页相关,Limit, Offset, and Pagination
// offset by 10, limit to at most 5 results
//下标从0开始,包含开始下标。
List joes = query.find(10, 5);
3.4.7 延迟加载 Lazy loading results
为了避免立即加载查询结果,query提供了findLazy()和findLazyCached(),它们返回查询结果的LazyList。
LazyList是一个线程安全的、不可修改的列表,它只在实体被访问时才惰性地读取实体。根据调用的find方法,懒列表是否会被缓存。缓存的惰性列表存储以前访问过的对象,以避免多次加载实体。列表的一些特性仅限于缓存列表(例如需要整个列表的特性)
3.4.8 查询结果作为一个流返回 Query results stream
暂只支持dart
Query query = userBox.query().build();
Stream print(user));
query.close();
3.4.9只查询某一列的值。PropertyQuery
返回的属性值数组没有任何特定的顺序,即使您在构建查询时指定了顺序。
String[] emails = userBox.query().build()
.property(User_.email)
.findStrings();
// or use .findString() to return just the first result
默认情况下,不返回空值。但是,如果一个属性为空,你可以指定一个替换值来返回:
String[] emails = userBox.query().build()
.property(User_.email)
.nullValue("unknown")
.findStrings();
3.5.0 去重查询 Distinct and unique results
PropertyQuery pq = userBox.query().build().property(User_.firstName);
// returns ['joe'] because by default, the case of strings is ignored.
String[] names = pq.distinct().findStrings();
// returns ['Joe', 'joe', 'JOE']
String[] names = pq.distinct(StringOrder.CASE_SENSITIVE).findStrings();
// the query can be configured to throw there is more than one value
String[] names = pq.unique().findStrings();
3.5.1 聚合值 Aggregating values
属性查询(JavaDoc和Dart API文档)还提供了聚合函数来直接计算所有发现值的最小值、最大值、平均值、总和和计数:
min() / minDouble():在匹配查询的所有对象上查找给定属性的最小值。
max() / maxDouble():找到最大值
sum() / sumDouble():计算所有值的和。注意:非双精度版本检测溢出并在这种情况下抛出异常。
avg():计算所有值的平均值(总是双精度)。
count():返回结果的数量。这比查找和获取结果数组的长度要快。可以与distinct()组合使用,只计算不同值的数量。
3.5.2 关联表查询 links Add query conditions for related entities (links)
在创建实体之间的关系之后,您可能希望为只存在于相关实体中的属性添加查询条件。在SQL中,这可以使用join来解决。但由于ObjectBox不是一个SQL数据库,我们构建了一些非常类似的东西:links。链接基于关系
假设有一个Person可以与多个Address实体相关联: ToMany 一对多关系
@Entity
public class Person {
@Id long id;
String name;
ToMany addresses;
}
@Entity
public class Address {
@Id long id;
String street;
String zip;
}
获取具有特定名称且也居住在特定街道上的Person,我们需要查询Person的关联Address实体,为此,使用查询构建器的link()方法来说明应该查询地址关系。然后为Address添加一个条件。
// get all Person objects named "Elmo"...
QueryBuilder builder = personBox
.query().equal(Person_.name, "Elmo");
// ...which have an address on "Sesame Street"
builder.link(Person_.addresses).equal(Address_.street, "Sesame Street");
List elmosOnSesameStreet = builder.build().find();
如果我们想要一个Address列表而不是Person列表,该怎么办?
// get all Address objects with street "Sesame Street"...
QueryBuilder builder = addressBox.query()
.equal(Address_.street, "Sesame Street");
// ...which are linked from a Person named "Elmo"
builder.backlink(Person_.addresses).equal(Person_.name, "Elmo");
List sesameStreetsWithElmo = builder.build().find();
3.5.3 立即预取关联查询里的所有数据 Eager Loading of Relations
Only Java/Kotlin
默认情况下,关联是惰性加载的:当您第一次访问ToOne或ToMany属性时,它将执行数据库查找以获取其数据。在每次后续访问时,它将使用该数据的缓存版本。
List customers = customerBox.query().build().find();
// Customer has a ToMany called orders.
// First access: this will cause a database lookup.
Order order = customers.get(0).orders.get(0);
虽然初始查找速度很快,但您可能希望在返回查询结果之前预取ToOne或ToMany值。为此调用QueryBuilder。在构建查询时使用eager方法,并传递与ToOne和ToMany属性关联的RelationInfo对象来预取:
List customers = customerBox.query()
.eager(Customer_.orders) // Customer has a ToMany called orders.
.build()
.find();
// First access: this will cause a database lookup.
Order order = customers.get(0).orders.get(0);
只适用于深复制。
3.5.4 查询过滤器 Query filters
当您寻找需要匹配复杂条件的对象时(这些条件不能用QueryBuilder类完全表示),查询过滤器就发挥作用了。过滤器是用Java编写的,因此可以表达任何复杂性。不用说,与基于java的过滤器相比,可以更有效地匹配数据库条件。因此,当你将两者一起使用时,你将得到最好的结果:
1.使用标准数据库条件将结果缩小到合理的数量
2.现在,使用QueryFilter Java接口对这些候选者进行筛选,以确定最终结果
// Reduce object count to reasonable value.
songBox.query().equal(Song_.bandId, bandId)
// Filter is performed on candidate objects.
.filter((song) -> song.starCount * 2 > song.downloads);