Getting started

ObjectBox Generator produces binding code for ObjectBox C and C++ APIs (more languages to be supported in the future). This greatly simplifies the model declaration and FlatBuffers serialization, allowing you to concentrate on the actual application logic. Most of this documentation expects you to have the generator installed and available in $PATH, unless specified otherwise. Therefore, you might want to have a look at the installation page first.

Generating binding code

ObjectBox Generator uses FlatBuffer schema file (.fbs) as its primary input. The Generator also maintains some metadata around the data model in a JSON file (objectbox-model.json). Based on these two files, it generates code for the selected language (C or C++).

Let’s have a look at a sample schema and how Generator helps us.

tasklist.fbs
table Task {
id: ulong;
text: string;
date_created: ulong;
date_finished: ulong;
}

Launch the following command to generate the binding code from the FlatBuffers schema file:

C++
C
C++
objectbox-generator -cpp tasklist.fbs

The following files will be generated:

  • objectbox-model.h

  • objectbox-model.json

  • tasklist-cpp.obx.h

C
objectbox-generator -c tasklist.fbs

The following files will be generated:

  • objectbox-model.h

  • objectbox-model.json

  • tasklist.obx.h

You should add all these generated files to your source control (e.g. git), most importantly objectbox-model.json which ensures compatibility with previous versions of your database after you make changes to the schema.

Working with Object Boxes

Bet you wondered where our name comes from :)

From ObjectBox you vend Box instances to manage your entities. While you can have multiple Box instances of the same type (for the same Entity) "open" at once, it's usually preferable to just use one instance and pass it around your code.

Now, you can include the generated headers in your application and start working with your database. Consider the following main file:

C++
C
C++
main.cpp
#include "objectbox-cpp.h"
#include "objectbox-model.h"
#include "tasklist-cpp.obx.h"
int main(int argc, char* args[]) {
// create_obx_model() provided by objectbox-model.h
// obx interface contents provided by objectbox-cpp.h
obx::Store store(create_obx_model());
obx::Box<Task> box(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
}
return 0;
}
C
main.c
#include "objectbox.h"
#include "objectbox-model.h"
#include "tasklist.obx.h"
obx_err print_last_error() {
printf("Unexpected error: %d %s\n",
obx_last_error_code(), obx_last_error_message());
return obx_last_error_code();
}
obx_id task_put(OBX_box* box, Task* task) {
flatcc_builder_t builder;
flatcc_builder_init(&builder);
size_t size = 0;
void* buffer = NULL;
// Note: Task_to_flatbuffer() is provided by the generated code
obx_id id = 0;
if (Task_to_flatbuffer(&builder, task, &buffer, &size)) {
id = obx_box_put_object(box, buffer, size, OBXPutMode_PUT); // 0 on error
}
flatcc_builder_clear(&builder);
if (buffer) flatcc_builder_aligned_free(buffer);
if (id == 0) {
// TODO: restructure; won't print the right error if it occurred
// in Task_to_flatbuffer(), i.e. outside objectbox
print_last_error();
} else {
task->id = id; // update the ID property on new objects for convenience
}
return id;
}
Task* task_read(OBX_store* store, OBX_box* box, obx_id id) {
OBX_txn* txn = NULL;
// We need an explicit TX - read data lifecycle is bound to the open TX.
// The transaction can be closed after reading the object from flatbuffers.
txn = obx_txn_read(store);
if (!txn) {
print_last_error();
return NULL;
}
void* data;
size_t size;
int rc = obx_box_get(box, id, &data, &size);
if (rc != OBX_SUCCESS) {
// if (rc == OBX_NOT_FOUND); // No special treatment at the moment
obx_txn_close(txn);
return NULL;
}
Task* result = Task_new_from_flatbuffer(data, size);
obx_txn_close(txn);
return result;
}
int main(int argc, char* args[]) {
int rc = 0;
OBX_store* store = NULL;
OBX_box* box = NULL;
Task* task = NULL;
// Firstly, we need to create a model for our data and the store
{
OBX_model* model = create_obx_model(); // defined in objectbox-model.h
if (!model) goto handle_error;
if (obx_model_error_code(model)) {
printf("Model definition error: %d %s\n",
obx_model_error_code(model), obx_model_error_message(model));
obx_model_free(model);
goto handle_error;
}
OBX_store_options* opt = obx_opt();
obx_opt_model(opt, model);
store = obx_store_open(opt);
if (!store) goto handle_error;
// obx_store_open() takes ownership of model and opt and frees them.
}
box = obx_box(store, Task_ENTITY_ID); // Note the generated "Task_ENTITY_ID"
obx_id id = 0;
{ // Create
Task task = {.text = "Buy milk"};
id = task_put(box, &task);
if (!id) goto handle_error;
printf("New task inserted with ID %d\n", id);
}
{ // Read
task = task_read(store, box, id);
if (!task) goto handle_error;
printf("Task %d read with text: %s\n", id, task->text);
}
{ // Update
const char* appendix = " & some bread";
// updating a string property is a little more involved
size_t old_text_len = task->text ? strlen(task->text) : 0;
char* new_text =
(char*) malloc((old_text_len + strlen(appendix) + 1) * sizeof(char));
if (task->text) {
memcpy(new_text, task->text, old_text_len);
// free the memory allocated previously before overwritting below
free(task->text);
}
memcpy(new_text + old_text_len, appendix, strlen(appendix) + 1);
task->text = new_text;
printf("Updated task %d with a new text: %s\n", id, task->text);
}
// Delete
if (obx_box_remove(box, id) != OBX_SUCCESS) goto handle_error;
free_resources: // free any remaining allocated resources
if (task) Task_free(&task); // free allocs by Task_new_from_flatbuffer()
if (store) obx_store_close(store); // and close the store
return rc;
handle_error: // print error and clean up
rc = print_last_error();
if (rc <= 0) rc = 1;
goto free_resources;
}

If you've followed the installation instructions, you should be able to compile the example by running:

C++
C
C++
g++ main.cpp -I. -std=c++11 -lobjectbox
C
gcc main.c -I. -lobjectbox -lflatccrt

The command snippet assumes you have the libraries installed in a path recognized by your OS (e.g. /usr/local/lib/) and all the referenced headers are in the same folder alongside the main.c/.cpp file.

Wherever you have access to a Box, you can use it to persist objects and fetch objects from disk. Boxes are thread-safe. Here are some of the basic operations, have a look at the objectbox.h/-cpp.h for more:

  • put: persist an object at the given ID: either creating a new one or overwriting an existing one.

  • get: read an object from the database. There's also a variant that takes a list of IDs as an argument and returns multiple objects.

  • remove: deletes a previously persisted object from its box.

  • count: the number of objects stored in this box.