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
ObjectBox tools and dependencies are available on the Maven Central repository.
To add ObjectBox to your Android project, follow these steps:
Open the Gradle build file of your root project (not the ones for your app or module subprojects) and add a global variable for the version and the ObjectBox Gradle plugin:
buildscript {
ext.objectboxVersion = "5.0.1" // For Groovy build scripts
// val objectboxVersion by extra("5.0.1") // For KTS build scripts
repositories {
mavenCentral()
}
dependencies {
// Android Gradle Plugin 8.0 or later supported
classpath("com.android.tools.build:gradle:8.0.2")
classpath("io.objectbox:objectbox-gradle-plugin:$objectboxVersion")
}
}Open the Gradle build file for your app or module subproject and, after the
com.android.applicationplugin, apply theio.objectboxplugin:
// 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 lastThen do "Sync Project with Gradle Files" in Android Studio so the Gradle plugin automatically adds the required ObjectBox libraries and code generation tasks.
Your project can now use ObjectBox, continue by defining entity classes.
The ObjectBox Java SDK and runtime libraries support applications:
running on the JVM on Linux (x86_64, arm64, armv7), Windows (x86_64) and macOS 10.15 or newer (x86_64, Apple M1)
written in Java or Kotlin
targeting at least Java 8
built with Gradle or Maven
ObjectBox tools and dependencies are available on the Maven Central repository.
Maven projects
To set up a Maven project, see the README of the Java Maven example project.
Gradle projects
Open the Gradle build script of your root project and
add a global variable to store the common version of ObjectBox dependencies and
add the ObjectBox Gradle plugin:
buildscript {
ext.objectboxVersion = "5.0.1" // For Groovy build scripts
// val objectboxVersion by extra("5.0.1") // For KTS build scripts
repositories {
mavenCentral()
}
dependencies {
classpath("io.objectbox:objectbox-gradle-plugin:$objectboxVersion")
}
}Open the Gradle build file for your application subproject and, after other plugins, apply the
io.objectboxplugin:
// 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.Optionally, add a runtime library for each platform that your application should run on and instead apply the Gradle plugin after the dependencies block:
dependencies {
// ObjectBox platform-specific runtime libraries
// Add or remove them as needed to match what your application supports
// Linux (x64)
implementation("io.objectbox:objectbox-linux:$objectboxVersion")
// macOS (Intel and Apple Silicon)
implementation("io.objectbox:objectbox-macos:$objectboxVersion")
// Windows (x64)
implementation("io.objectbox:objectbox-windows:$objectboxVersion")
// Additional ObjectBox runtime libraries
// Linux (32-bit ARM)
implementation("io.objectbox:objectbox-linux-arm64:$objectboxVersion")
// Linux (64-bit ARM)
implementation("io.objectbox:objectbox-linux-armv7:$objectboxVersion")
}
// When manually adding ObjectBox dependencies, the plugin must be
// applied after the dependencies block so it can detect them.
// Using Groovy build scripts
apply plugin: "io.objectbox"
// Using KTS build scripts
apply(plugin = "io.objectbox")Your project can now use ObjectBox, continue by defining entity classes.
To add ObjectBox to your Flutter project:
Run these commands:
flutter pub add objectbox objectbox_flutter_libs:any
flutter pub add --dev build_runner objectbox_generator:anyOr to use ObjectBox Sync (requires access to the Sync feature) instead run:
flutter pub add objectbox objectbox_sync_flutter_libs:any
flutter pub add --dev build_runner objectbox_generator:anyThis should add lines like this to your
pubspec.yaml:
dependencies:
objectbox: ^5.0.1
objectbox_flutter_libs: any
# If you run the command for ObjectBox Sync it should add instead:
# objectbox_sync_flutter_libs: any
dev_dependencies:
build_runner: ^2.4.11
objectbox_generator: anyIf you added the above lines manually, then install the packages with
flutter pub get.
Run these commands:
dart pub add objectbox
dart pub add --dev build_runner objectbox_generator:anyThis should add lines like this to your
pubspec.yaml:
dependencies:
objectbox: ^5.0.1
dev_dependencies:
build_runner: ^2.4.11
objectbox_generator: anyIf you added the above lines manually, then install the packages with
dart pub getInstall the ObjectBox C library for your system (on Windows you can use "Git Bash"):
bash <(curl -s https://raw.githubusercontent.com/objectbox/objectbox-dart/main/install.sh)Or to use ObjectBox Sync (requires access to the Sync feature) instead run:
bash <(curl -s https://raw.githubusercontent.com/objectbox/objectbox-dart/main/install.sh) --syncDeploying 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:
/liband/usr/lib(again, possibly others).
ObjectBox for Python is available via PyPI: Stable Version (4.0.0):
pip install --upgrade objectboxDefine Entity Classes
Define your data model by creating a class with at least an ID property, a so called entity.
A simple entity representing a user with an ID and a name property could look like this:
@Entity
public class User {
@Id
public long id;
public String name;
}@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.
@Entity()
class User {
@Id()
int id = 0;
String? name;
}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.
from objectbox import Entity, Id, String
@Entity()
class User:
id = Id
name = String
Important:
Entities must have exactly one 64-bit integer ID property (a Java
long, KotlinLong, Dartint). If you need another type for the ID, like a string, see the @Id annotation docs for some tips. Also, the ID property must have non-private visibility (or non-private getter and setter methods).Entities must also have a no-argument constructor, or for better performance, a constructor with all properties as arguments. In the above examples, a default, no-argument 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 more details about entities, like how to create an index or a relation, check the Entity Annotations page.
You can also learn more about the ObjectBox model.
Generate ObjectBox code
Next, we generate some binding code based on the model defined in the previous step.
Build your project to generate the MyObjectBox class and other classes required to use ObjectBox, for example using Build > Make Project in Android Studio.
To change the package of the MyObjectBox class, see the annotation processor options on the Advanced Setup page.
To generate the binding code required to use ObjectBox run
dart run build_runner build
ObjectBox generator will look for all @Entity annotations in your lib folder and create
a single database definition
lib/objectbox-model.jsonandsupporting code in
lib/objectbox.g.dart.
To customize the directory where generated files are written see Advanced Setup.
You typically commit the generated code file objectbox.g.dart to your version control system (e.g. git) to avoid having to re-run the generator unless there are changes.
Among other files ObjectBox generates a JSON model file, by default to
app/objectbox-models/default.jsonfor Android projects,lib/objectbox-model.jsonfor Dart/Flutter projects, or<user-module-dir>/objectbox-model.jsonfor Python projects
To change the model file path, see Advanced Setup.
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.
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 store;
public static void init(Context context) {
store = MyObjectBox.builder()
.androidContext(context)
.build();
}
public static BoxStore get() { return store; }
}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)
.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 store;
public static void init(Context context) {
store = MyObjectBox.builder()
.name("objectbox-notes-db")
.build();
}
public static BoxStore get() { return store; }
}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());
}Create it using the generated openStore() 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.
from objectbox import Store
store = Store()It is possible to specify various options when building a store. Notably for testing or caching, to use an in-memory database that does not create any files:
BoxStore inMemoryStore = MyObjectBox.builder()
.androidContext(context)
.inMemory("test-db")
.build(); final inMemoryStore =
Store(getObjectBoxModel(), directory: "memory:test-db");store = Store(directory="memory:testdata")For more store configuration options: for Java see the BoxStoreBuilder and for Dart the Store documentation. (Python APIs will be published soon)
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:
Box<User> userBox = store.boxFor(User.class);
Box<Order> orderBox = store.boxFor(Order.class);val userBox = store.boxFor(User::class)
val orderBox = store.boxFor(Order::class)final userBox = store.box<User>();
final orderBox = store.box<Order>();user_box = store.box(User)
order_box = 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.
User user = new User("Tina");
userBox.put(user);
List<User> users = getNewUsers();
userBox.put(users);val user = User(name = "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);user = User(name="Tina")
user_box.put(user)
users = get_new_users()
user_box.put(*users)get and getAll: Given an object’s ID, get reads it from its box. To get all objects in the box use getAll .
User user = userBox.get(userId);
List<User> users = userBox.getAll();val user = userBox[userId]
val users = userBox.allfinal user = userBox.get(userId);
final users = userBox.getMany(userIds);
final users = userBox.getAll();user = user_box.get(user_id)
users = user_box.get_all()query: Starts building a query to return objects from the box that match certain conditions. See queries for details.
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();query = user_box \
.query(User.name.equals('Tom')) \
.build()
results = query.find()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.
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();is_removed = user_box.remove(user_id)
user_box.remove_all()count: Returns the number of objects stored in this box.
long userCount = userBox.count();val userCount = userBox.count()final userCount = userBox.count();user_box.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.
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.
store.callInTxAsync(() -> {
Box<User> box = store.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 = store.awaitCallInTx {
val box = store.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 insertedTo 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:
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.idfinal user = User();
// user.id == 0
box.put(user);
// user.id != 0
final id = user.id;user = User()
box.put(user)
id: int = user.idFor 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.
Reserved Object IDs
Object IDs can not be:
0(zero) ornull(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
putruns an implicit transaction.Prefer
putbulk overloads for lists (likeput(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
Check out the ObjectBox example projects on GitHub.
Learn how to write unit tests.
To enable debug mode and for advanced use cases, see the Advanced Setup page.
Last updated
Was this helpful?