Links

Getting started

Discover ObjectBox: The Lightning-Fast Mobile Database for Persistent Object Storage. Streamline Your Workflow, Eliminate Repetitive Tasks, and Enjoy a User-Friendly Data Interface.

Add ObjectBox to your project

Java/Kotlin (Android)
Java/Kotlin (JVM)
Flutter
Dart Native
Video Tutorial on Getting Started with ObjectBox for Android and Java
You can get ObjectBox from the Central repository. To add ObjectBox to your Android project, follow these steps:
  1. 1.
    Open the root build.gradle file of your project (not the ones for your app or module) and add a global variable for the version and the ObjectBox Gradle plugin:
buildscript {
ext.objectboxVersion = "3.7.0"
repositories {
mavenCentral()
// Note: 2.9.0 and older are available on jcenter()
}
dependencies {
// Android Gradle Plugin 3.4.0 or later supported.
classpath("com.android.tools.build:gradle:8.0.0")
classpath("io.objectbox:objectbox-gradle-plugin:$objectboxVersion")
}
}
  1. 2.
    Open the build.gradle file for your app or module and, after the com.android.application plugin, apply the io.objectbox plugin:
// Using plugins syntax:
plugins {
id("com.android.application")
id("kotlin-android") // Only for Kotlin projects.
id("kotlin-kapt") // Only for Kotlin projects.
id("io.objectbox") // Apply last.
}
// Or using the old apply syntax:
apply plugin: "com.android.application"
apply plugin: "kotlin-android" // Only for Kotlin projects.
apply plugin: "kotlin-kapt" // Only for Kotlin projects.
apply plugin: "io.objectbox" // Apply last.
If you encounter any problems in this or later steps, check out the FAQ and Troubleshooting pages.
  1. 3.
    Then do "Sync Project with Gradle Files" in Android Studio so the Gradle plugin automatically adds the required ObjectBox libraries and code generation tasks.

Optional: Advanced Setup

The ObjectBox plugin uses reasonable defaults and detects most configurations automatically. However, if needed you can configure the model file path, the MyObjectBox package, enable debug mode and more using advanced setup options.
ObjectBox for Java supports JVM (Linux, macOS, Windows) Java or Kotlin projects.
It ships with a Gradle plugin. To apply it, your project needs to use Gradle as the build system.
There is an experimental Maven plugin available. See the Java Maven example. We welcome your feedback if supporting Maven is of interest to you.
ObjectBox is available from the Central repository.
To add the ObjectBox plugin:
  1. 1.
    Open the root build.gradle file of your project (not the ones for your app or module) and add a global variable for the version and the ObjectBox Gradle plugin:
buildscript {
ext.objectboxVersion = "3.7.0"
repositories {
mavenCentral()
// Note: 2.9.0 and older are available on jcenter()
}
dependencies {
classpath("io.objectbox:objectbox-gradle-plugin:$objectboxVersion")
}
}
  1. 2.
    Open the build.gradle file for your app or module and, after other plugins, apply the io.objectbox plugin:
// Using plugins syntax:
plugins {
id("java-library") // or org.jetbrains.kotlin.jvm for Kotlin projects.
id("io.objectbox") // Apply last.
}
// Or using the old apply syntax:
apply plugin: "java-library" // or org.jetbrains.kotlin.jvm for Kotlin projects.
apply plugin: "io.objectbox" // Apply last.
Using your IDE of choice with a Gradle project might require additional configuration. E.g.
  • For IntelliJ IDEA see the help page for Gradle. Also make sure to use version 2019.1 or newer as it has improved Gradle support, like delegating build actions to Gradle.
  • For Eclipse see the Buildship project and Getting Started article.

Native Libraries

ObjectBox is an object database running mostly in native code written in C/C++ for optimal performance. Thus, ObjectBox will load a native library: a “.dll” on Windows, a “.so” on Linux, and a “.dylib” on macOS. By default, the ObjectBox Gradle plugin adds a dependency to the native library matching your system. This means that your app is already set up to run on your system.
On Windows you might have to install the latest Microsoft Visual C++ Redistributable package (X64) to use the ObjectBox DLL.
ObjectBox binaries are built for 64-bit systems for best performance. Talk to us if you require 32-bit support.
To add native libraries for all platforms that your app supports and additional configuration options (configure the model file path, the MyObjectBox package, enable debug mode) see:
For examples and how to write unit tests see:
Video Tutorial on Getting Started with ObjectBox for Flutter
You can watch these video tutorials as well 😀:
To add ObjectBox to your Flutter project:
  1. 1.
    Run these commands:
flutter pub add objectbox objectbox_flutter_libs:any
flutter pub add --dev build_runner objectbox_generator:any
For ObjectBox Sync add another Flutter libs package instead:
flutter pub add objectbox objectbox_sync_flutter_libs:any
flutter pub add --dev build_runner objectbox_generator:any
  1. 2.
    This should add lines like this to your pubspec.yaml:
dependencies:
objectbox: ^2.2.1
objectbox_flutter_libs: any
# For ObjectBox Sync this dependency should appear instead:
# objectbox_sync_flutter_libs: any
dev_dependencies:
build_runner: ^2.0.0
objectbox_generator: any
  1. 3.
    If you added the above lines manually, then install the packages with flutter pub get.
For ObjectBox Sync, also increase the Android minSdkVersion to at least 21:
# /android/app/build.gradle
android {
defaultConfig {
// ObjectBox Sync requires at least SDK 21 (Android 5.0)
minSdkVersion 21
}
}
For iOS using Flutter 3.0 or older: increase the deployment target in XCode to iOS 11 and, under Architectures, replace ${ARCHS_STANDARD} with arm64 (or $ARCHS_STANDARD_64_BIT).
For Linux Desktop apps: the Flutter snap ships with an outdated version of CMake. Install Flutter manually instead to use the version of CMake installed on your system.
For all macOS apps need to target macOS 10.13: in Podfile change the platform and in the Runner.xcodeproj/poject.pbxproj file update MACOSX_DEPLOYMENT_TARGET.
For sandboxed macOS apps specify an application group. Check all macos/Runner/*.entitlements files if they contain a <dict> section with correct group ID info. Change the string value to the DEVELOPMENT_TEAM you can find in your Xcode settings, plus an application-specific suffix, for example:
macos/Runner/*.entitlements
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>FGDTDLOBXDJ.demo</string>
</array>
...
</dict>
Next, in your app code, pass the same string when opening the Store, for example: openStore(macosApplicationGroup: 'FGDTDLOBXDJ.demo'). Note: Pick a short group identifier; there's an internal limit in macOS that requires the complete string to be 19 characters or fewer.
To run unit tests on your machine, download a native ObjectBox library for your machine by running this script in a bash shell (e.g. Git Bash on Windows):
bash <(curl -s https://raw.githubusercontent.com/objectbox/objectbox-dart/main/install.sh)
For ObjectBox Sync, append the --sync argument to above command.
  1. 1.
    Run these commands:
dart pub add objectbox
dart pub add --dev build_runner objectbox_generator:any
  1. 2.
    This should add lines like this to your pubspec.yaml:
dependencies:
objectbox: ^2.2.1
dev_dependencies:
build_runner: ^2.0.0
objectbox_generator: any
  1. 3.
    If you added the above lines manually, then install the packages with dart pub get
  2. 4.
    Install objectbox-c (use "Git bash" on Windows):
bash <(curl -s https://raw.githubusercontent.com/objectbox/objectbox-dart/main/install.sh)
For ObjectBox Sync instead pass the --sync argument to the script:
bash <(curl -s https://raw.githubusercontent.com/objectbox/objectbox-dart/main/install.sh) --sync
By default the library is downloaded into the lib subdirectory of the working directory. It's not necessary to install the library system-wide. This also allows to use different versions for different projects. For details see below.

Deploying Dart Native projects

Natively compiled Dart applications that use ObjectBox Dart require a reference to the objectbox-c library. Hence, the shared library file downloaded with install.sh needs to be shipped with the executable.
The install.sh script downloads the library by default to the lib subdirectory of the working directory. An executable using ObjectBox Dart looks for the library in this lib directory.
If it is not found there, it falls back to using system directories (using Dart's DynamicLibrary.open):
  • Windows: working directory and %WINDIR%\system32.
  • macOS: /usr/local/lib (and maybe others).
  • Linux: /lib and /usr/lib (again, possibly others).

Define Entity Classes

Define your model by adding an @Entity (internal name for database objects) annotation to at least one class and an @Id annotation to one of the class variables. Learn more about the ObjectBox model here.
A simple entity representing a user could look like this:
Java
Kotlin
Dart
User.java
@Entity
public class User {
@Id
public long id;
public String name;
}
models.kt
@Entity
data class User(
@Id
var id: Long = 0,
var name: String? = null
)
When using a data class, add default values for all parameters. This will ensure your data class will have a constructor that can be called by ObjectBox. (Technically this is only required if adding properties to the class body, like custom or transient properties or relations, but it's a good idea to do it always.)
Avoid naming properties like reserved Java keywords, like private and default. ObjectBox tooling works with the Java representation of your Kotlin code to be compatible with both Java and Kotlin. It will ignore such properties.
models.dart
@Entity()
class User {
@Id()
int id = 0;
String? name;
@Property(type: PropertyType.date) // Store as int in milliseconds
DateTime? date;
@Transient() // Ignore this property, not stored in the database.
int? computedProperty;
}
You can have multiple entities in the same file (here models.dart), or you can have them spread across multiple files in your package's lib directory.
Important:
  • Entities must have one ID property of type long (or Long in Kotlin, int in Dart). If you need to use other types, like a String ID, see the @Id annotation docs. Also, it must have non-private visibility (or non-private getter and setter methods).
  • Entities must also have a no-args constructor, or for better performance, a constructor with all properties as arguments. In the above example, a default, no-args constructor is generated by the compiler.
Support for many property types is already built-in, but almost any type can be stored with a converter.
For a deeper explanation and a look at all other available annotations (e.g. for relations and indexes) check the Entity Annotations page.

Generate ObjectBox code

Next, we generate some binding code based on the model defined in the previous step.
Java/Kotlin
Flutter
Dart Native
Build your project to generate the classes required to use ObjectBox, for example using Build > Make Project in Android Studio.
Note: If you make significant changes to your entities, e.g. by moving them or modifying annotations, make sure to rebuild the project so generated ObjectBox code is updated.
Run flutter pub run build_runner build to generate the binding code required to use ObjectBox.
ObjectBox generator will look for all @Entity annotations in your lib folder and create a single database definition lib/objectbox-model.json and supporting code in lib/objectbox.g.dart
Add objectbox.g.dart to your version control ignore list (e.g. .gitignore), otherwise build_runner will complain about it being changed each time you pull a change.
Run dart run build_runner build to generate the binding code required to use ObjectBox.
ObjectBox generator will look for all @Entity annotations in your lib folder and create
  • a single database definition lib/objectbox-model.json and
  • supporting code in lib/objectbox.g.dart.
To customize the directory where generated files are written see Advanced Setup.
You might want to add objectbox.g.dart to your version control ignore list (e.g. .gitignore), otherwise build_runner will complain about it being changed each time you pull a change.
The generator will process lib and test folders separately and generate files for each one, if it finds @Entity classes there. This allows to create a separate test database that does not share any of the entity classes of the main database.

Keep the Model file

Among other files ObjectBox generates a JSON model file, by default to
  • app/objectbox-models/default.json for Android projects, or
  • lib/objectbox-model.json for Dart/Flutter projects.
In Android Studio you might have to switch the Project view from Android to Project to see the default.json model file.
This JSON file changes when you change your entity classes (or sometimes with a new version of ObjectBox).
Keep this JSON file, commit the changes to version control!
This file keeps track of unique IDs assigned to your entities and properties. This ensures that an older version of your database can be smoothly upgraded if your entities or properties change.
The model file also enables you to keep data when renaming entities or properties or to resolve conflicts when two of your developers make changes at the same time.

Create a Store

BoxStore (Java) or Store (Dart) is the entry point for using ObjectBox. It is the direct interface to the database and manages Boxes. Typically, you want to only have a single Store (single database) and keep it open while your app is running, not closing it explicitly.
Java (Android)
Kotlin (Android)
JVM
Flutter
Dart Native
Create it using the builder returned by the generated MyObjectBox class, for example in a small helper class like this:
public class ObjectBox {
private static BoxStore boxStore;
public static void init(Context context) {
boxStore = MyObjectBox.builder()
.androidContext(context.getApplicationContext())
.build();
}
public static BoxStore get() { return boxStore; }
}
If you encounter UnsatisfiedLinkError or LinkageError on the build call, see App Bundle, split APKs and Multidex for solutions.
The best time to initialize ObjectBox is when your app starts. We suggest to do it in the onCreate method of your Application class:
public class ExampleApp extends Application {
@Override
public void onCreate() {
super.onCreate();
ObjectBox.init(this);
}
}
Create it using the builder returned by the generated MyObjectBox class, for example in a small helper class like this:
object ObjectBox {
lateinit var store: BoxStore
private set
fun init(context: Context) {
store = MyObjectBox.builder()
.androidContext(context.applicationContext)
.build()
}
}
If you encounter UnsatisfiedLinkError or LinkageError on the build call, see App Bundle, split APKs and Multidex for solutions.
The best time to initialize ObjectBox is when your app starts. We suggest to do it in the onCreate method of your Application class:
class ExampleApp : Application() {
override fun onCreate() {
super.onCreate()
ObjectBox.init(this)
}
}
public class ObjectBox {
private static BoxStore boxStore;
public static void init(Context context) {
boxStore = MyObjectBox.builder()
.name("objectbox-notes-db")
.build();
}
public static BoxStore get() { return boxStore; }
}
The best time to initialize ObjectBox is when your app starts. For a command line app this is typically inside the main method.
Create it using the generated openStore() method, for example in a small helper class like this:
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'objectbox.g.dart'; // created by `flutter pub run build_runner build`
class ObjectBox {
/// The Store of this app.
late final Store store;
ObjectBox._create(this.store) {
// Add any additional setup code, e.g. build queries.
}
/// Create an instance of ObjectBox to use throughout the app.
static Future<ObjectBox> create() async {
final docsDir = await getApplicationDocumentsDirectory();
// Future<Store> openStore() {...} is defined in the generated objectbox.g.dart
final store = await openStore(directory: p.join(docsDir.path, "obx-example"));
return ObjectBox._create(store);
}
}
The best time to initialize ObjectBox is when your app starts. We suggest to do it in your app's main() function:
/// Provides access to the ObjectBox Store throughout the app.
late ObjectBox objectbox;
Future<void> main() async {
// This is required so ObjectBox can get the application directory
// to store the database in.
WidgetsFlutterBinding.ensureInitialized();
objectbox = await ObjectBox.create();
runApp(MyApp());
}
On mobile devices/sandboxed apps, data should be stored in the app's documents directory. See Flutter: read & write files for more info. This is exactly what openStore()does, if the directory argument is not specified.
On desktop systems it is recommended to specify a directory to create a custom sub-directory to avoid conflicts with other apps.
If your code passes a directory that the application can't write to, you get an error that looks somewhat like this: failed to create store: 10199 Dir does not exist: objectbox (30).
Create it using the generated getObjectBoxModel() method, for example like this:
import 'objectbox.g.dart'; // created by `dart pub run build_runner build`
void main() {
// Store openStore() {...} is defined in the generated objectbox.g.dart
final store = openStore();
// your app code ...
store.close(); // don't forget to close the store
}
The above minimal example omits the argument to (directory: ), using the default - ./objectbox - in the current working directory.
For more MyObjectBox builder configuration options see the BoxStoreBuilder documentation.

Basic Box operations

The Box class is likely the class you interact with most. A Box instance gives you access to objects of a particular type. For example, if you have User and Order entities, you need a Box object to interact with each:
Java
Kotlin
Dart
Box<User> userBox = ObjectBox.get().boxFor(User.class);
Box<Order> orderBox = ObjectBox.get().boxFor(Order.class);
val userBox = ObjectBox.store.boxFor(User::class.java)
val orderBox = ObjectBox.store.boxFor(Order::class.java)
final userBox = store.box<User>();
final orderBox = store.box<Order>();
These are some of the operations offered by the Box class:
put inserts a new object or updates an existing one (with the same ID). When inserting, an ID will be assigned to the just inserted object (this will be explained below) and returned. put also supports putting multiple objects, which is more efficient.
Java
Kotlin
Dart
User user = new User("Tina");
userBox.put(user);
List<User> users = getNewUsers();
userBox.put(users);
val user = User("Tina")
userBox.put(user)
val users: List<User> = getNewUsers()
userBox.put(users)
final user = User(name: 'Tina');
userBox.put(user);
final users = getNewUsers();
userBox.putMany(users);
get and getAll: Given an object’s ID, get reads it from its box. To get all objects in the box use getAll .
Java
Kotlin
Dart
User user = userBox.get(userId);
List<User> users = userBox.getAll();
val user = userBox[userId]
val users = userBox.all
final user = userBox.get(userId);
final users = userBox.getMany(userIds);
final users = userBox.getAll();
query: Starts building a query to return objects from the box that match certain conditions. See queries for details.
Java
Kotlin
Dart
Query<User> query = userBox
.query(User_.name.equal("Tom"))
.order(User_.name)
.build();
List<User> results = query.find();
query.close();
val query = userBox
.query(User_.name.equal("Tom"))
.order(User_.name)
.build()
val results = query.find()
query.close()
final query =
(userBox.query(User_.name.equals('Tom'))..order(User_.name)).build();
final results = query.find();
query.close();
remove and removeAll: Remove a previously put object from its box (deletes it). remove also supports removing multiple objects, which is more efficient. removeAll removes (deletes) all objects in a box.
Java
Kotlin
Dart
boolean isRemoved = userBox.remove(userId);
userBox.remove(users);
// alternatively:
userBox.removeByIds(userIds);
userBox.removeAll();
val isRemoved = userBox.remove(userId)
userBox.remove(users)
// alternatively:
userBox.removeByIds(userIds)
userBox.removeAll()
final isRemoved = userBox.remove(userId);
userBox.removeMany(userIds);
userBox.removeAll();
count: Returns the number of objects stored in this box.
Java
Kotlin
Dart
long userCount = userBox.count();
val userCount = userBox.count()
final userCount = userBox.count();
For a complete list of methods available in the Box class, check the API reference documentation for Java or Dart.

Asynchronous operations

ObjectBox has built-in support to run (typically multiple or larger) database operations asynchronously.
Java
Dart
runInTxAsync and callInTxAsync: runs the given Runnable/Callable in a transaction on a background thread (the internal ObjectBox thread pool) and calls the given callback once done. In case of callInTxAsync the callback also receives the returned result.
boxStore.callInTxAsync(() -> {
Box<User> box = boxStore.boxFor(User.class);
String name = box.get(userId).name;
box.remove(userId);
return text;
}, (result, error) -> {
if (error != null) {
System.out.println("Failed to remove user with id " + userId);
} else {
System.out.println("Removed user with name: " + result);
}
});
awaitCallInTx (Kotlin Coroutines only): wraps callInTxAsync in a coroutine that suspends until the transaction has completed. Likewise, on success the return value of the given callable is returned, on failure an exception is thrown.
try {
val name = boxStore.awaitCallInTx {
val box = boxStore.boxFor(User::class.java)
val name = box.get(userId).name
box.remove(userId)
name
}
println("Removed user with name $name")
} catch (e: Exception) {
println("Failed to remove user with id $userId")
}
Most Box methods do have async versions which run the operation in a worker isolate.
For example putAsync: asynchronously inserts a new object or updates an existing one (with the same ID). The returned future completes when the object is successfully written to the database.
final user = User(name: 'Tina');
Future<int> idFuture = userBox.putAsync(user);
...
final id = await idFuture;
userBox.get(id); // after the future completed, the object is inserted
To run multiple operations, it is more efficient to wrap the synchronous calls in an asynchronous transaction with runInTransactionAsync (API reference): run a callback with multiple database operations within a write or read transaction in the background without blocking the user interface. Can return results.
// The callback must be a function that can be sent to an isolate:
// either a top-level function, static method or a closure that only
// captures objects that can be sent to an isolate.
String? readNameAndRemove(Store store, int objectId) {
var box = store.box<User>();
final nameOrNull = box.get(objectId)?.name;
box.remove(objectId);
return nameOrNull;
}
final nameOrNull =
await store.runInTransactionAsync(TxMode.write, readNameAndRemove, objectId);
There is also runAsync (API reference): like runInTransactionAsync but does not start a transaction, leaving that to your callback code. This allows to supply a callback that is an async function.
If it is necessary to call put many times in a row, take a look at putQueued: Schedules the given object to be put later on, by an asynchronous queue, returns the id immediately even though the object may not have been written yet. You can use Store's awaitQueueCompletion() or awaitQueueSubmitted() to wait for the async queue to finish.
for (int i = 0; i < 100; i++) {
userBox.putQueued(User(name: 'User $i'));
}
// Optional: wait until submitted items are processed.
store.awaitQueueSubmitted();
expect(userBox.count(), equals(100));

Object IDs

By default IDs for new objects are assigned by ObjectBox. When a new object is put, it will be assigned the next highest available ID:
Java
Kotlin
Dart
User user = new User();
// user.id == 0
box.put(user);
// user.id != 0
long id = user.id;
val user = User()
// user.id == 0
box.put(user)
// user.id != 0
val id = user.id
final user = User();
// user.id == 0
box.put(user);
// user.id != 0
final id = user.id;
For example, if there is an object with ID 1 and another with ID 100 in a box, the next new object that is put will be assigned ID 101.
If you try to assign a new ID yourself and put the object, ObjectBox will throw an error.
If you need to assign IDs by yourself, have a look at how to switch to self-assigned IDs and what side effects apply.

Reserved Object IDs

Object IDs can not be:
  • 0 (zero) or null (if using java.lang.Long) As said above, when putting an object with ID zero it will be assigned an unused ID (not zero).
  • 0xFFFFFFFFFFFFFFFF (-1 in Java) Reserved for internal use.
For a detailed explanation see the page on Object IDs.

Transactions

While ObjectBox offers powerful transactions, it is sufficient for many apps to consider just some basics guidelines about transactions:
  • A put runs an implicit transaction.
  • Prefer put bulk overloads for lists (like put(entities)) when possible.
  • For a high number of DB interactions in loops, consider explicit transactions, such as using runInTx().
For more details check the separate transaction documentation.

Have an app with greenDAO? DaoCompat is for you!

DaoCompat is a compatibility layer that gives you a greenDAO like API for ObjectBox. It makes switching from greenDAO to ObjectBox simple. Have a look at the documentation and the example. Contact us if you have any questions!

Next steps