如何在 Android 中使用 ObjectBox 数据库框架?
导读
不论是对象映射还是数据集合,Android开发者有太多数据库框架可以选择。ObjectBox数据库这个号称移动端最快的数据库框架,具有超快速、面向对象的API、单元测试即时、线程简单、数据库可自动升级等特点。本文将重点为你讲解ObjectBox数据库在Android系统中的使用。
目前安卓数据存储技术解决方案
Serializable:序列化对象为文件,并保存在文件里;
SharedPreferences:Android官方提供的缓存文件,以XML形式存储;
SQLite:官方数据库;
greenDAO(Google Room):基于SQLite的轻量级ORM;
Realm:第三方数据库;
ObjectBox:第三方数据库;
ObjectBox官网:http://objectbox.io/
用过EventBus和GreenDao的都知道他GreenRobot,而ObjectBox就是GreenRobot推出的移动端数据库架构,灵感来自于NoSql。
使用
1.引入
buildscript {ext.objectboxVersion = '1.5.0'repositories {google()jcenter()}dependencies {classpath 'com.android.tools.build:gradle:3.1.2'classpath "io.objectbox:objectbox-gradle-plugin:$objectboxVersion"}
}在 build.gradle 中dependencies 配置 在最后 添加 apply plugin: 'io.objectbox'dependencies {implementation fileTree(dir: 'libs', include: ['*.jar'])
// implementation "io.objectbox:objectbox-gradle-plugin:1.5.0"annotationProcessor "io.objectbox:objectbox-processor:1.5.0"debugImplementation "io.objectbox:objectbox-android-objectbrowser:1.5.0"releaseImplementation "io.objectbox:objectbox-android:1.5.0"
}apply plugin: 'io.objectbox'
注意:io.objectbox:objectbox-android-objectbrowser 后出现这个错误:
More than one file was found with OS independent path 'lib/armeabi-v7a/libobjectbox.so'
1.是 apply plugin: 'io.objectbox' 没有在最后添加;
2.编码
1.初始化
官方推荐在Application中初始化ObjectBox的实例:
public class MyApplication extends Application {private static BoxStore mBoxStore;@Overridepublic void onCreate() {super.onCreate();
// String inPath = getInnerSDCardPath() + "/db";
// File file= new File(inPath);
// if(!file.exists()){
// file.mkdirs();
// }
// mBoxStore = MyObjectBox.builder().androidContext(this).directory(new File(inPath)).build();mBoxStore = MyObjectBox.builder().androidContext(this).build();if (BuildConfig.DEBUG) {// 添加调试boolean start= new AndroidObjectBrowser(mBoxStore).start(this);Log.e("====start=======",start+"");}Log.d("App===", "Using ObjectBox " + BoxStore.getVersion() + " (" + BoxStore.getVersionNative() + ")");}public static BoxStore getBoxStore() {return mBoxStore;}/*** 获取内置SD卡路径** @return*/public String getInnerSDCardPath() {return Environment.getExternalStorageDirectory().getPath();}}
注意:
- 记得在 AndroidManifest 引用自定义的 Application
- MyObjectBox 直接使用时找不到, 需要创建了对应的实体类后 (Ctrl+F9 )Rebuild Project 才会出现
2.建立user 实体
@Entity
public class UserEntity {@Idprivate long id;private String userName;private int age;public long getId() {return id;}public void setId(long id) {this.id = id;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
3.获取
public class MainActivity extends AppCompatActivity {private Box<UserEntity> mBox;private Button mBtnPut;private Button mBtnGet;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);setSupportActionBar(toolbar);mBtnPut = findViewById(R.id.btn_put);mBtnGet = findViewById(R.id.btn_get);// 获取mBox = MyApplication.getBoxStore().boxFor(UserEntity.class);mBtnPut.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Long startTime = System.currentTimeMillis();// 添加for (int i = 0; i < 100; i++) {UserEntity userEntity = new UserEntity();userEntity.setAge(20);userEntity.setUserName("jayqiu" + i);mBox.put(userEntity);}Log.e("Put花费时间:", (System.currentTimeMillis() - startTime) + "");}});// 获取数据mBtnGet.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Long startTime = System.currentTimeMillis();List<UserEntity> userEntities = mBox.getAll();Log.e("Get花费时间:", (System.currentTimeMillis() - startTime) + "");if (userEntities != null && userEntities.size() > 0) {for (UserEntity user : userEntities) {Log.e("UserID===",user.getId()+"");}}}});}
}
注解 | 说明 |
---|---|
@Entity | 这个对象需要持久化。 |
@Id | 这个对象的主键。 |
@Index | 这个对象中的索引。对经常大量进行查询的字段创建索引,会提高你的查询性能。 |
@NameInDb | 有的时候数据库中的字段跟你的对象字段不匹配的时候,可以使用此注解。 |
@Transient | 如果你有某个字段不想被持久化,可以使用此注解。 |
@Relation | 做一对多,多对一的注解。 |
注意:
默认情况下,id是会被objectbox管理的,也就是自增id,如果你想手动管理id需要在注解的时候加上@Id(assignable = true)即可。当你在自己管理id的时候如果超过long的最大值,objectbox 会报错。id=0的表示此对象未被持久化,id的值不能为负数。id的数据类型只能是long。
运行后默认的数据库位置在:/data/data/包名/files/objectbox/data.mdb下,也可以在在初始化的时候定义位置:
BoxStore mBoxStore = MyObjectBox.builder().androidContext(this).directory(new File(inPath)).build();
3.调试
debugImplementation "io.objectbox:objectbox-android-objectbrowser:1.5.0"
在电脑终端执行一个adb命令:
adb forward tcp:8090 tcp:8090
在电脑端 http://localhost:8090/index.html 就可以查看数据库
注意:
boolean start=new AndroidObjectBrowser(mBoxStore).start(this);
返回为false。
添加后new AndroidObjectBrowser(mBoxStore).start(this);
返回为true,但浏览器访问不了
1.网络权限添加
<uses-permission android:name="android.permission.INTERNET" />
2.对应
annotationProcessor "io.objectbox:objectbox-processor:1.5.0"debugImplementation "io.objectbox:objectbox-android-objectbrowser:1.5.0"releaseImplementation "io.objectbox:objectbox-android:1.5.0" // 使用releaseImplementation 不能使用implementation
升华
事务:
在前文中直接的for循环的去添加数据是不正确的,而且还是在主线程中,会使APP卡顿,或者crash;
通过源码分析看到几乎所有ObjectBox的操作都涉及事务。如果你调用put方法,会使用一个写事务。
Box.javapublic long put(T entity) {Cursor<T> cursor = getWriter();try {long key = cursor.put(entity);commitWriter(cursor);return key;} finally {releaseWriter(cursor);}}void commitWriter(Cursor<T> cursor) {// NOP if TX is ongoingif (activeTxCursor.get() == null) {cursor.close();cursor.getTx().commitAndClose();}}Cursor.javapublic Transaction getTx() {return tx;}
但是对应更复杂的应用,通常值得学习事务的知识,以使您的应用程序更加高效。
BoxStore类提供以下方法来执行显式事务:
runInTx:在事务内运行runnable 。
runInReadTx:在只读事务中运行runnable 。与写入事务不同,多个读取事务可以同时运行。
runInTxAsync将给定的Runnable作为单独线程中的事务运行。一旦事务完成,给定的callback被调用(回调可能为空)。
callInTx:类似runInTx,支持一个返回值和异常抛出。
eg:
MyApplication.getBoxStore().runInTx(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 10000; i++) {UserEntity userEntity = new UserEntity();userEntity.setAge(20);userEntity.setUserName("jayqiu" + i);mBox.put(userEntity);}}});MyApplication.getBoxStore().runInTxAsync(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 10000; i++) {UserEntity userEntity = new UserEntity();userEntity.setAge(20);userEntity.setUserName("jayqiu" + i);mBox.put(userEntity);}}}, new TxCallback<Void>() {@Overridepublic void txFinished(@Nullable Void result, @Nullable Throwable error) {Log.e("Put花费时间:", (System.currentTimeMillis() - startTime) + "");if(error==null){runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(MainActivity.this,"成功",Toast.LENGTH_SHORT).show();}});}else {runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(MainActivity.this,"失败",Toast.LENGTH_SHORT).show();}});}}});
Relations
一对多,多对一@Relations
我们在现实生活中一个用户可以对应多个地址,比如家庭地址,工作地址,学校地址等
一对多
ToOne<>
多对一
ToMany<>
@Backlink
public ToMany<> ;
实体 AddressEntity.java
@Entity
public class AddressEntity {// 可以自定义ID 默认为自增@Id(assignable = true)private long addId;public ToOne<UserEntity> user;private String address;public long getAddId() {return addId;}public void setAddId(long addId) {this.addId = addId;}public ToOne<UserEntity> getUser() {return user;}public void setUser(ToOne<UserEntity> user) {this.user = user;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}
}mBtnrRlationsPut.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {UserEntity userEntity = new UserEntity();userEntity.setUserName(System.currentTimeMillis() + "NAME");userEntity.setAge(25);AddressEntity addressEntity = new AddressEntity();addressEntity.setAddress(System.currentTimeMillis()+"Lu");addressEntity.getUser().setTarget(userEntity);long addId = MyApplication.getBoxStore().boxFor(AddressEntity.class).put(addressEntity);Log.e("addId:", addId + "======");}});mBtnrRlationsGut.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {List<AddressEntity> addList = MyApplication.getBoxStore().boxFor(AddressEntity.class).getAll();Log.e("addList:", addList.size() + "======");if(addList!=null&& addList.size()>0){AddressEntity addressEntity= addList.get(0);UserEntity userEntity= addressEntity.getUser().getTarget();Log.e("userEntity:", userEntity.getUserName() + "======");}}});
id 只能是long的数据类型
错误: [ObjectBox] An @Id property has to be of type Long (com.**.objectbox.AddressEntity.addId)
更新
ObjectBox更新数据库实例
添加@Uid注解。
make project编译, 会报错, 点击as右下 Gradle Console 会有类似报错信息:
注: [ObjectBox] Starting ObjectBox processor (debug: false)
错误: [ObjectBox] UID operations for property "LocationEntity.locationTime": [Rename] apply the current UID using @Uid(3939342872662404404L) - [Change/reset] apply a new UID using @Uid(7349095691908173825L)
你的类名获取字段名称, 编译即可完成
把报错信息里后面一个新的数填写到注解里, 此处为:
@Uid(3939342872662404404L)
响应的在ObjectiveBox都有非常详细的实例
Objectbox文档:http://objectbox.io/documentation/
Objectbox API:http://objectbox.io/files/objectbox-java/current/
如何在 Android 中使用 ObjectBox 数据库框架?相关推荐
- 如何在Android中使用Realm数据库
我们都知道使用SQLite的本地数据库,它在Android 开发中用于内部存储器存储,主要存储本地数据,如联系人,电话详细信息等.现在我发现一个比SQLite更轻的数据库,被称为Realm数据库,我想 ...
- 【Android】在Android上使用OrmLite数据库框架 之 基本用法
转载自:http://blog.csdn.net/oo8_8oo/article/details/7302156 官网:http://ormlite.com/ 官方帮助,目录: http://orml ...
- 如何在android中创建自定义对话框?
本文翻译自:How to create a Custom Dialog box in android? I want to create a custom dialog box like below ...
- Android中实现SQLite数据库CRUD操作的两种方式
Android中实现SQLite数据库CRUD操作的两种方式 SQLite是一款轻量级的关系型数据库,具有运行速度.占用资源少的特点.通常只需要几百KB的内存就够了,因此特别适合在移动设备上使用.SQ ...
- android 定时器5秒执行一次,如何在android中每30秒执行一次查询?
我有一个查询,我想每30秒执行一次并将其记录到Logcat.我是通过处理程序完成的,我没有得到回应.如何在android中每30秒执行一次查询? 这里是我的代码: runnable = new Run ...
- 让一个图片填满一个控件_如何在Android中实现一个全景图控件(二)
一.背景 在 如何在Android中实现一个全景图控件(一)中,介绍了项目的一些基本情况(有 demo 演示),如果项目对你有帮助,希望文章赏个赞,项目 star 一下. 项目地址:https://g ...
- android view gesturedetector,如何在Android中利用 GestureDetector进行手势检测
如何在Android中利用 GestureDetector进行手势检测 发布时间:2020-11-26 16:15:21 来源:亿速云 阅读:92 作者:Leah 今天就跟大家聊聊有关如何在Andro ...
- php oracle 操作 sql语句中能不能添加数组_如何在PHP中使用Oracle数据库_php
在php3.0以上版本中,php内置了几乎目前所有的数据库处理函数,包括oracle;在本文中我们通过一个实例来介绍了如何使用这些函数来操作Oracle数据库. PHP提供了2大类API(应用程序接口 ...
- 技巧实例:如何在.NET中访问MySQL数据库
引言:如果你不是只在大集团公司工作过的话,你一定会有机会接触到MySQL,虽然它并不支持事务处理,存储过程,但是它提供的功能一定能满足你的大部分需求,另外,简洁的MySQL也有一些它独到的优势,在有些 ...
最新文章
- 理解CSRF(跨站请求伪造)
- 配置IP Phone在CUCME路由器上注册
- module 'bit' not found:No LuaRocks module found for bit
- JSP使用教程(第四版)学习笔记
- 微信公众号禁止分享功能
- 2022危险化学品经营单位安全管理人员特种作业证考试题库模拟考试平台操作
- 明星热图|欧阳娜娜、张艺兴代言新品牌;吴彦祖、高圆圆、全智贤演绎服装新品;关晓彤、欧阳靖、张云龙出席品牌活动...
- 快速排序——寻找数组第K大数(由浅入深,四种方法对比讲解!)
- 知乎上的一道问题:出家人怎么解决性欲?
- 微信订阅公众号如何开通文章留言评论功能?经验分享!怎样获取微信订阅公众号留言管理功能?
- Collection.sort()方法
- html做人脸识别 博客,一个人脸识别+特效的小例子
- react 函数组件父组件调用子组件方法
- Mac系统安装MATLAB 2015b 破解版
- 下载微信公众号里的视频
- Qt调用python有参和无参方法,取方法返回值
- 信号处理算法(4):全球最快的傅里叶变换算法(FFTW)
- 华山路1076号:汇丰洋行大班别墅 现上海市信息中心 上海市信息技术培训中心...
- 【转】Win10开机密码忘了?教你破解Win10开机密码
- 微信公众号主域名防封、H5域名防封怎么处理?
热门文章
- 如何计算 Pairwise correlations
- 抖音 Android 基础技术大揭秘!
- 分享7款颜色的CSS表格样式美化网页表格用户体验
- springcloud 微服务鉴权_springcloud 微服务权限校验JWT模式获取 token 实战(十二)...
- 关于联想新电脑安装win7系统的一些问题
- 扩频技术的作用及实现方法
- TYPE-C口是怎么样的接口?它有什么功能强大的地方?
- NandFlash K9F1208U0A/ K9F1208U0B的读取操作
- Kubernetes 上的服务网格技术大比较: Istio, Linkerd 和 Consul
- 第一次使用python、appium运行移动自动化遇到的报错