ObjectBox Dart/Flutter v0.11 Database: Performance & Relations

ObjectBox Dart/Flutter v0.11 Database: Performance & Relations

Flutter Databases are few. Therefore, we’re happy to take a big step towards 1.0 with this ObjectBox Dart v0.11 release, improving performance and bringing the much-desired relations support known from other ObjectBox DB language bindings to Dart/Flutter.

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:

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 Swift 1.4 – In Relation to…

ObjectBox Swift 1.4 – In Relation to…

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:

let yoda = Teacher(name: "Yoda")
yoda.students.append(Student(name: "Luke"))
yoda.students.append(Student(name: "Anakin"))
try box.put(yoda)

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.

Introducing: ObjectBox Generator, plus C++ API [Request for Feedback!]

Introducing: ObjectBox Generator, plus C++ API [Request for Feedback!]

We are introducing the ObjectBox Generator today to simplify ObjectBox development for more programming languages, starting with C/C++. Additionally, we are releasing a brand new C++ API that goes hand in hand with the new generator. Historically, our C API was rather low level as it was focused on providing the foundation for our Swift and Go APIs. With this release we want to provide C/C++ developers with ObjectBox convenience and ease of use. 

ObjectBox Generator takes over the burden of writing the binding code and data model declaration. Based on a single input file, it generates the code for you, so you can focus on the actual application logic.

Generator Example

ObjectBox let’s you handle data as FlatBuffers. For example, you can put and get data objects as FlatBuffers encoded bytes. To work with FlatBuffers, you need to define a FlatBuffer schema file (.fbs). And this file is also the input for ObjectBox Generator. This way, everything is defined in a single location.

Let’s say we have a FlatBuffers schema file “task.fbs” with the following content:

Now, we can tell ObjectBox Generator to use this file to generate C++ sources:

This makes ObjectBox Generator to generate the following files:

  • objectbox-model.h: source code to build the internal data model, that you need to pass when creating a store.
  • objectbox-model.json: keeps track of internal schema IDs; you don’t need to worry about this except that you should put it in your source control.
  • task-cpp.obx.h: the C++ value structs (data objects), binding code for FlatBuffers and the new Box class.

C++ API Example

Now, let’s use the previously generated code and the new C++ API around the Store and Box classes. A simple CRUD application boils down to a few lines:

Note that the generated code is header-only and compatible with the existing ObjectBox C-API, allowing both to be used from the same application. The C and C++ APIs both have unique advantages: the C++ API uses RAII so you do not need to worry about cleaning up, while the C API has additional features, e.g. queries.

Open Source, Docs

ObjectBox Generator is open source and available on GitHub. The repository comes with a readme file that also serves as a documentation. Among other things, you will find ObjectBox specific annotations there, which are used in fbs files to express ObjectBox-specific concerns. For example, in the definition of Task above, we used ulong as a FlatBuffers type to store dates. However, FlatBuffers does not know what a date is and we use ObjectBox annotations to express this:

For our initial release of ObjectBox Generator and the public C++ API we decided on labeling it as version 0.9. Although we are already very close to a 1.0 and we wanted to gather some feedback before our first major release. As we can still change the API or smooth out any rough edges you may find, we cannot stress enough how much we welcome and appreciate your feedback at this point. Thank you!

ObjectBox Java 2.4

ObjectBox Java 2.4

Update: newer versions were released; check the changelog for details. 

The 2.4.0 update of ObjectBox for Java (and Kotlin) is here. We encourage everyone to update to this release, as it includes quite a few quality of life improvements and resolves many of the issues that you have reported, so thank you for that!

This is also the first release where the ObjectBox LiveData and Paging integration has migrated from Android Support Libraries, to Jetpack (AndroidX) Libraries. If you are using those features, check the upgrade notes for possible changes that you need to make to your app.

Also note that this version makes some changes to the generated MyObjectBox and JSON model file. Make sure to commit changes to the model file after building your app. Also, if you are using a library that ships with a pre-generated MyObjectBox file, that library needs to be updated to 2.4.0 as well.

Besides those improvements, we were also fine-tuning performance a bit. While fixing a performance regression for 32 bit CPUs related to ordered queries, we were able to do additional optimizations. Now ordered queries using a limit run up to three times faster than before.

For a list of all the changes, please check the changelog.

Last not least, let us share some related ObjectBox’ developments in the mobile space. Today, we also released version 0.3 for ObjectBox Dart. So, if you are interested in creating Flutter apps, you will be able to use ObjectBox soon. Last month, we released ObjectBox Swift 1.0. Therefore, you can build native apps with ObjectBox for the two prominent mobile platforms, Android and iOS. Additionally, we’re also making great progress with data synchronization; sign up for sync updates to be notified sync related news and to be part of the upcoming early releases.

The swift iOS database – ObjectBox Swift 1.0 Released

The swift iOS database – ObjectBox Swift 1.0 Released

Update: newer versions have been released. Check the changelog.

ObjectBox Swift 1.0 is here! Since the first public alpha released 10 months ago, we’ve worked hard and made major changes to put Swift first, tune the performance, and iterate on the API. We hope you love the result and appreciate your feedback.

All of this, to bring you the features you expect from a database, but more importantly – the features that we think delight developers and sets ObjectBox apart from other databases out there. Let’s swiftly (cheap pun intended) dive into ObjectBox Swift 1.0:

Built with Swift in Mind

ObjectBox isn’t just a database bolted onto Swift. Your database entities are regular Swift classes or structs that you devise. No need to subclass a particular class (as with CoreData’s NSManagedObject), nor to write tedious serialization code. ?

All you need to do is add one property for the unique ID, build your project, and ObjectBox’s code generator will write a little bit of code for you, just like the Swift compiler does for Codable objects. All that’s left then, is to call a simple method like put() on the object to write it out:

We’ve tried to keep this simplicity throughout the Swift binding, e.g. making it very easy to use any RawRepresentable enum without writing any conversion code.

Automatic Schema Migrations

A common chore with databases is schema migration. ObjectBox takes care of that. If you add a new property or class there are no additional migration steps required. Old objects will keep working, and new objects will be saved with the additional fields. Similarly, adding new classes will add them to the database without any error-prone migration steps.

Moreover, you do not need to maintain a dedicated schema, because your classes and structs are the schema in the first place.

Relations

To ensure ObjectBox knows how to save object references, you use a wrapper class. Either ToOne or ToMany, instead of a straight reference or an array. This lets ObjectBox lazily load the related objects from the database, only when you’re actually accessing a related object.

The Swift 1.0 release brings you our complete set of relations: One-to-many, many-to-many, and their corresponding back-links. ToMany behaves just like any other Swift collection, you can add or remove objects as you please with your familiar methods like append().

Queries

Of course ObjectBox lets you perform queries to collect data; either complete objects or individual properties (basic Swift data types).

But with ObjectBox you don’t mess around with query strings or unpack data from cursors. You simply write Swift expressions with function calls and operators you’re already used to.

Also, you get to keep the type-safety guarantees and compile-time checking. So you don’t have to spend hours figuring out why your query doesn’t return the proper results, just to discover you made a typo in a field name in a query string.

ObjectBox lets you then operate on these objects, watch a query for changes, retrieve the results, delete the objects matching a query etc. The source code even contains a file that adds Combine support so you can integrate with its pipelines to take advantage of Apple’s newest technology.

Open Source Swift Binding

If you’re curious how things work behind the scenes, feel free to check out the Swift source code. The source code for the Swift binding, as well as our code generator based on Sourcery, are available among other projects through our Github account.

How-to Get Started

It’s a matter of minutes to get started with ObjectBox. Check our setup instructions (based on CocoaPods) and jump right into code with the getting started guide.

Your Feedback. And what’s Next?

As always, we would love to hear your feedback! Do you like ObjectBox as much as we do? We put our hearts in this product and are excited to learn your thoughts: What features are you most excited about, what are we missing?

We haven’t written much about a topic very dear to us: performance. We will cover this in a follow up post. Also, look forward to our ObjectBox Swift 1.0 benchmarks, which we will release soon including the sources.

——

Looking for a fast and simple data synchronization solution?