ObjectBox Flutter database reached 2.0 today – with new async APIs.
This release has major improvements when using ObjectBox with asynchronous programming in Dart. Notably, the Box API has added async variants of methods to get and put one or more objects.
Behind the scenes, these use the existing async API provided by Store which runs the operation on a worker isolate. With this release we have made additional improvements that allow it to be used with objects that contain relations (ToOne and ToMany). This made us comfortable to offer it through the new async convenience methods on Box and Query mentioned above.
Sometimes, it is favorable to bundle multiple database operations and execute them “at once”. This is what runInTransactionAsync() is for. It allows to use the synchronous APIs and wrap them in a single, asynchronous transaction. For example, let’s look at transferring funds between two bank accounts. The fund can safely be transferred from one account to another by reading the current balances, deducting the amount from one account and adding the amount to another account:
1
2
3
4
5
6
7
8
9
10
11
12
void_transferFunds(Store store,List<int>ids){
finalbox=store.box<account>();
finalfrom=box.get(ids[0])!;
finalto=box.get(ids[1])!;
from.balance-=100;
to.balance+=100;
box.putMany([from,to]);
}
await store.runInTransactionAsync(TxMode.write,
_transferFunds,[accountIdFrom,accountIdTo]);
</account></int>
This is more efficient than calling multiple async operations and further offers transactional safety as ObjectBox offers ACID semantics.
Ready to go? To upgrade to this major release run flutter pub upgrade objectbox --major-versions (or for Dart Native apps dart pub upgrade objectbox --major-versions ).
To add ObjectBox database to your Flutter project read our Getting Started guide.
We are happy to announce version 3.1 of ObjectBox for Java and Kotlin. The major feature of this version is the new Flex type. For a long time, ObjectBox worked on rigid data schemas, and we think that this is a good thing. Knowing what your data looks like is a feature – similar to programming languages that are statically typed. Fixed schemas make data handling more predictable and robust. Nevertheless, sometimes there are use cases which require flexible data structures. ObjectBox 3.1 allows exactly this.
Flex properties
Expanding on the string and flexible map support in 3.0.0, this release adds support for Flex properties where the type must not be known at compile time. To add a Flex property to an entity use Object in Java and Any? in Kotlin. Then at runtime store any of the supported types.
For example, assume a customer entity with a tag property:
1
// Java@Entitypublic class Customer { @Id private long id; private Object tag; // TODO getter and setter}// Kotlin@Entitydata class Customer( @Id var id: Long = 0, var tag: Any? = null)
Then set a String tag on one customer, and an Integer tag on another customer and just put them:
When getting the customer from its box the original type is restored. For simplicity the below example just casts the tag to the expected type:
1
// JavaString stringTag = (String) box.get(customerStrTag.getId()).getTag();Integer intTag = (Integer) box.get(customerIntTag.getId()).getTag();// Kotlinval stringTag = box.get(customerStrTag.id).tag as Stringval intTag = box.get(customerIntTag.id).tag as Int
A Flex property can be not justString or Integer. Supported types are all integers (Byte, Short, Integer, Long), floating point numbers (Float, Double), String and byte arrays.
It can also hold a List<Object> or a Map<String, Object> of those types. Lists and maps can be nested.
Behind the scenes Flex properties use a FlexBuffer converter to store the property value, so some limitations apply. See the FlexObjectConverter class documentation for details.
Query for map keys and values
If the Flex property contains integers or strings, or a list or map of those types, it’s also possible to do queries. For example, take this customer entity with a properties String to String map:
1
// Java@Entitypublic class Customer { @Id private long id; private Map<String, String> properties; // TODO getter and setter}// Kotlin@Entitydata class Customer( @Id var id: Long = 0, var properties: MutableMap<String, String>? = null)
Why is properties not of type Object? ObjectBox supports using Map<String, String> (or Map<String, Object>) directly and will still create a Flex property behind the scenes.
Then put a customer with a premium property:
1
// JavaCustomer customer = new Customer();Map<String, String> properties = new HashMap<>();properties.put("premium", "tier-1");customer.setProperties(properties);box.put(customer);// Kotlinval customer = Customer( properties = mutableMapOf("premium" to "tier-1"))box.put(customer)
To query for any customers that have a premium key in their properties map, use the containsElement condition:
ObjectBox database is free to use. Check out our docs and this video tutorial to get started today.
We strive to bring joy to mobile developers and appreciate all kinds feedback, both positive and negative. You can always raise an issue on GitHub or post a question on Stackoverflow. Otherwise, star the ObjectBox Java database GitHub repo and up-vote the features you’d like to see in the next release.
The Android database for superfast Java / Kotlin data persistence goes 3.0. Since our first 1.0-release in 2017 (Android-first, Java), we have released C/C++, Go, Flutter/Dart, Swift bindings, as well as Data Sync and we’re thrilled that ObjectBox has been used by over 800,000 developers.
We love our Java / Kotlin community ❤️ who have been with us since day one. So, with today’s post, we’re excited to share a feature-packed new major release for Java Database alongside CRUD performance benchmarks for MongoDB Realm, Room (SQLite) and ObjectBox.
What is ObjectBox?
ObjectBox is a high performance database and an alternative to SQLite and Room. ObjectBox empowers developers to persist objects locally on Mobile and IoT devices. It’s a NoSQL ACID-compliant object database with an out-of-the-box Data Sync providing fast and easy access to decentralized edge data (Early Access).
// Kotlin// equal AND (less OR oneOf)val query = box.query( User_.firstName equal "Joe" and (User_.age less 12 or (User_.stamp oneOf longArrayOf(1012)))) .order(User_.age) .build()// Java// equal AND (less OR oneOf)Query<User> query = box.query( User_.firstName.equal("Joe") .and(User_.age.less(12) .or(User_.stamp.oneOf(new long[]{1012})))) .order(User_.age) .build();
In Kotlin, the condition methods are also available as infix functions. This can help make queries easier to read:
1
val query=box.query(User_.firstName equal"Joe").build()
Unique on conflict replace strategy
One unique property in an @Entity can now be configured to replace the object in case of a conflict (“onConflict”) when putting a new object.
1
// Kotlin@Entitydata class Example( @Id var id: Long = 0, @Unique(onConflict = ConflictStrategy.REPLACE) var uniqueKey: String? = null)// Java@Entitypublic class Example { @Id public long id; @Unique(onConflict = ConflictStrategy.REPLACE) String uniqueKey;}
This can be helpful when updating existing data with a unique ID different from the ObjectBox ID. E.g. assume an app that downloads a list of playlists where each has a modifiable title (e.g. “My Jam”) and a unique String ID (“playlist-1”). When downloading an updated version of the playlists, e.g. if the title of “playlist-1” has changed to “Old Jam”, it is now possible to just do a single put with the new data. The existing object for “playlist-1” is then deleted and replaced by the new version.
Built-in string array and map support
String array or string map properties are now supported as property types out-of-the-box. For string array properties it is now also possible to find objects where the array contains a specific item using the new containsElement condition.
1
// Kotlin@Entitydata class Example( @Id var id: Long = 0, var stringArray: Array<String>? = null, var stringMap: MutableMap<String, String>? = null)// matches [“first”, “second”, “third”]box.query(Example_.stringArray.containsElement(“second”)).build()// Java@Entitypublic class Example { @Id public long id; public String[] stringArray; public Map<String, String> stringMap;}// matches [“first”, “second”, “third”]box.query(Example_.stringArray.containsElement(“second”)).build();
Kotlin Flow, Android 12 and more
Kotlin extension functions were added to obtain a Flow from a BoxStore or Query:
1
val flow=box.query().subscribe().toFlow()
Data Browser has added support for apps targeting Android 12.
We compared against the Android databases, MongoDB Realm and Room (on top of SQLite) and are happy to share that ObjectBox is still faster across all four major database operations: Create, Read, Update, Delete.
We benchmarked ObjectBox along with Room 2.3.0 using SQLite 3.22.0 and MongoDB Realm 10.6.1 on an Samsung Galaxy S9+ (Exynos) mobile phone with Android 10. All benchmarks were run 10+ times and no outliers were discovered, so we used the average for the results graph above. Find our open source benchmarking code on GitHub and as always: feel free to check them out yourself. More to come soon, follow us on Twitter or sign up to our newsletter to stay tuned (no spam ever!).
Using a fast on-device database matters
A fast local database is more than just a “nice-to-have.” It saves device resources, so you have more resources (CPU, Memory, battery) left for other resource-heavy operations. Also, a faster database allows you to keep more data locally with the device and user, thus improving privacy and data ownership by design. Keeping data locally and reducing data transferal volumes also has a significant impact on sustainability.
Sustainable Data Sync
Some data, however, you might want or need to synchronize to a backend. Reducing overhead and synchronizing data selectively, differentially, and efficiently reduces bandwidth strain, resource consumption, and cloud / Mobile Network usage – lowering the CO2 emissions too. Check out ObjectBox Data Sync, if you are interested in an out-of-the-box solution.
Get Started with ObjectBox for Java / Kotlin Today
Already an ObjectBox Android database user and ready to take your application to the next level? Check out ObjectBox Data Sync, which solves data synchronization for edge devices, out-of-the-box. It’s incredibly efficient and (you guessed it) superfast 😎
We ❤️ your Feedback
We believe, ObjectBox is super easy to use. We are on a mission to make developers’ lives better, by building developer tools that are intuitive and fun to code with. Now it’s your turn: let us know what you love, what you don’t, what do you want to see next? Share your feedback with us, or check out GitHub and up-vote the features you’d like to see next in ObjectBox.
In 2019 we first introduced the ObjectBox database v0.1 for Flutter/Dart. Our team has loved the engagement and feedback we’ve received from the developer community since, and we’re thrilled to announce the first stable version 1.0 for ObjectBox Dart/Flutter today.
With this release we bring you the fast and easy to use ObjectBox database for Dart objects: optimized for high performance on mobile and desktop devices. ObjectBox persists your Dart objects (null safe, of course) and comes with relations, queries, transactions, and Data Sync. For a feature list and more, please also check the pub.dev page.
ObjectBox by Example
For those of you new to ObjectBox, here is how you can use it (or check the docs if you want to dive deep right away). By annotating a class with @Entity you tell ObjectBox that you want to persist its objects, which is done putting the object in a Box:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Entity()
classPerson{
intid=0;
StringfirstName;
StringlastName;
Person(this.firstName,this.lastName);
}
// Open a Store once and keep it open to get type specific Boxes
finalstore=Store(getObjectBoxModel());
finalbox=store.box<Person>();
// let’s put a new Person in the database
varperson=Person("Marty","McFly");
finalid=box.put(person);
What’s new with the 1.0?
Version 1.0 delivers a stabilized API and adds new essential features like async writes and query streams. We’ve also extended support for Flutter desktop. Let’s look at queries and how they can be used depending on the use case:
1
2
3
4
5
6
7
8
9
10
11
12
// find all people whose name starts with "Mc"; 1. Query variant
There are two new approaches to do async puts for asynchronous database writes: putAsync() returns a Future to check if the call was successful.
1
2
3
4
Future<int>idFuture=box.putAsync(person);
...
finalid=await idFuture;
userBox.get(id);// after the Future completed, the object is stored in the database
Or you can use a background queue if you don’t need individual Futures, the following code inserts 100 objects and only waits once:
1
2
3
4
5
6
for(inti=0;i<100;i++){
box.putQueued(Person(...));
}
store.awaitAsyncSubmitted();
// after `awaitAsync*`: objects are inserted
expect(box.count(),equals(100));
If you are interested in further improvements we made to 1.0, please check out the full changelog.
Dart Flutter Database Benchmarks
ObjectBox Dart v1.0 also comes with considerable optimizations bringing a new level of database performance to Flutter apps. ObjectBox enables data-heavy apps that were not possible on Flutter before. Consider this a first sneak-peek; stay tuned for detailed performance benchmarks to be released including queries (hint: they are really fast) along with updated benchmarking code.
What we tested
We looked at some two popular approaches: sqflite, a SQLite wrapper for Flutter (no Dart Native support), and Hive, a key-value store with Class-adapters which seems still popular although its creator abandoned it for architectural shortcomings (it has memory problems and does not support queries). In the previous benchmark we’ve also had a look at Firestore, but being an online-only database it was thousands of times slower than the rest so we’ve left it to rest this time around. Check our previous benchmark if you’re interested.
To get an overview of the databases, we tested CRUD operations (create, read, update, delete). Each test was run multiple times and executed manually outside of the measured time. Data preparation and evaluation were also done outside of the measured time.
Looking at the results, we can see ObjectBox performing significantly faster than sqflite across the board, with up to 100 time speed-up in case of create & update operations. Compared to Hive, the results are a little closer in some cases (read) though ObjectBox still comes out on top in all the metrics. Considering that Hive keeps all Dart objects in memory (!) while ObjectBox does not, should give you a good impression of how fast object persistence with ObjectBox is.
ObjectBox Database for Flutter/Dart Highlights
For those of you new to ObjectBox, here’s a quick summary of what our super-fast embedded database offers, out of the box:
automatic schema migration: adding new classes or fields just works
type-safe APIs, e.g. no interface{} arguments
embedded edge database – no server needed, store all data directly on the device
no ORM, no SQL
relations: to-one, to-many (eager and lazy fetching)
robust query support, including indexes for scalable lookups
Support for implicit (automatic) and explicit (user defined)
transactions: ACID compliant with superfast bulk/batch operations
low memory usage
runs across operating systems: 64-bit Linux, macOS, Windows, small 32-bit ARM-based Linux devices (e.g. Raspberry Pi)
Data Sync: an efficient and easy way to synchronize data between your app and the cloud
Getting Started with ObjectBox for Flutter/Dart Today
Now it’s your turn: let us know what you love, what you don’t, what do you want to see next? Share your feedback with us, or check out GitHub and up-vote the features you’d like to see next in ObjectBox.
For those of you new to ObjectBox: ObjectBox is a superfast NoSQL object database for Flutter / Dart and here is how you can save data in your Dart / Flutter apps:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Entity()
classPerson{
intid;
StringfirstName;
StringlastName;
}
finalstore=Store(getObjectBoxModel());
finalbox=store.box<Person>();
varperson=Person()
..firstName="Joe"
..lastName="Green";
finalid=box.put(person);// Create
person=box.get(id);// Read
person.lastName="Black";
box.put(person);// Update
box.remove(person.id);// Delete
// find all people whose name start with letter 'J'
To learn about more ObjectBox features, like relations, queries and data sync, check our ObjectBox pub.dev page.
How fast is ObjectBox Dart? Performance Benchmarks
Speed is important for data persistence solutions. Accordingly, we wanted to test how ObjectBox compares performance-wise to other Flutter Dart database options. Therefore, we looked for libraries with comparable levels of storage abstraction and feature set – so not just plain SQL/Key-value storage but also ORM-like features. There doesn’t seem to be that much choice…
We looked at some two popular approaches: sqflite a SQLite wrapper for Flutter (no Dart Native support), and Hive, a key-value store with Class-adapters which seems still popular although its technology is phased out (see below). As a third alternative we pulled in Firestore, which does not really fit as it is no local database, but would be fun to compare anyway.
What we tested
To get an overview of the databases, we tested CRUD operations (create, read, update, delete). Each test was run multiple times and executed manually outside of the measured time. Data preparation and evaluation was also done outside of the measured time.
We tried to keep the test implementations as close as possible to each other while picking the approaches recommended by the docs for each database. We open sourced the test code at https://github.com/objectbox/objectbox-dart-performance if you want to have a closer look.
Performance Benchmark Results
Looking at the results, we can see ObjectBox performing significantly faster than sqflite across the board, with up to 70 times speedup in case of create & update operations. Compared to Hive, the results are a little more varied, with Hive being faster at reading objects than ObjectBox (we come to that later in our outlook), and ObjectBox being faster at creating objects, about four times faster at updates and three times for deletes. As a mostly-online database, it becomes clear that Firestore’s performance is not really comparable.
Implementation notes
ObjectBox: This release largely boosted the performance. The remaining bottlenecks are due to Dart itself and how it allows to modify byte buffers. There’s potential to double the speed if we look at other languages supported by ObjectBox. And if that’s not happening soon, we’d still have the option to do some low-level hacks…
Sqflite: a wrapper around SQLite, which is a relational database without direct support for Dart objects. Each dart object field is mapped to a column in the database, as per sqflite docs, i.e. converting between the Dart class and a Map.
Hive: We’ve tested with the latest Hive release, which is technically discontinued. The author hit two architectural roadblocks (RAM usage and queries) and is currently in the process to do a rewrite from scratch. Update: strictly speaking it’s not straightforward to directly compare e.g. ObjectBox vs. Hive. In Hive, the high read numbers result from Dart objects already cached in memory. If the objects are fetched using the async API from disk, the numbers drop by factor 1000.
Firestore: This is totally apples and oranges, but we still decided to include Firebase/Firestore as it seems at least somewhat popular to “persist data”. It’s quite Cloud centric and thus offers limited offline features. For example, in order to use batches (“transactions”), an internet connection is required to “commit”. Also, due to its low performance, the test configuration was different: batches of 500 objects and only 10 runs.
Test setup
We ran the benchmarks as a Flutter app on a Android 10 device with a Kirin 980 CPU. The app executed all operations in batches of 10.000 objects, with each batch forming a single transaction. Each test was run 50 times, averaging the results over all the runs. This ensured the VM warmup (optimization) during the first run and garbage collections don’t affect the overall result significantly. (We care about accurate benchmarks; read more about our benchmarking best practices here.)
Outlook
With this latest release, we’re not far away from a stable API for a 1.0 release (🎉), so please share your thoughts and feedback. For the next release, we’ll add features like async operations, more relation types and some smaller improvements. We are also working on an ObjectBox variant for the Web platform that is planned close to the 1.0 release. And of course there is ObjectBox Data Sync for Flutter/Dart. If you want to be first in line to try, drop us a line, we can put you on the shortlist.
ObjectBox for Swift 1.4 makes object relations more natural and intuitive for Swift developers. For example, let’s take the teacher-student relation to Swift and how you store objects in the database. Let’s say “Teacher” is a Swift class that has a collection called “students”. Now let’s say we have a new teacher with new students and want to store them in the ObjectBox database. It’s done like this:
This is pretty much standard Swift. A single put command is enough to store all three new objects in the database (sorry for the “try”, Yoda, but you know, IO…). Now let’s see how this works. The students’ property in the Teacher’s class is of type ToMany<Student> and works like any Swift collection. This is because ToMany implements the protocols RandomAccessCollection and RangeReplaceableCollection. Under the hood however, it tracks all changes. Thus, when ObjectBox is instructed to put Yoda in a box, it also knows that two students were added. It also knows that our two Jedi students are new and thus puts them in database too. If you supply students that have been already persisted, it won’t put them. You can also mix new and existing objects.
Version 1.4 does not only bring TooMany (sic) improvements, but also brings a couple of new features, e.g. a bulk-get and read-only stores. You also may have heard of Sync (some kind of teleportation for objects, my young padawan). We’re still working on that, but we started to expose the Sync API with this release. It doesn’t come with any (space consuming) implementation so it’s really about getting early awareness and feedback. A full changelog is available at the docs.
So, time to start your (cocoa) pod again and let us know what you think. May the for… um, OK, that’s getting too many references for one article. One to many.
We use cookies to ensure that we give you the best experience on our website. If you continue to use this site we will assume that you are happy with it.Ok