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:
1
2
3
4
5
6
tableTask{
id:ulong;
text:string;
date_created:ulong;
date_finished:ulong;
}
Now, we can tell ObjectBox Generator to use this file to generate C++ sources:
1
./objectbox-generator-cpp task.fbs
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:
#include "task-cpp.obx.h" // provides Task struct and bindings
intmain(intargc,char*args[]){
obx::Store store(create_obx_model());
obx::Box<Task>taskBox(store);
obx_id id=box.put({.text="Buy milk"});// Create
std::unique_ptr<Task>task=box.get(id);// Read
if(task){
task->text+=" & some bread";
box.put(*task);// Update
...
box.remove(id);// Delete
}
...
}
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:
1
2
/// objectbox: date
date_created:ulong;
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!
The 1.1 release of ObjectBox for Go is now available, bringing new features such as Box insert() and update() semantics, a new AsyncBox with all write operations (put, insert, update, delete), improved Queries with order and aliases; as well as some fixes and quality of life improvements, such as time.Time support or more forgiving generator code validation. For the full list of changes see the changelog.
To upgrade to the latest version, run go get -u in your project and don’t forget to re-run the generator to make sure all the code is in sync and you get the new features:
1
2
go get-ugithub.com/objectbox/objectbox-go
go generate./…
Async Box
The new AsyncBox gives you asynchronous processing for write operations such as Put, Insert, Update, Remove, RemoveId.
First a quick reminder how a standard (synchronous) Box works:
1
2
3
4
5
6
7
8
9
10
11
box:=model.BoxForTask(ob)
// Create
id,_:=box.Put(&model.Task{
Text:"Buy milk",
})
task,_:=box.Get(id)// Read
task.Text+=" & some bread"
box.Put(task)// Update
box.Remove(task)// Delete
Now, let’s have a look at the new AsyncBox. Let’s say tasks are processed in multiple iterations by calling a “WorkOn(*Task)”. Let’s also assume that WorkOn() sets a “finished” flag on the object if it was able to complete a task in an iteration. In that case, the task can be removed from the database. Otherwise, partial progress on the task should be saved for the next iteration.
1
2
3
4
5
6
7
8
9
10
11
12
13
asyncBox:=box.Async()
vartasks=QueryTasksToWorkOn()
for_,task:=rangetasks{
WorkOn(task)// Consumes some CPU time
iftask.finished{
// Remove a finished task
asyncBox.Remove(task)
}else{
// Save progress on task
asyncBox.Update(task)
}
}
So, what’s the advantage of using AsyncBox in this example? Because we don’t wait for updating or removing a task, we just created an efficient pipeline: we can spend all computational resources on WorkOn(), while AsyncBox performs persistence in the background. Both steps never have to wait for each other.
The second advantage of AsyncBox is “transaction merging.” Because “WorkOn” takes some time, we operate on a single object at a time. A synchronous solution would require a transaction per object, introducing significant disk overhead. AsyncBox can reduce the amount of transactions required and thus dramatically improve throughput.
You may also have noted the usage of “Update()” instead of the standard “Put”. An update is different from a put as it only persists the object if it already exists in the database. Let’s say our example has another process that removes Tasks; a standard put operation might “resurrect” a task previously removed by the other process. If we don’t want that to happen, we can use update semantics. The new update and insert operations are also available in the standard Box API.
Please let us, and everyone else, know what you like about this release and ObjectBox in general. We’d love to hear from you to know what you’d like to see next.
Looking for an easy way to sync data between devices? Check out ObjectBox Sync, sign up for early access, and look out for the release early 2020!
With EdgeX Foundry just reaching v1.1, we continue to provide ObjectBox as an embedded high-performance database backend so you can start using ObjectBox EdgeX v1.1 right away. If you need data reliability and high-speed database operations, ObjectBox is for you. Additionally, starting with ObjectBox EdgeX 1.1, you can use it on 32-bit ARM devices.
Combining the speed and size advantages of ObjectBox on the EdgeX platform, we empower companies to analyze more data locally on the machine, enabling new use cases.
With ObjectBox-backed EdgeX we’re bringing the efficiency, performance and small footprint of the ObjectBox database to all EdgeX applications. It is fully compatible, so you can use it as a drop-in replacement: you call against the same REST and Go EdgeX APIs. As simple as that;no need to change any code.
Performance comparison of EdgeX database backends
EdgeX Foundry comes with a choice of two database engines: MongoDB and Redis. ObjectBox EdgeX brings an alternative to Redis and MongoDB to the table. Because ObjectBox is an embedded database, optimized for high speed and ease of use while also delivering data reliability, it enables a new set of use cases. As we all know, benchmarks are hard to do. This is why all our benchmarks are open source and we invite you to check them out for yourself. To give you a quick impression of how you could benefit from using ObjectBox, let’s have a look at how each compares in basic database operations on “Device Readings”, one of the most performance intensive data points.
Note: The Read and Write operations (all CRUD (Create, Read, Update, Delete) operations are measured in objects / second). The benchmarks test internal EdgeX database layer performance, not the REST APIs throughput.
These benchmarks provide a good perspective why you should consider ObjectBox with EdgeX. Benchmark sources are available publicly in ObjectBox EdgeX github repo.
So, why is ObjectBox EdgeX faster?
First of all, you are probably aware of the phrase “Lies, damned lies, and statistics benchmarks”. Of course, you should look at performance for yourself and consider based on your specific use case needs. That’s why we make our benchmarks available as open source. It is a good starting point.
To make it easier to compare ObjectBox (in addition to our open source benchmarks) here are some of the high-level “unfair advantages” that make ObjectBox fast:
Objects: As you can derive from its name, ObjectBox is all about for objects. It’s highly optimized for persisting objects. The EdgeX architecture and Go sources are a great fit here as it puts Go’s objects (structs) in the center of its interface. This means, we can omit costly transformations and this helps with speed.
Embedded database: Redis and MongoDB are client/server databases running in separate processes. ObjectBox, however, is running in the same process as EdgeX itself (each EdgeX microservice, to be precise). This has definite efficiency advantages, but it also comes with some restrictions: Whereas you can put Redis/MongoDB in separate Dockers or machines, this option is not available for ObjectBox yet.
Transaction merging: ObjectBox can execute individual write operations in a common database transaction. This means, we can reduce the costly transactions for a number of write operations. This is a great way to add on performance, delaying the transaction end by single digit milliseconds.
Get started with ObjectBox EdgeX
The simplest way to get started is to fetch the latest docker-compose.yml and start the containers:
1
wget https://raw.githubusercontent.com/objectbox/edgex-objectbox/fuji/bin/docker-compose.ymldocker-compose up -d
You can check the status of your running services by going to http://localhost:8500/. At this point, you have the REST services running at their respective ports, available to access from your EdgeX applications.
If you’re new to EdgeX, find out all about the open source IoT Edge Platform here. The EdgeX project is led by the Linux Foundation and supported by many industry players, including Dell, IBM, and Fujitsu.
We love to hear from you ?
We’re very interested to hear about the challenges you are facing on the edge and in IoT. As performance experts, we are always embracing a tough challenge. Reach out to us to set up a pilot project.
Is there something you are missing? Please do reach out to us. We want to make ObjectBox the best edge data persistence layer available. We love to receive your feedback.
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.
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
1
2
3
4
5
6
7
8
9
10
// objectbox: entity
classOlympian{
varid:Id=0
...
}
let store=tryStore(directoryPath:dbURL.path)
let box=store.box(for:Olympian.self)
let olympians:[Olympian]=trybox.all()
let goldMedalist:Olympian=trybox.get(sarahHoefflinId)
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:
1
trybox.put(ruthJebet)
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
1
2
3
4
5
6
7
8
classAuthor:Entity{
varid:Id=0
varname:String
varbooks:ToMany<Book>=nil
...
}
amandaPalmer.books.append(theArtOfAsking)
tryamandaPalmer.books.applyToDb()
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
1
2
3
4
5
let query=trybox.query{Author.fname=="Jeaniene"}.build()
let results=tryquery.find()// All Authors matching query.
// All last names of the matching authors:
let names=tryquery.property(Author.lname)
.distinct().findStrings()
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.
1
2
3
4
5
subscription=query.subscribe(){authors,error in
iflet error=error{self.reportError(error);return}
self.authors=authors
self.tableView.reloadData()
}
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 Swift database source code, 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?
Today’s 0.9 release is not just a step closer to 1.0, but also an important milestone for us: for the first time, all Swift binding source code is available on GitHub. We’ve also improved performance (again) and added useful features like type converters.
ObjectBox’ lightning-fast database offers a cross-platform C API for language-specific bindings. These bindings provide a thin and nimble wrapper around the C calls and expose ObjectBox functionality in the way that feels most natural to the host language (i.e. Swift).
An open-source binding allows you to make changes to ObjectBox and build it on your own, backport it to older OS or Swift versions, and contribute your changes back to us if you feel so inclined.
Is this the binding I’ve been using all this time?
Although it’s one of our newer language bindings, our Swift binding already has a storied and colorful history. The first Swift binding actually predates our C library and was built directly against the core C++ codebase. As such, it used Objective-C to bridge Swift and C++.
Once we had the C library, we set about eliminating the intermediary and started rewriting the central Objective-C portions as pure Swift on top of the C API. The immediate result when we released ObjectBox for Swift 0.8, was a significant speed-up in write operations due to Swift’s new native UTF-8 strings, and struct support.
But for the open source release, we completed this rewrite, and ObjectBox Swift got even faster:
Not having to bridge to Objective-C meant we could take advantage of Swift’s associated types to resolve class/entity associations instead of a runtime look-up, and eliminate a few duplicated classes necessitated by adding Swift features to Objective-C classes. This in turn allowed removing some unnecessary data copying, which translated directly to increased speed.
While we enjoy a good performance increase as much as the next database developer, we also added new features and fixes, like more query comparison operators for scalar types, and improved behavior when adding properties to an entity.
Property Converters
One major new feature of this release is support for converting simple user-defined types into types the database understands. For example, you can mark any RawRepresentable enum with an annotation to tell ObjectBox how to serialize it. Let’s say we have an enum like
1
2
3
4
5
6
enumOlympicRanking:Int{
caseunset=0
casefirst=100
casesecond=200
casethird=300
}
In your class, you simply add the following annotation:
But of course we also let you work with your own types, or system types. Let’s say you wanted to save a window’s dimensions into your database. You do this using a converter class that implements convert() methods that convert your custom type into one of ObjectBox’s built-in types, and back:
1
2
3
4
5
6
7
8
9
classRectConverter{
staticfunc convert(_box:CGRect)->String{
returnNSCoder.string(for:box)
}
staticfunc convert(_boxString:String?)->CGRect{
guard let boxString=boxStringelse{return.zero}
returnNSCoder.cgRect(for:boxString)
}
}
And then you tell ObjectBox to use that converter using an annotation:
And that’s just a selection of the changes you’ll find in the new open-source release. It contains the entire binding, but also the fork of Sourcery that does its duties as our code generator, and a performance test app. It is faster, more feature-full, and we’ve also squashed some bugs in the binding.
To take a look and get started, simply go to our ObjectBox Swift Git repository, where, in addition to our example application, you can now also find the binding’s source code. Or pull version 0.9 via CocoaPods.
We can’t wait for you to try it out and let us know what you think. Don’t forget to star us if you like it!
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