# ObjectBox Docs

Explore the on-device database and SQLite alternative for object and vector data. This is the official ObjectBox documentation for Java/Kotlin, Dart/Flutter, and Python.

{% content-ref url="/pages/-LEUEB3nMDmS4vcFKZju" %}
[Getting Started](/getting-started)
{% endcontent-ref %}

{% hint style="info" %}
**C++ and Swift:** Check the docs in <https://cpp.objectbox.io/> and <https://swift.objectbox.io/>.
{% endhint %}

{% hint style="info" %}
**We're hiring!** We believe resource-efficient coding is still cool 😎 and are [looking for a C / C++ developer](https://objectbox.io/jobs/objectbox-senior-c-plusplus-developer/) who shares our sentiment.
{% endhint %}

🚀 Our mission is to revolutionize mobile app development by making it fast and efficient with our intuitive and user-friendly native-language APIs. With ObjectBox, you can easily store and synchronize objects in various programming languages such as Python, Java, C, Go, Kotlin, Dart, C++, and Swift. It works seamlessly across different platforms like Android, iOS, Windows, and Linux, and you can even use it with Docker.

💚 [Please share your feedback](https://forms.gle/bdktGBUmL4m48ruj7)! Community feedback is very important for us as we strive to make ObjectBox better for our users. Our feedback form is anonymous and only takes 2 minutes to fill in. Every response is highly appreciated. Thank you in advance! 🙏

[👍](https://emojipedia.org/thumbs-up/) To rate this documentation, use the "Was this page helpful?" smiley 🙃 at the end of each page.

🚩 Feel free to open an issue on GitHub (for [Java & Kotlin](https://github.com/objectbox/objectbox-java/issues/new/choose) or [Dart/Flutter](https://github.com/objectbox/objectbox-dart/issues/new/choose)) or send us your comments to contact\[at]objectbox.io - Thank you! - and if you like what you see, we also appreciate a shout out :) or GitHub star ⭐


# 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

{% tabs %}
{% tab title="Java/Kotlin (Android)" %}
{% hint style="info" %}
Prefer to look at example code? Check out [our Java SDK examples repository](https://github.com/objectbox/objectbox-examples).
{% endhint %}

These instructions show how to add ObjectBox to an Android project built with at least Android Gradle Plugin 9.0 and using a TOML version catalog.

For other setup options and more details, see [the instructions in the objectbox-java README](https://github.com/objectbox/objectbox-java?tab=readme-ov-file#getting-started).

1. In the TOML version catalog file define a version variable using the [latest ObjectBox version](https://github.com/objectbox/objectbox-java/releases/latest) and an alias for the ObjectBox and kapt plugin:

{% code title="gradle/libs.versions.toml" fullWidth="false" %}

```toml
[versions]
agp = "AGP_VERSION"
# Define a variable for the version of the ObjectBox plugin
objectbox = "OBJECTBOX_VERSION"

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
# Add an alias for the kapt plugin
kotlin-kapt = { id = "com.android.legacy-kapt", version.ref = "agp" }
# Add an alias for the ObjectBox plugin
objectbox = { id = "io.objectbox", version.ref = "objectbox" }
```

{% endcode %}

2. In the root Gradle build script, add the ObjectBox and kapt plugin:

{% code title="build.gradle.kts" %}

```kts
plugins {
    alias(libs.plugins.android.application) apply false
    // Add the kapt plugin
    alias(libs.plugins.kotlin.kapt) apply false    
    // Add the ObjectBox plugin
    alias(libs.plugins.objectbox) apply false
}
```

{% endcode %}

3. In the Gradle settings file, add Maven Central to the plugin and dependency repositories:

{% code title="settings.gradle.kts" %}

```kts
pluginManagement {
    repositories {
        // Add Maven Central to the plugin repositories
        mavenCentral()
    }
}

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        // Add Maven Central to the dependency repositories
        mavenCentral()
    }
}
```

{% endcode %}

4. In the Gradle build script of your app project, apply the kapt and then the ObjectBox plugin after the Android application plugin:

{% code title="app/build.gradle.kts" %}

```kts
plugins {
    alias(libs.plugins.android.application)  
    // Apply the kapt plugin
    alias(libs.plugins.kotlin.kapt)
    // Apply the ObjectBox plugin
    alias(libs.plugins.objectbox)    
}
```

{% endcode %}

5. Your project can now use ObjectBox, continue below with defining entity classes.
   {% endtab %}

{% tab title="Java/Kotlin (JVM)" %}
{% hint style="info" %}
Prefer to look at example code? Check out [our Java SDK examples repository](https://github.com/objectbox/objectbox-examples).
{% endhint %}

These instructions show how to add ObjectBox to a Java Virtual Machine (JVM) project built with at least Gradle 7.0 and using a TOML version catalog.

For other setup options and more details, see [the instructions in the objectbox-java README](https://github.com/objectbox/objectbox-java?tab=readme-ov-file#getting-started).

{% hint style="info" %}
Using your IDE of choice with a Gradle project might require additional configuration. E.g.

* For IntelliJ IDEA see the [help page for Gradle](https://www.jetbrains.com/help/idea/gradle.html).
* For Eclipse see the [Buildship ](https://projects.eclipse.org/projects/tools.buildship)project and [Getting Started](https://www.vogella.com/tutorials/EclipseGradle/article.html) article.
  {% endhint %}

1. In the TOML version catalog file define a version variable using the [latest ObjectBox version](https://github.com/objectbox/objectbox-java/releases/latest) and an alias for the ObjectBox plugin:

{% code title="gradle/libs.versions.toml" fullWidth="false" %}

```toml
[versions]
# If using Kotlin
kotlin = "KOTLIN_VERSION"
# Define a variable for the version of the ObjectBox plugin
objectbox = "OBJECTBOX_VERSION"

[plugins]
# If using Kotlin 
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" }
# Add an alias for the ObjectBox plugin
objectbox = { id = "io.objectbox", version.ref = "objectbox" }
```

{% endcode %}

2. In the root Gradle build script, add the ObjectBox plugin:

{% code title="build.gradle.kts" %}

```kts
plugins {   
    // If using Kotlin
    alias(libs.plugins.kotlin.jvm) apply false
    alias(libs.plugins.kotlin.kapt) apply false
    // Add the ObjectBox plugin
    alias(libs.plugins.objectbox) apply false
}
```

{% endcode %}

3. In the Gradle settings file, add Maven Central to the plugin and dependency repositories:

{% code title="settings.gradle.kts" %}

```kts
pluginManagement {
    repositories {
        // Add Maven Central to the plugin repositories
        mavenCentral()
    }
}

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        // Add Maven Central to the dependency repositories
        mavenCentral()
    }
}
```

{% endcode %}

4. In the Gradle build script of your app project, apply the ObjectBox plugin after the application or java-library plugin:

{% code title="app/build.gradle.kts" %}

```kts
plugins {
    id("application") // or id("java-library")
    // If using Kotlin
    alias(libs.plugins.kotlin.jvm)
    alias(libs.plugins.kotlin.kapt)
    // Apply the ObjectBox plugin
    alias(libs.plugins.objectbox)
}
```

{% endcode %}

5. **Optionally**, add a database library dependency for each platform that your application should run on:

{% code title="app/build.gradle.kts" %}

```groovy
dependencies {
    // ObjectBox platform-specific database libraries
    // Add or remove them as needed to match what your application supports.
    // There are Sync variants available named like "objectbox-sync-<platform>".
    // 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")
}
```

{% endcode %}

{% hint style="info" %}
The ObjectBox database runs mostly in native code written in C/C++ for optimal performance. Thus, ObjectBox will load a database library: a “.dll” on Windows, a “.so” on Linux, and a “.dylib” on macOS.

By default, the Gradle plugin adds a database library (only) for your current operating system. It also adds the Java SDK (objectbox-java) and if needed the ObjectBox Kotlin extension functions (objectbox-kotlin).
{% endhint %}

{% hint style="info" %}
ObjectBox only supports 64-bit systems for best performance going forward. Talk to us if you require 32-bit support.
{% endhint %}

6. Your project can now use ObjectBox, continue below with defining entity classes.
   {% endtab %}

{% tab title="Flutter" %}
{% hint style="info" %}
Prefer to look at example code? Check out our [Flutter and Dart SDK examples directory](https://github.com/objectbox/objectbox-dart/tree/main/objectbox/example).

Some fine people have created videos explaining core concepts, you can watch these as well:

* [Shop order app](https://www.youtube.com/watch?v=AxYbdriXKI8)
* [Event Management app](https://youtu.be/6YPSQPS_bhU)
* [Restaurant: chef and order apps](https://youtu.be/r9Lc2r22KBk)
* [Task-list app (in Spanish)](https://youtu.be/osUq6B92-BY)
  {% endhint %}

To add ObjectBox to your Flutter project:

1. Run these commands to add compatible versions of the [objectbox package](https://pub.dev/packages/objectbox) and its dependencies to your `pubspec.yaml`:

```
flutter pub add objectbox objectbox_flutter_libs:any
flutter pub add --dev build_runner objectbox_generator:any
```

Or to use [ObjectBox Sync](https://objectbox.io/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:any
```

{% hint style="info" %}
**To run unit tests on your machine**, download the latest ObjectBox database library for your system by running this script in your package directory in a bash shell (e.g. Git Bash on Windows):

`bash <(curl -s https://raw.githubusercontent.com/objectbox/objectbox-dart/main/install.sh)`

To get the variant for **ObjectBox Sync**, append the `--sync` argument to the above command.
{% endhint %}

{% hint style="info" %}
**For all iOS apps** target iOS 15.0: in `ios/Podfile` change the platform and in the `ios/Runner.xcodeproj/poject.pbxproj` file update `IPHONEOS_DEPLOYMENT_TARGET` (or open the Runner workspace in Xcode and edit the build setting). In `ios/Flutter/AppframeworkInfo.plist` update `MinimumOSVersion` to `15.0`.

**For all macOS apps** target macOS 11.0: in `macos/Podfile` change the platform and in the `macos/Runner.xcodeproj/poject.pbxproj` file update `MACOSX_DEPLOYMENT_TARGET` (or open the Runner workspace in Xcode and edit the build setting).

**For macOS apps using Sync,** open `macos/Runner.xcodeproj` in Xcode and for the Runner target under Signing & Capabilities in the App Sandbox sections, enable incoming and outgoing network access.
{% endhint %}

2. Your project can now use ObjectBox, continue below with defining entity classes.
   {% endtab %}

{% tab title="Dart Native" %}
{% hint style="info" %}
Prefer to look at example code? Check out our [Flutter and Dart SDK examples directory](https://github.com/objectbox/objectbox-dart/tree/main/objectbox/example).
{% endhint %}

To add ObjectBox to your Dart Native project:

1. Run these commands to add compatible versions of the [objectbox package](https://pub.dev/packages/objectbox) and its dependencies to your `pubspec.yaml`:

```
dart pub add objectbox
dart pub add --dev build_runner objectbox_generator:any
```

2. In your package directory, download the [ObjectBox C library](https://github.com/objectbox/objectbox-c) for your system (on Windows you can use "Git Bash"):

{% code overflow="wrap" %}

```bash
bash <(curl -s https://raw.githubusercontent.com/objectbox/objectbox-dart/main/install.sh)
```

{% endcode %}

Or to use [ObjectBox Sync](https://objectbox.io/sync/) (requires access to the Sync feature) instead run:

{% code overflow="wrap" %}

```bash
bash <(curl -s https://raw.githubusercontent.com/objectbox/objectbox-dart/main/install.sh) --sync
```

{% endcode %}

{% hint style="info" %}
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. See the notes on deploying below.
{% endhint %}

3. Your project can now use ObjectBox, continue by defining entity classes.

**Deploying Dart Native projects**

Natively compiled Dart applications that use ObjectBox Dart require a reference to the [ObjectBox C](https://github.com/objectbox/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).
  {% endtab %}

{% tab title="Python" %}
{% hint style="info" %}
Prefer to look at example code? Check out our [examples directory](https://github.com/objectbox/objectbox-python/tree/main/example).
{% endhint %}

ObjectBox for Python is available via PyPI:\
\
Stable Version (4.0.0):

```sh
pip install --upgrade objectbox
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
If you encounter any problems in this or later steps, also check the [FAQ](/faq) and [Troubleshooting](/troubleshooting) pages.
{% endhint %}

## Define 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:

{% tabs %}
{% tab title="Java" %}
{% code title="User.java" %}

```java
@Entity
public class User {
    @Id 
    public long id;
    public String name;
}
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code title="models.kt" %}

```kotlin
@Entity
data class User(
        @Id 
        var id: Long = 0,
        var name: String? = null
)
```

{% endcode %}

{% hint style="warning" %}
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.)
{% endhint %}

{% hint style="warning" %}
**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.
{% endhint %}
{% endtab %}

{% tab title="Dart" %}
{% code title="models.dart" %}

```dart
@Entity()
class User {
  @Id()
  int id = 0;
  
  String? name;
}
```

{% endcode %}

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.
{% endtab %}

{% tab title="Python" %}
{% code title="model.py" %}

```python
from objectbox import Entity, Id, String

@Entity()
class User:
  id = Id
  name = String
  
```

{% endcode %}
{% endtab %}
{% endtabs %}

**Important:**

* **Entities must have exactly one 64-bit integer ID property** (a Java `long`, Kotlin `Long`, Dart `int`). If you need another type for the ID, like a string, [see the @Id annotation docs](/entity-annotations#object-ids-id) 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](/property-types) is already built-in, but almost any type can be stored [with a converter](/advanced/custom-types#convert-annotation-and-property-converter).

For more details about entities, like how to create an index or a relation, check the [Entity Annotations](/entity-annotations) page.

You can also [learn more about the ObjectBox model](/advanced/meta-model-ids-and-uids).

{% hint style="info" %}
ObjectBox also supports changing your model at a later point. You can **add and remove properties in entities** and the database model is updated **automatically** (after re-generating some code, see section below). There is no need to write migration code.

**To rename entities or properties, change the type of a property** and more details in general see [Data Model Updates](/advanced/data-model-updates).
{% endhint %}

## Generate ObjectBox code

Next, we generate some binding code based on the model defined in the previous step.

{% tabs %}
{% tab title="Java/Kotlin" %}
**Build your project** to generate the `MyObjectBox` class and other classes required to use ObjectBox, for example using **Build > Make Project** in Android Studio.

{% hint style="info" %}
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.
{% endhint %}

To change the package of the `MyObjectBox` class, see the annotation processor options on the [Advanced Setup](/advanced/advanced-setup) page.
{% endtab %}

{% tab title="Flutter/Dart Native" %}
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.json` and
* supporting code in `lib/objectbox.g.dart`.

To customize the directory where generated files are written see [Advanced Setup](/advanced/advanced-setup).

{% hint style="info" %}
If you **make changes to your entities**, e.g. by adding a property or modifying annotations, or after the **ObjectBox library has updated** make sure to **re-run the generator** so generated ObjectBox code is updated.
{% endhint %}

{% hint style="success" %}
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.
{% endhint %}

{% hint style="info" %}
Actually we lied above. The generator will process `lib` and `test` folders separately and generate files for each one (if `@Entity` classes exist there). This allows to create a separate test database that does not share any of the entity classes with the main database.
{% endhint %}
{% endtab %}

{% tab title="Python" %}
{% hint style="info" %}
Python bindings offer a convenient default Model to which Entity definitions are automatically associated if not specified otherwise.\
Similar to the other bindings, a JSON model file is also used for management of Schema history (i.e. to handle add/remove/rename of Entity and Property).
{% endhint %}
{% endtab %}
{% endtabs %}

Among other files ObjectBox generates a JSON **model file**, by default to

* `app/objectbox-models/default.json` for Android projects,
* `lib/objectbox-model.json` for Dart/Flutter projects, or
* `<user-module-dir>/objectbox-model.json` for Python projects

To change the model file path, see [Advanced Setup](/advanced/advanced-setup).

{% hint style="info" %}
In Android Studio you might have to switch the *Project view* from *Android* to *Project* to see the `default.json` model file.\
\
Python checks for the call-stack to determine the user-module directory in which the JSON file is stored.
{% endhint %}

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**.

{% hint style="success" %}
The model file also enables you to keep data [when renaming entities or properties](/advanced/data-model-updates) or to [resolve conflicts](https://docs.objectbox.io/advanced/meta-model-ids-and-uids) when two of your developers make changes at the same time.
{% endhint %}

## Create a Store

[**BoxStore**](https://objectbox.io/files/objectbox-java/current/io/objectbox/BoxStore.html) (Java) or [**Store**](https://pub.dev/documentation/objectbox/latest/objectbox/Store-class.html) (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.

{% tabs %}
{% tab title="Java (Android)" %}
Create it using the builder returned by the generated `MyObjectBox` class, for example in a small helper class like this:

```java
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; }
}
```

{% hint style="warning" %}
If you encounter `UnsatisfiedLinkError` or `LinkageError` on the build call, see [App Bundle, split APKs and Multidex](/android/app-bundle-and-split-apk) for solutions.
{% endhint %}

The best time to initialize ObjectBox is when your app starts. We suggest to do it in the `onCreate` method of your [Application class](https://developer.android.com/reference/android/app/Application):

```java
public class ExampleApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        ObjectBox.init(this);
    }
}
```

{% endtab %}

{% tab title="Kotlin (Android)" %}
Create it using the builder returned by the generated MyObjectBox class, for example in a small helper class like this:

```kotlin
object ObjectBox {
    lateinit var store: BoxStore
        private set

    fun init(context: Context) {
        store = MyObjectBox.builder()
                .androidContext(context)
                .build()
    }
}
```

{% hint style="warning" %}
If you encounter `UnsatisfiedLinkError` or `LinkageError` on the build call, see [App Bundle, split APKs and Multidex](/android/app-bundle-and-split-apk) for solutions.
{% endhint %}

The best time to initialize ObjectBox is when your app starts. We suggest to do it in the `onCreate` method of your [Application class](https://developer.android.com/reference/android/app/Application):

```kotlin
class ExampleApp : Application() {
    override fun onCreate() {
        super.onCreate()
        ObjectBox.init(this)
    }
}
```

{% endtab %}

{% tab title="JVM" %}

```java
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.
{% endtab %}

{% tab title="Flutter" %}
**Create it** using the generated `openStore()` method, for example in a small helper class like this:

```dart
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);
  }
}
```

{% hint style="info" %}
**For sandboxed macOS apps** also pass `macosApplicationGroup` to `openStore()`. See the notes about "macOS application group" in [the constructor documentation](https://pub.dev/documentation/objectbox/latest/objectbox/Store/Store.html) of the `Store` class.

For example:

`openStore(macosApplicationGroup: "FGDTDLOBXDJ.demo")`
{% endhint %}

{% hint style="info" %}
**On mobile devices or sandboxed apps** data should be stored in the app's documents directory. See [Flutter: read & write files](https://flutter.dev/docs/cookbook/persistence/reading-writing-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)`.
{% endhint %}

The best time to **initialize ObjectBox** is when your app starts. We suggest to do it in your app's `main()` function:

```dart
/// 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());
}
```

{% hint style="info" %}
**When using Dart isolates,** note that [each Dart isolate has its own global fields](https://dart.dev/language/concurrency#isolates), they do not share state on the Dart level.

However, as ObjectBox runs on the native or process level (so one native instance shared across all isolates), instead of creating a new Store in another isolate your code should instead [attach to the open native store](https://pub.dev/documentation/objectbox/latest/objectbox/Store/Store.attach.html).
{% endhint %}
{% endtab %}

{% tab title="Dart Native" %}
Create it using the generated `openStore()` method, for example like this:

```dart
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.

{% hint style="info" %}
**When using Dart isolates,** note that [each Dart isolate has its own global fields](https://dart.dev/language/concurrency#isolates), they do not share state on the Dart level.

However, as ObjectBox runs on the native or process level (so one native instance shared across all isolates), instead of creating a new Store in another isolate your code should instead [attach to the open native store](https://pub.dev/documentation/objectbox/latest/objectbox/Store/Store.attach.html).
{% endhint %}
{% endtab %}

{% tab title="Python" %}

```python
from objectbox import Store
  
store = Store()
```

{% endtab %}
{% endtabs %}

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:

{% tabs %}
{% tab title="Java" %}

<pre class="language-java"><code class="lang-java"><strong>BoxStore inMemoryStore = MyObjectBox.builder()
</strong>        .androidContext(context)
        .inMemory("test-db")
        .build();
</code></pre>

{% endtab %}

{% tab title="Dart" %}

```dart
 final inMemoryStore =
     Store(getObjectBoxModel(), directory: "memory:test-db");
```

{% endtab %}

{% tab title="Python" %}

```python
store = Store(directory="memory:testdata")
```

{% endtab %}
{% endtabs %}

For **more store configuration options:** for Java see the [BoxStoreBuilder](https://objectbox.io/docfiles/java/current/io/objectbox/BoxStoreBuilder.html) and for Dart the [Store](https://pub.dev/documentation/objectbox/latest/objectbox/Store/Store.html) documentation. (Python APIs will be published soon)

## Basic Box operations

The[ Box class](https://objectbox.io/files/objectbox-java/current/io/objectbox/Box.html) 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:

{% tabs %}
{% tab title="Java" %}

```java
Box<User> userBox = store.boxFor(User.class);
Box<Order> orderBox = store.boxFor(Order.class);
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val userBox = store.boxFor(User::class)
val orderBox = store.boxFor(Order::class)
```

{% endtab %}

{% tab title="Dart" %}

```dart
final userBox = store.box<User>();
final orderBox = store.box<Order>();
```

{% endtab %}

{% tab title="Python" %}

```python
user_box = store.box(User)
order_box = store.box(Order)
```

{% endtab %}
{% endtabs %}

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.

{% tabs %}
{% tab title="Java" %}

```java
User user = new User("Tina");
userBox.put(user);

List<User> users = getNewUsers();
userBox.put(users);
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val user = User(name = "Tina")
userBox.put(user)

val users: List<User> = getNewUsers()
userBox.put(users)
```

{% endtab %}

{% tab title="Dart" %}

```dart
final user = User(name: 'Tina');
userBox.put(user);

final users = getNewUsers();
userBox.putMany(users);
```

{% endtab %}

{% tab title="Python" %}

```python
user = User(name="Tina")
user_box.put(user)

users = get_new_users()
user_box.put(*users)
```

{% endtab %}
{% endtabs %}

**get and getAll:** Given an object’s ID, `get` reads it from its box. To get all objects in the box use `getAll` .

{% tabs %}
{% tab title="Java" %}

```java
User user = userBox.get(userId);

List<User> users = userBox.getAll();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val user = userBox[userId]

val users = userBox.all
```

{% endtab %}

{% tab title="Dart" %}

```dart
final user = userBox.get(userId);

final users = userBox.getMany(userIds);

final users = userBox.getAll();
```

{% endtab %}

{% tab title="Python" %}

```python
user = user_box.get(user_id)

users = user_box.get_all()
```

{% endtab %}
{% endtabs %}

**query:** Starts building a query to return objects from the box that match certain conditions. See [queries](/queries) for details.

{% tabs %}
{% tab title="Java" %}

```java
Query<User> query = userBox
    .query(User_.name.equal("Tom"))
    .order(User_.name)
    .build();
List<User> results = query.find();
query.close();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val query = userBox
    .query(User_.name.equal("Tom"))
    .order(User_.name)
    .build()
val results = query.find()
query.close()
```

{% endtab %}

{% tab title="Dart" %}

```dart
final query =
    (userBox.query(User_.name.equals('Tom'))..order(User_.name)).build();
final results = query.find();
query.close();
```

{% endtab %}

{% tab title="Python" %}

```python
query = user_box \
    .query(User.name.equals('Tom')) \
    .build()
results = query.find()
```

{% endtab %}
{% endtabs %}

**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.

{% tabs %}
{% tab title="Java" %}

```java
boolean isRemoved = userBox.remove(userId);

userBox.remove(users);
// alternatively:
userBox.removeByIds(userIds);

userBox.removeAll();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val isRemoved = userBox.remove(userId)

userBox.remove(users)
// alternatively:
userBox.removeByIds(userIds)

userBox.removeAll()
```

{% endtab %}

{% tab title="Dart" %}

```dart
final isRemoved = userBox.remove(userId);

userBox.removeMany(userIds);

userBox.removeAll();
```

{% endtab %}

{% tab title="Python" %}

```python
is_removed = user_box.remove(user_id)

user_box.remove_all()
```

{% endtab %}
{% endtabs %}

**count:** Returns the number of objects stored in this box.

{% tabs %}
{% tab title="Java" %}

```java
long userCount = userBox.count();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val userCount = userBox.count()
```

{% endtab %}

{% tab title="Dart" %}

```dart
final userCount = userBox.count();
```

{% endtab %}

{% tab title="Python" %}

```
user_box.count()
```

{% endtab %}
{% endtabs %}

For a complete list of methods available in the Box class, check the API reference documentation for [Java](https://objectbox.io/files/objectbox-java/current/io/objectbox/Box.html) or [Dart](https://pub.dev/documentation/objectbox/latest/objectbox/Box-class.html).

### Asynchronous operations

ObjectBox has built-in support to run (typically multiple or larger) database operations asynchronously.

{% tabs %}
{% tab title="Java" %}
**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.

```java
store.callInTxAsync(() -> {
    Box<User> box = store.boxFor(User.class);
    String name = box.get(userId).name;
    box.remove(userId);
    return name;
}, (result, error) -> {
    if (error != null) {
        System.out.println("Failed to remove user with id " + userId);
    } else {
        System.out.println("Removed user with name: " + result);
    }
});
```

**newCachedThreadPoolExecutor** and **newFixedThreadPoolExecutor**: create an [ObjectBoxThreadPoolExecutor](https://github.com/objectbox/objectbox-java/blob/main/objectbox-java/src/main/java/io/objectbox/ObjectBoxThreadPoolExecutor.java) to asynchronously execute ObjectBox operations. This default thread pool executor implementation properly cleans up thread-local ObjectBox resources. Use this if the async methods above don't work for your use case or your code needs full control over the thread pool.

```java
// Create a cached thread pool executor optimized for ObjectBox.
// Use ObjectBoxThreadPoolExecutor directly for more customization options.
private ObjectBoxThreadPoolExecutor executor = store.newCachedThreadPoolExecutor();

try {
    Future<String> future = executor.submit(() -> store.callInTx(() -> {
        Box<User> box = store.boxFor(User.class);
        String name = box.get(userId).name;
        box.remove(userId);
        return name;
    }));
    String result = future.get();
    System.out.println("Removed user with name: " + result);
} catch (Exception e) {
    System.err.println("Failed to remove user with id " + userId);
}
```

#### Kotlin Coroutines

**awaitCallInTx:** 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.

```kotlin
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 (_: Exception) {
    println("Failed to remove user with id $userId")
}
```

**newCachedThreadPoolDispatcher** and **newFixedThreadPoolDispatcher**: create a coroutine dispatcher backed by an [ObjectBoxThreadPoolExecutor](https://github.com/objectbox/objectbox-java/blob/main/objectbox-java/src/main/java/io/objectbox/ObjectBoxThreadPoolExecutor.java) to asynchronously execute ObjectBox operations. The executor properly cleans up thread-local ObjectBox resources. For example, use it instead of `Dispatchers.IO` when doing ObjectBox operations in coroutines.

```kotlin
// Create a Coroutine Dispatcher optimized for ObjectBox.
// Use ObjectBoxThreadPoolExecutor and asCoroutineDispatcher() directly 
// for more customization options.
private val dispatcher = store.newCachedThreadPoolDispatcher()

// Within a suspend function
val name = withContext(dispatcher) {
    store.callInTx {
        val box = store.boxFor(User::class.java)
        val name = box.get(userId).name
        box.remove(userId)
        name
    }
}
println("Removed user with name $name")
```

{% endtab %}

{% tab title="Dart" %}
**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.

```dart
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**](https://pub.dev/documentation/objectbox/latest/objectbox/Store/runInTransactionAsync.html)**):** run a callback with multiple database operations within a write or read transaction in the background without blocking the user interface. Can return results.

```dart
// 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**](https://pub.dev/documentation/objectbox/latest/objectbox/Store/runAsync.html)**):** 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.

```dart
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));
```

{% endtab %}

{% tab title="Python" %}
{% hint style="info" %}
Currently work in progress.
{% endhint %}
{% endtab %}
{% endtabs %}

## 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:

{% tabs %}
{% tab title="Java" %}

```java
User user = new User();
// user.id == 0
box.put(user);
// user.id != 0
long id = user.id;
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val user = User()
// user.id == 0
box.put(user)
// user.id != 0
val id = user.id
```

{% endtab %}

{% tab title="Dart" %}

```dart
final user = User();
// user.id == 0
box.put(user);
// user.id != 0
final id = user.id;
```

{% endtab %}

{% tab title="Python" %}

```
user = User()
box.put(user)
id: int = user.id
```

{% endtab %}
{% endtabs %}

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.

{% hint style="info" %}
If you **need to assign IDs by yourself,** have a look at [how to switch to self-assigned IDs](/advanced/object-ids#self-assigned-object-ids) and what side effects apply.
{% endhint %}

### 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](/advanced/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](/transactions).

## 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](http://greenrobot.org/greendao/documentation/objectbox-compat/) and [the example](https://github.com/objectbox/objectbox-examples/tree/master/android-app-daocompat). [Contact us](https://github.com/objectbox/objectbox-java/issues) if you have any questions!

## Next steps

* Check out the [ObjectBox example projects on GitHub](https://github.com/objectbox/objectbox-examples/).
* Learn about [Queries](/queries) and [Relations](/relations).
* Learn [how to write unit tests](/android/android-local-unit-tests).
* To enable debug mode and for advanced use cases, see the [Advanced Setup](/advanced/advanced-setup) page.


# Tutorial: Demo Project

Learn how to build a simple note-taking app with ObjectBox.

This tutorial will walk you through a simple note-taking app explaining how to do basic operations with ObjectBox. To just integrate ObjectBox into your project, look at the [Getting Started](/getting-started) page.

You can check out the example code from GitHub. This allows you to run the code and explore it in its entirety. It is a simple app for taking notes where you can add new notes by typing in some text and delete notes by clicking on an existing note.

{% tabs %}
{% tab title="Java" %}

```bash
git clone https://github.com/objectbox/objectbox-examples.git
cd objectbox-examples/android-app
```

{% endtab %}

{% tab title="Kotlin" %}

```bash
git clone https://github.com/objectbox/objectbox-examples.git
cd objectbox-examples/android-app-kotlin
```

{% endtab %}

{% tab title="Dart" %}

```bash
git clone https://github.com/objectbox/objectbox-dart.git
cd objectbox-dart/objectbox/examples/flutter/objectbox_demo
```

{% endtab %}

{% tab title="Python" %}

```sh
git clone https://github.com/objectbox/objectbox-python.git
cd objectbox-python
```

{% endtab %}
{% endtabs %}

## The Note entity and Box class

To store notes there is an entity class called `Note` (or `Task` in Python). It defines the structure or model of the data persisted (saved) in the database for a note: its id, the note text and the creation date.

{% tabs %}
{% tab title="Java" %}
{% code title="src/Note.java" %}

```java
@Entity
public class Note {
    
    @Id
    long id;
    
    String text;
    String comment;
    Date date;
    
    ...
}
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code title="src/Note.kt" %}

```kotlin
@Entity
data class Note(
        @Id var id: Long = 0,
        var text: String? = null,
        var comment: String? = null,
        var date: Date? = null
)
```

{% endcode %}
{% endtab %}

{% tab title="Dart" %}
{% code title="lib/model.dart" %}

```dart
@Entity()
class Note {
  int id;
  String text;
  String? comment;
  DateTime date;

  ...
}
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code title="example/tasks/main.py" %}

```python

@Entity()
class Task:
    id = Id()
    text = String()
    date_created = Date(py_type=int)
    date_finished = Date(py_type=int)
```

{% endcode %}
{% endtab %}
{% endtabs %}

In general, an ObjectBox *entity* is an annotated class persisted in the database with its properties. In order to extend the note or to create new entities, you simply modify or create new plain classes and annotate them with `@Entity` and `@Id;` in Python opt-in `uid` argument i.e. `@Entity(uid=)`.

{% content-ref url="/pages/-LEUYsjP1BaBoJ\_OPUD3" %}
[Entity Annotations](/entity-annotations)
{% endcontent-ref %}

{% tabs %}
{% tab title="Java" %}
Go ahead and build the project, for example by using **Build > Make project** in Android Studio. This triggers ObjectBox to generate some classes, like `MyObjectBox.java`, and some other classes used by ObjectBox internally.
{% endtab %}

{% tab title="Kotlin" %}
Go ahead and build the project, for example by using **Build > Make project** in Android Studio. This triggers ObjectBox to generate some classes, like `MyObjectBox.kt`, and some other classes used by ObjectBox internally.
{% endtab %}

{% tab title="Dart" %}
Before running the app, run the ObjectBox code generator to create binding code for the entity classes: `flutter pub run build_runner build`

Also re-run this after changing the note class.
{% endtab %}

{% tab title="Python" %}

```
# Make sure to install objectbox >= 4.0.0
$ pip install --upgrade objectbox
$ ls example
ollama 
tasks
vectorsearch-cities

$ cd tasks
$ python main.py

Welcome to the ObjectBox tasks-list app example. Type help or ? for a list of commands.
> 
```

{% endtab %}
{% endtabs %}

### Inserting notes

To see how new notes are added to the database, take a look at the following code fragments. The `Box` provides database operations for `Note` objects. A Box is the main interaction with object data.

{% tabs %}
{% tab title="Java" %}
{% code title="NoteActivity.java" %}

```java
@Override
public void onCreate(Bundle savedInstanceState) {
    ...
    notesBox = ObjectBox.get().boxFor(Note.class);
    ...
}
```

{% endcode %}

{% hint style="info" %}
Note: In the example project, `ObjectBox` is the name of a helper class to set up and keep a reference to `BoxStore`.
{% endhint %}
{% endtab %}

{% tab title="Kotlin" %}
{% code title="NoteActivity.kt" %}

```kotlin
public override fun onCreate(savedInstanceState: Bundle?) {
    ...
    notesBox = ObjectBox.boxStore.boxFor()
    ...
}
```

{% endcode %}

{% hint style="info" %}
Note: In the example project, `ObjectBox` is the name of a helper class to set up and keep a reference to `BoxStore`.
{% endhint %}
{% endtab %}

{% tab title="Dart" %}
{% code title="lib/objectbox.dart" %}

```dart
class ObjectBox {
  late final Store _store;
  late final Box<Note> _box;
  
  ObjectBox._create(this._store) {
    _noteBox = Box<Note>(_store);
    ...
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code title="example/tasks/main.py" %}

```python
class TasklistCmd(Cmd):
    # ...

    def __init__(self):
        # ...
        self._store = Store(directory="tasklist-db")
        self._task_box = self._store.box(Task)
```

{% endcode %}
{% endtab %}
{% endtabs %}

When a user adds a note the method `addNote()` is called. There, a new `Note` object is created and put into the database using the `Box` reference:

{% tabs %}
{% tab title="Java" %}
{% code title="NoteActivity.java" %}

```java
private void addNote() {
    ...
    Note note = new Note();
    note.setText(noteText);
    note.setComment(comment);
    note.setDate(new Date());
    notesBox.put(note);
    Log.d(App.TAG, "Inserted new note, ID: " + note.getId());
    ...
}
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code title="NoteActivity.kt" %}

```kotlin
private fun addNote() {
    ...
    val note = Note(text = noteText, comment = comment, date = Date())
    notesBox.put(note)
    Log.d(App.TAG, "Inserted new note, ID: " + note.id)
    ...
}
```

{% endcode %}
{% endtab %}

{% tab title="Dart" %}
{% code title="lib/objectbox.dart" %}

```dart
Future<void> addNote(String text) => _noteBox.putAsync(Note(text));
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code title="example/tasks/main.py" %}

```python
def add_task(self, text: str):
    task = Task(text=text, date_created=now_ms())
    self._task_box.put(task)
```

{% endcode %}
{% endtab %}
{% endtabs %}

Note that the ID property (0 when creating the `Note` object), is assigned by ObjectBox during a put.

### Removing/deleting notes

When the user taps a note, it is deleted. The `Box` provides `remove()` to achieve this:

{% tabs %}
{% tab title="Java" %}
{% code title="NoteActivity.java" %}

```java
OnItemClickListener noteClickListener = new OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        Note note = notesAdapter.getItem(position);
        notesBox.remove(note);
        Log.d(App.TAG, "Deleted note, ID: " + note.getId());
        ...
    }
};
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code title="NoteActivity.kt" %}

```kotlin
private val noteClickListener = OnItemClickListener { _, _, position, _ ->
    notesAdapter.getItem(position)?.also {
        notesBox.remove(it)
        Log.d(App.TAG, "Deleted note, ID: " + it.id)
    }
    ...
}
```

{% endcode %}
{% endtab %}

{% tab title="Dart" %}
{% code title="lib/objectbox.dart" %}

```dart
Future<void> removeNote(int id) => _noteBox.removeAsync(id);
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code title="example/tasks/main.py" %}

```python
def remove_task(self, task_id: int) -> bool:
    is_removed = self._task_box.remove(task_id)
    return is_removed
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Querying notes

To query and display notes in a list a `Query` instance is built once:

{% tabs %}
{% tab title="Java" %}
{% code title="NoteActivity.java" %}

```java
@Override
public void onCreate(Bundle savedInstanceState) {
    ...
    // Query all notes, sorted a-z by their text.
    notesQuery = notesBox.query().order(Note_.text).build();
    ...
}
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code title="NoteActivity.kt" %}

```kotlin
public override fun onCreate(savedInstanceState: Bundle?) {
    ...
    // Query all notes, sorted a-z by their text.
    notesQuery = notesBox.query {
        order(Note_.text)
    }
    ...
}
```

{% endcode %}
{% endtab %}

{% tab title="Dart" %}
{% code title="lib/objectbox.dart" %}

```dart
Stream<List<Note>> getNotes() {
  // Query for all notes, sorted by their date.
  // https://docs.objectbox.io/queries
  final builder = _noteBox.query().order(Note_.date, flags: Order.descending);
  ...
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code title="example/tasks/main.py" %}

```python
def __init__(self):
    # ...
    self._query = self._task_box.query().build()
```

{% endcode %}
{% endtab %}
{% endtabs %}

And then executed each time any notes change:

{% tabs %}
{% tab title="Java" %}
{% code title="NoteActivity.java" %}

```java
private void updateNotes() {
    List<Note> notes = notesQuery.find();
    notesAdapter.setNotes(notes);
}
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code title="NoteActivity.kt" %}

```kotlin
private fun updateNotes() {
    val notes = notesQuery.find()
    notesAdapter.setNotes(notes)
}
```

{% endcode %}
{% endtab %}

{% tab title="Dart" %}
{% code title="lib/objectbox.dart" %}

```dart
  ...
  // Build and watch the query,
  // set triggerImmediately to emit the query immediately on listen.
  return builder
      .watch(triggerImmediately: true)
      // Map it to a list of notes to be used by a StreamBuilder.
      .map((query) => query.find());
}
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code title="example/tasks/main.py" %}

```python
def find_tasks(self):
    return self._query.find()
```

{% endcode %}

{% hint style="info" %}
You can also use `self._task_box.get_all()` to get all Task objects without any condition (instead of building a query).
{% endhint %}
{% endtab %}
{% endtabs %}

In addition to a result sort order, you can add various conditions to filter the results, like equality or less/greater than, when building a query.

{% content-ref url="/pages/-LEUom1xpQh\_31VZC80C" %}
[ObjectBox Queries](/queries)
{% endcontent-ref %}

### Updating notes and more

What is not shown in the example, is how to update an existing (== the ID is not 0) note. Do so by just modifying any of its properties and then put it again with the changed object:

{% tabs %}
{% tab title="Java" %}

```java
note.setText("This note has changed.");
notesBox.put(note);
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
note.text = "This note has changed."
notesBox.put(note)
```

{% endtab %}

{% tab title="Dart" %}

```dart
note.text = "This note has changed.";
_noteBox.putAsync(note);
```

{% endtab %}

{% tab title="Python" %}

```python
task.text = "This task has changed."
self._task_box.put(task)
```

{% endtab %}
{% endtabs %}

There are **additional methods to put, find, query, count or remove** entities. Check out the methods of the Box class in API docs (for [Java/Kotlin](https://objectbox.io/docfiles/java/current/io/objectbox/Box.html) or [Dart](https://pub.dev/documentation/objectbox/latest/objectbox/Box-class.html)) to learn more.

{% content-ref url="/pages/-LEUEB3nMDmS4vcFKZju" %}
[Getting Started](/getting-started)
{% endcontent-ref %}

## Setting up the database

Now that you saw ObjectBox in action, how did we get that database (or store) instance? Typically you should set up a BoxStore or Store once for the whole app. This example uses a [helper class as recommended in the Getting Started guide](/getting-started#core-initialization).

{% content-ref url="/pages/-LEUEB3nMDmS4vcFKZju" %}
[Getting Started](/getting-started)
{% endcontent-ref %}

Remember: ObjectBox is a NoSQL database on its own and thus NOT based on SQL or SQLite. That’s why you do not need to set up “CREATE TABLE” statements during initialization.

{% hint style="info" %}
Note: it is perfectly fine to never close the database. That’s even recommended for most apps.
{% endhint %}


# Entity Annotations

Instead of SQL, you define your data model by annotating your persistent object types on the programming language level.

## ObjectBox - Database Persistence with Entity Annotations

ObjectBox is a database that persists objects, which we sometimes also call **entities**.

To let ObjectBox know which classes are entities you annotate them with `@Entity`. This annotation identifies the class `User` in the following example as a persistable entity. This will trigger ObjectBox to generate persistence code tailored for this class:

{% tabs %}
{% tab title="Java" %}

```java
@Entity
public class User {
    
    @Id
    private long id;
    
    private String name;
    
    // Not persisted:
    @Transient
    private int tempUsageCount;
    
   // TODO: getters and setters.
}
```

{% hint style="info" %}
Note:

* It’s often good practice to model entities as **“dumb” data classes (POJOs)** with just properties.
* **Entities must 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.
  {% endhint %}
  {% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Entity
data class User(
        @Id var id: Long = 0,
        var name: String? = null,
        // Not persisted:
        @Transient var tempUsageCount: Int = 0
)
```

{% hint style="info" %}
Note:

* It’s often good practice to model entities as **“dumb” data classes (POJOs)** with just properties.
* **Entities must 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.\
  For Kotlin data classes this can be achieved by **adding default values for all parameters.** (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.)
  {% endhint %}
  {% endtab %}

{% tab title="Dart" %}

```dart
@Entity()
class User {
  // Annotate with @Id() if name isn't "id" (case insensitive).
  int id = 0;
  String? name;
  
  // Not persisted:
  @Transient
  int tempUsageCount = 0;
)
```

{% hint style="info" %}
It’s often good practice to model entities as **“dumb” data classes** with just properties.
{% endhint %}

{% hint style="info" %}
For ObjectBox to be able to construct objects read from the database, entities must have a no-args constructor or a constructor with argument names matching the properties, for example:

```dart
User({this.id, this.name});
```

{% endhint %}
{% endtab %}

{% tab title="Python" %}

```python
@Entity()
class User:
    id = Id
    name = String
    temp_usage_count = None
```

{% hint style="info" %}
Available Property types include: Bool, Char, Bytes, String, Int8/16/32/64, Float32/64 \*Vector and \*List variants (e.g. Float32Vector)
{% endhint %}
{% endtab %}
{% endtabs %}

## Object IDs: @Id

In ObjectBox, **entities must have one 64-bit integer ID property** with **non-private visibility** (or non-private getter and setter method) to efficiently get or reference objects.

{% tabs %}
{% tab title="Java" %}

```java
@Entity
public class User {
    @Id public long id;
    // Note: You can use the nullable java.lang.Long, but we do not recommend it.
    
    ...
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Entity
data class User(
        @Id var id: Long = 0,
        ...
)
```

{% endtab %}

{% tab title="Dart" %}

```dart
@Entity()
class User {
  // Annotate with @Id() if name isn't "id" (case insensitive).
  int id;

  ...
}
```

{% endtab %}

{% tab title="Python" %}

```python
@Entity()
class User:
    id = Id  # Use either class Id or instance notation (e.g. Id())
```

{% endtab %}
{% endtabs %}

If you **need to use another type for IDs** (such as a string UID given by a server), model them as regular properties and use [queries](/queries) to look up objects by your application-specific ID. Also, make sure to index the property, and if it's a string use a case-sensitive condition, to speed up lookups. To prevent duplicates it is also possible to enforce a [unique](#unique-constraints) value for this secondary ID.

{% tabs %}
{% tab title="Java" %}

```java
@Entity
class StringIdEntity {
    @Id public long id;
    @Index public String uid;
    // Alternatively:
    // @Unique String uid;
}

StringIdEntity entity = box.query()
    .equal(StringIdEntity_.uid, uid, StringOrder.CASE_SENSITIVE)
    .build().findUnique()
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Entity
data class StringIdEntity(
        @Id var id: Long = 0,
        @Index var uid: String? = null
        // Alternatively:
        // @Unique uid: String? = null
)

val entity = box.query()
        .equal(StringIdEntity_.uid, uid, StringOrder.CASE_SENSITIVE)
        .build().findUnique()
```

{% endtab %}

{% tab title="Dart" %}

```dart
@Entity()
class StringIdEntity(
  int id;
  
  @Index() // or alternatively use @Unique()
  String uid;
)

final objects = box.query(StringIdEntity_.uid.equals('...')).build().find();
```

{% endtab %}
{% endtabs %}

ID properties are unique and indexed by default.

When you put a new object you do not assign an ID. By default **IDs for new objects are assigned by ObjectBox**. See the page on [Object IDs](/advanced/object-ids) for details.

{% hint style="info" %}
If you **need to assign IDs by yourself** have a look at [how to switch to self-assigned IDs](/advanced/object-ids#self-assigned-object-ids) and what side effects apply.
{% endhint %}

## Make entity data accessible

ObjectBox needs to access the data of your entity’s properties (e.g. in the generated code). You have two options:

1. Make sure properties do **not** have private visibility.
2. Provide standard getters (your IDE can generate them easily).

To improve performance when ObjectBox constructs your entities, you might also want to provide an all-properties constructor.

{% tabs %}
{% tab title="Java" %}

```java
@Entity
public class Order {
    
    // Option 1: field is not private.
    @Id long id;
    
    // Option 2: field is private, but getter is available.
    private String name;
    
    public ToOne<Customer> customer;
    public ToMany<Order> relatedOrders;
    
    // At minimum, provide a default constructor for ObjectBox.
    public Order() {
    }

    // Optional: all-properties constructor for better performance.
    // - make sure type matches exactly,
    // - for ToOne add its virtual ID property instead,
    // - for ToMany add no parameter.
    public Order(long id, String name, long customerId) {
        this.id = id;
        this.name = name;
        this.customer.setTargetId(customerId);
    }
    
    public String getName() {
        return this.name;
    }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// For Kotlin a data class with default values
// meets all above requirements. 
@Entity
data class User(
        @Id var id: Long = 0,
        var name: String? = null
)
```

{% endtab %}

{% tab title="Dart" %}

```dart
@Entity()
class User {
  int id;
    
  String? _name;
  
  String get name {...}
  
  set name(String value) {...}
    
  User(this.id);
}
```

{% endtab %}
{% endtabs %}

## Supported property types

ObjectBox supports a wide range of property types including integers, floats, strings, dates, lists, and flex properties for dynamic data. See the dedicated page for details:

{% content-ref url="/pages/kSqRmDrNicE3QmU2oyBf" %}
[Property Types](/property-types)
{% endcontent-ref %}

If the built-in types don't cover your needs, you can use [custom types and converters](/advanced/custom-types).

## Basic annotations for entity properties

### Transient

**`@Transient`** marks properties that should not be persisted. In Java `static` or `transient` properties will also not be persisted.

{% tabs %}
{% tab title="Java" %}

```java
@Transient
private int notPersisted;
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Transient
var notPersisted: Int = 0
```

{% endtab %}

{% tab title="Dart" %}

```dart
@Transient()
int? notPersisted;
```

{% endtab %}
{% endtabs %}

### NameInDb

{% hint style="info" %}
Only available for Java/Kotlin at the moment
{% endhint %}

**`@NameInDb`** lets you define a name on the database level for a property. This allows you to rename the property without affecting the property name on the database level.

{% hint style="warning" %}

* We recommend [**using @Uid annotations**](/advanced/data-model-updates) to rename properties and even entities **instead**.
* @NameInDb only works with inline constants to specify a column name.
  {% endhint %}

{% tabs %}
{% tab title="Java" %}

```java
@NameInDb("username")
private String name;
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@NameInDb("username")
var name: String? = null
```

{% endtab %}
{% endtabs %}

## Property Indexes

Annotate a property with **`@Index`** to create a database index for the corresponding database column. This can improve performance when querying for that property.

{% tabs %}
{% tab title="Java" %}

```java
@Index
private String name;
```

{% hint style="warning" %}
@Index is currently not supported for `String[]`, `byte[]`, `float` and `double`
{% endhint %}
{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Index
var name: String? = null
```

{% hint style="warning" %}
@Index is currently not supported for `Array<String>`, `ByteArray` , `Float` and `Double`
{% endhint %}
{% endtab %}

{% tab title="Dart" %}

```dart
@Index()
String name;
```

{% hint style="warning" %}
@Index is currently not supported for `double` and lists`List<String>, List<int>, Uint8List, Int8List`
{% endhint %}
{% endtab %}

{% tab title="Python" %}

```python
@Entity()
class User:
  name = String(index=Index())
```

{% endtab %}
{% endtabs %}

An index stores additional information in the database to make lookups faster. As an analogy, we could look at Java-like programming languages where you store objects in a list. For example, you could store persons using a `List<Person>`. Now, you want to search for all persons with a specific name so you would iterate through the list and check for the name property of each object. This is an O(N) operation and thus doesn't scale well with an increasing number of objects. To make this more scalable you can introduce a second data structure `Map<String, Person>` with the name as a key. This will give you a constant lookup time (O(1)). The downside of this is that it needs more resources (here: RAM) and slows down add/remove operations on the list a bit. These principles can be transferred to database indexes, just that the primary resource consumed is disk space.

### Index types (String)

For scalar properties, ObjectBox uses a value-based index. Because `String` properties typically require more storage space than scalar values, by default ObjectBox uses a hash index for strings instead.

To override the default and use a value-based index for a `String` property, specify the index `type`:

{% tabs %}
{% tab title="Java" %}

```java
@Index(type = IndexType.VALUE)
private String name;
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Index(type = IndexType.VALUE)
var name: String? = null
```

{% endtab %}

{% tab title="Dart" %}

```dart
@Index(type: IndexType.value)
String name;
```

{% endtab %}
{% endtabs %}

Keep in mind that for `String`, depending on the length of your values, a value-based index may require more storage space than the default hash-based index.

ObjectBox supports these index types:

* **Not specified or DEFAULT** Uses the best index based on property type (HASH for `String`, VALUE for others).
* **VALUE** Uses property values to build the index. For `String,` this may require more storage than a hash-based index.
* **HASH** Uses a 32-bit hash of property values to build the index. Occasional collisions may occur which should not have any performance impact in practice. Usually, a better choice than HASH64, as it requires less storage.
* **HASH64** Uses a 64-bit hash of property values to build the index. Requires more storage than HASH and thus should not be the first choice in most cases.

{% hint style="info" %}
**Limits of hash-based indexes:** Hashes work great for equality checks, but not for **"starts with"** type conditions. If you frequently use those, you should use value-based indexes instead.
{% endhint %}

### Vector Index for Nearest Neighbor Search

To enable nearest neighbor search, a special index type for vector properties is available:

{% content-ref url="/pages/OtC8AlACVJfXcCtFY9sC" %}
[On-Device Vector Search](/on-device-vector-search)
{% endcontent-ref %}

## Unique constraints

Annotate a property with **`@Unique`** to enforce that values are unique before an entity is put:

{% tabs %}
{% tab title="Java" %}

```java
@Unique
private String name;
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Unique
var name: String? = null
```

{% endtab %}

{% tab title="Dart" %}

```dart
@Unique()
String? name;
```

{% endtab %}
{% endtabs %}

A `put()` operation will abort and throw a `UniqueViolationException` if the unique constraint is violated:

{% tabs %}
{% tab title="Java" %}

```java
try {
    box.put(new User("Sam Flynn"));
} catch (UniqueViolationException e) {
    // A User with that name already exists.
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
try {
    box.put(User("Sam Flynn"))
} catch (e: UniqueViolationException) {
    // A User with that name already exists.
}
```

{% endtab %}

{% tab title="Dart" %}

```dart
try {
    box.put(User('Sam Flynn'))
} on UniqueViolationException catch (e) {
    // A User with that name already exists.
}
```

{% endtab %}
{% endtabs %}

For a single property it is possible to specify that a conflicting object should be replaced instead of an exception being thrown:

{% tabs %}
{% tab title="Java" %}

```java
@Unique(onConflict = ConflictStrategy.REPLACE)
private String name;
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Unique(onConflict = ConflictStrategy.REPLACE)
var name: String? = null
```

{% endtab %}

{% tab title="Dart" %}

```dart
@Unique(onConflict: ConflictStrategy.replace)
String? name;
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
The `REPLACE` strategy will add a new object **with a different ID**. As relations (ToOne/ToMany) reference objects by ID, if the previous object was referenced in any relations, these need to be updated manually.
{% endhint %}

{% hint style="info" %}
Unique constraints are based on an index, so it is possible to further configure the index with an **@Index** annotation.
{% endhint %}

## Change database type

Use `@Type` in Java/Kotlin or the type attribute on `@Property` in Dart to override how the value of a property is stored and interpreted in the database.

{% tabs %}
{% tab title="Java" %}

```java
// Store 64-bit integer as time in nanoseconds.
@Type(DatabaseType.DateNano)
private long timeInNanos;
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// Store 64-bit integer as time in nanoseconds.
@Type(DatabaseType.DateNano)
var timeInNanos: Long = 0
```

{% endtab %}

{% tab title="Dart" %}

```dart
// Time with nanosecond precision.
@Property(type: PropertyType.dateNano)
DateTime nanoDate;

@Property(type: PropertyType.byte)
int byte; // 1 byte

@Property(type: PropertyType.short)
int short; // 2 bytes

@Property(type: PropertyType.char)
int char; // 1 bytes

@Property(type: PropertyType.int)
int int32; // 4 bytes

@Property(type: PropertyType.float)
double float; // 4 bytes

@Property(type: PropertyType.byteVector)
List<int> byteList;
```

{% endtab %}

{% tab title="Python" %}

```python
class Tasks:
    date_started = Date(py_type=int)
```

{% endtab %}
{% endtabs %}

## Relations

Creating to-one and to-many relations between objects is possible as well, see the [Relations](/relations) documentation for details.

## Triggering code generation

Once your entity schema is in place, you can [trigger the code generation process](/getting-started#generate-objectbox-code).


# Property Types

ObjectBox supports a variety of data types inside the stored objects including various list types and even special "flex" types for dynamic and schema-less data.

ObjectBox stores data as objects, which are instances of entity classes you define. Objects consist of properties, which are the individual fields that hold your data, like a user's name or age. This page covers the property types ObjectBox supports:

* **Standard types** for single values of a fixed type
* **List/Vector types** for collections of values of a fixed type
* **The flex type** for dynamic and schema-less data

## Standard Types

Standard types include boolean, integers, floating point types, string, and date types. These are the most common types you will use in typical applications.

### Quick Reference

| Category         | ObjectBox Type | Stored as      |
| ---------------- | -------------- | -------------- |
| Boolean          | Bool           | 1 byte         |
| Integer (8-bit)  | Byte           | 1 byte         |
| Integer (16-bit) | Short          | 2 bytes        |
| Integer (32-bit) | Int            | 4 bytes        |
| Integer (64-bit) | Long           | 8 bytes        |
| To-One Relation  | Relation       | 8 bytes        |
| Float (32-bit)   | Float          | 4 bytes        |
| Float (64-bit)   | Double         | 8 bytes        |
| Text Character   | Char           | 2 bytes        |
| String           | String         | UTF-8 bytes    |
| Date (ms)        | Date           | 8 bytes (long) |
| Date (ns)        | DateNano       | 8 bytes (long) |

{% hint style="info" %}
**Nullable types:** All ObjectBox property types support null values. In Java, use wrapper classes (`Integer` vs `int`) for nullable types. In Kotlin, Dart, and Python, nullability is part of the type system.
{% endhint %}

### Integers

ObjectBox supports all standard integer types, from 8-bit to 64-bit. Rule of thumb: use large enough integer types that are safe for your use case in the future. Use smaller types only if you are certain about the range of values as you cannot change the type of an existing property later.

{% tabs %}
{% tab title="Java" %}

```java
@Entity
public class Sensor {
    @Id public long id;
    
    public byte status;        // -128 to 127
    public short temperature;  // -32,768 to 32,767  
    public int count;          // ~2 billion range
    public long timestamp;     // Large numbers, IDs
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Entity
data class Sensor(
    @Id var id: Long = 0,
    var status: Byte = 0,
    var temperature: Short = 0,
    var count: Int = 0,
    var timestamp: Long = 0
)
```

Kotlin unsigned types (`UByte`, `UShort`, `UInt`, `ULong`) are also supported—they're stored as their signed equivalents.
{% endtab %}

{% tab title="Dart" %}
Note: Dart just has `int` and does not differentiate between the integer types. You can still use ObjectBox types to pick the storage type and align with other languages (e.g. when using Sync).

```dart
@Entity()
class Sensor {
  @Id()
  int id = 0;
  
  @Property(type: PropertyType.byte)
  int status = 0;  // 8-bit
  
  @Property(type: PropertyType.short)
  int temperature = 0;  // 16-bit
  
  @Property(type: PropertyType.int)
  int count = 0;  // 32-bit
  
  int timestamp = 0;  // 64-bit (default)
}
```

{% endtab %}

{% tab title="Python" %}

```python
@Entity()
class Sensor:
    id = Id
    status = Int8
    temperature = Int16
    count = Int32
    timestamp = Int64
```

{% endtab %}
{% endtabs %}

### Floating Point

Choose 32-bit floats for memory efficiency when \~7 digits of precision is enough. Use 64-bit doubles when you need \~15 digits.

{% tabs %}
{% tab title="Java" %}

```java
public float score;      // 32-bit, ~7 significant digits
public double precise;   // 64-bit, ~15 significant digits
```

{% endtab %}

{% tab title="Dart" %}

```dart
@Property(type: PropertyType.float)
double score = 0;  // stored as 32-bit

double precise = 0;  // 64-bit (default)
```

{% endtab %}
{% endtabs %}

### Strings

Strings are stored as UTF-8 encoded bytes without a length limit.

```java
public String name;
public String description;  // Can be very long
```

### Dates and Times

ObjectBox stores dates as 64-bit integers ("timestamps") representing time since the Unix epoch (January 1, 1970, 00:00:00 UTC). Choose your precision:

| Precision    | ObjectBox Type     | Use case                  |
| ------------ | ------------------ | ------------------------- |
| Milliseconds | Date (the default) | Most applications         |
| Nanoseconds  | DateNano           | High-precision timestamps |

Note: while DateNano values are always stored as nanoseconds, the actual precision of the values depends on the platform and programming language. For example, Dart uses only microsecond precision (the last three digits of the nanosecond are "truncated").

{% tabs %}
{% tab title="Java" %}

```java
// Millisecond precision (default)
public Date createdAt;

// Nanosecond precision
@Type(DatabaseType.DateNano)
public long preciseTimestamp;
```

{% endtab %}

{% tab title="Dart" %}

```dart
// Millisecond precision (default)
@Property(type: PropertyType.dateUtc)
DateTime? createdAt;

// Stored with nanosecond precision; however,
// Dart uses only microsecond precision! 
@Property(type: PropertyType.dateNanoUtc)
DateTime? preciseTimestamp;
```

{% hint style="warning" %}
Proper UTC handling requires ObjectBox for Dart/Flutter 5.1 with the `dateUtc` and `dateNanoUtc` annotations. Prior versions do not support proper UTC handling. Please update and avoid the old `date` and `dateNano` annotations.
{% endhint %}
{% endtab %}
{% endtabs %}

### IDs and Relations

Every ObjectBox entity must have exactly one **ID property** of type Long (64-bit integer). See [Entity Annotations](/entity-annotations#object-ids-id) for details.

**"Relation"** is used for one of two relation types available in ObjectBox. It's a 64-bit integer that references the ID of another object. Depending on the programming language you use, it may translate to a "to-one" construct. See [Relations](/relations) for complete documentation.

## Lists and Arrays

Store collections of values directly; no special tables or joins required. The internal type names are "vectors", but they may translate to lists or arrays in the programming language you use.

{% hint style="info" %}
**Binary data** is stored as a `ByteVector`. In other databases, this is may be called a "BLOB" (Binary Large Object).
{% endhint %}

### Quick Reference

| ObjectBox Type | Description                                       |
| -------------- | ------------------------------------------------- |
| BoolVector     | Bool values (stored using one byte per value)     |
| ByteVector     | Byte values (8-bit integers), binary data, BLOB   |
| ShortVector    | Short values (16-bit integers)                    |
| CharVector     | Char values (16-bit characters)                   |
| IntVector      | Int values (32-bit integers)                      |
| LongVector     | Long values (64-bit integers)                     |
| FloatVector    | Float values (32-bit floating point)              |
| DoubleVector   | Double values (64-bit floating point)             |
| StringVector   | String values (UTF-8 encoded strings)             |
| DateVector     | Date values (64-bit timestamp)                    |
| DateNanoVector | DateNano values (high precision 64-bit timestamp) |

### Language Examples

{% tabs %}
{% tab title="Java" %}

```java
// Primitive arrays
public byte[] imageData;
public int[] scores;
public double[] measurements;

// String collections
public String[] tags;
public List<String> categories;
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
var imageData: ByteArray? = null
var scores: IntArray? = null
var measurements: DoubleArray? = null

var tags: Array<String>? = null
var categories: MutableList<String>? = null
```

{% endtab %}

{% tab title="Dart" %}

```dart
// Typed lists for efficiency
Uint8List? imageData;
Int32List? scores;
Float64List? measurements;

// String list
List<String>? tags;
```

{% endtab %}

{% tab title="Python" %}

```python
image_data = Int8Vector
scores = Int32Vector
measurements = Float64Vector
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
**For vector search:** Float vectors used for embeddings and similarity search have special indexing support. See [On-Device Vector Search](/on-device-vector-search).
{% endhint %}

## Flex Properties

Flex properties can hold data values of various types, including complex structures, in a single property. They serve two main purposes:

* Dynamic data: you want to process JSON-like data dynamically
* Nested objects: as an alternative to relations, you can embed map-like data directly

{% hint style="info" %}
When syncing with MongoDB, flex properties can hold nested documents.
{% endhint %}

{% tabs %}
{% tab title="Java" %}

```java
@Entity
public class Customer {
    @Id long id;

    // Stores any supported type at runtime
    @Nullable Object tag;

    // Or explicitly use a String map
    @Nullable Map<String, Object> stringMap;

    // Or a list
    @Nullable List<Object> flexList;
    
    public Customer(Object tag) {
        this.id = 0;
        this.tag = tag;
    }
    
    public Customer() {} // For ObjectBox
}

Customer customerStrTag = new Customer("string-tag");
Customer customerIntTag = new Customer(1234);
box.put(customerStrTag, customerIntTag);
```

See [FlexObjectConverter](https://objectbox.io/docfiles/java/current/io/objectbox/converter/FlexObjectConverter.html) for Java/Kotlin additional notes.
{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Entity
data class Customer(
    @Id var id: Long = 0,

    // Stores any supported type at runtime
    var tag: Any? = null,

    // Or explicitly use a String map
    var stringMap: MutableMap<String, Any?>? = null

    // Or a list
    var flexList: MutableList<Any?>? = null
)

val customerStrTag = Customer(tag = "string-tag")
val customerIntTag = Customer(tag = 1234)
box.put(customerStrTag, customerIntTag)
```

See [FlexObjectConverter](https://objectbox.io/docfiles/java/current/io/objectbox/converter/FlexObjectConverter.html) for Java/Kotlin additional notes.
{% endtab %}

{% tab title="Dart/Flutter" %}

```dart
@Entity()
class Customer {
  @Id()
  int id = 0;
  
  // Stores any supported type at runtime
  dynamic tag;
  
  // Or explicitly use a String map (JSON-like)
  Map<String, dynamic>? stringMap;
  
  // Or a list with mixed types
  List<dynamic>? flexList;
  
  // Or a list of maps
  List<Map<String, dynamic>>? nestedList;
}

final customerStrTag = Customer()..tag = "string-tag";
final customerIntTag = Customer()..tag = 1234;
box.putMany([customerStrTag, customerIntTag]);
```

{% endtab %}
{% endtabs %}

## Custom Types

If the existing property types do not fully cover your use case, check [custom types and converters](/advanced/custom-types).


# Android (Java/Kotlin)

ObjectBox is an Android database designed for Edge Computing and Offline First experiences. Here is the How-to and troubleshooting guides for using ObjectBox on Android with Java or Kotlin.

Learn all about Local Unit Tests, LiveData, Paging, and App Bundle. Please let us know, if our developer resources are helpful for you: The smiley at the bottom of the page already gives us an indication.


# Android Local Unit Tests

How to create ObjectBox local unit tests for Android projects.

## Android Local Unit Tests

ObjectBox supports local unit tests. This gives you the full ObjectBox functionality for running super fast test directly on your development machine.

On [Android, unit tests](https://developer.android.com/training/testing/unit-testing/index.html) can either run on an Android device (or emulator), so called instrumented tests, or they can run on your local development machine. Running local unit tests is typically much faster.

To learn how local unit tests for Android work in general have a look at the Android developers documentation on [Building Local Unit Tests](https://developer.android.com/training/testing/unit-testing/local-unit-tests.html). Read along to learn how to use ObjectBox in your local unit tests.

{% hint style="success" %}
This page also applies to writing unit tests for desktop projects.
{% endhint %}

## Set Up Your Testing Environment

{% hint style="info" %}
**The setup step is only required for ObjectBox 1.4 or older** (or if you want to manually add the dependencies)**.** In newer versions the ObjectBox plugin automatically adds the native ObjectBox library required for your current operating system.
{% endhint %}

Add the native ObjectBox library for your operating system to your existing test dependencies in your app’s `build.gradle` file:

```java
dependencies {
    // Required -- JUnit 4 framework
    testImplementation("junit:junit:4.12")
    // Optional -- manually add native ObjectBox library to override auto-detection
    testImplementation("io.objectbox:objectbox-linux:$objectboxVersion")
    testImplementation("io.objectbox:objectbox-macos:$objectboxVersion")
    testImplementation("io.objectbox:objectbox-windows:$objectboxVersion")
    // Not added automatically:
    // Since 2.9.0 we also provide ARM support for the Linux library
    testImplementation("io.objectbox:objectbox-linux-arm64:$objectboxVersion")       
    testImplementation("io.objectbox:objectbox-linux-armv7:$objectboxVersion")
}
```

{% hint style="info" %}
The ObjectBox native libraries currently only support 64-bit desktop operating systems.
{% endhint %}

{% hint style="info" %}
On Windows you might have to install the [Microsoft Visual C++ 2015 Redistributable (x64)](https://www.microsoft.com/en-US/download/details.aspx?id=48145) packages to use the native library.
{% endhint %}

## Create a Local Unit Test Class

You create your local unit test class as usual under `module-name/src/test/java/`. To use ObjectBox in your test methods you need to build a BoxStore instance using the generated MyObjectBox class of your project. You can use the [directory(File) method](http://objectbox.io/files/objectbox-java/current/io/objectbox/BoxStoreBuilder.html#directory-java.io.File-) on the BoxStore builder to ensure the test database is stored in a specific folder on your machine. To start with a clean database for each test you can delete the existing database using [BoxStore.deleteAllFiles(File)](http://objectbox.io/files/objectbox-java/current/io/objectbox/BoxStore.html#deleteAllFiles-java.io.File-).

The following example shows how you could implement a local unit test class that uses ObjectBox:

{% tabs %}
{% tab title="Java" %}

```java
public class NoteTest {
    
    private static final File TEST_DIRECTORY = new File("objectbox-example/test-db");
    private BoxStore store;
    
    @Before
    public void setUp() throws Exception {
        // Delete any files in the test directory before each test to start with a clean database.
        BoxStore.deleteAllFiles(TEST_DIRECTORY);
        store = MyObjectBox.builder()
                // Use a custom directory to store the database files in.
                .directory(TEST_DIRECTORY)
                // Optional: add debug flags for more detailed ObjectBox log output.
                .debugFlags(DebugFlags.LOG_QUERIES | DebugFlags.LOG_QUERY_PARAMETERS)
                .build();
    }
    
    @After
    public void tearDown() throws Exception {
        if (store != null) {
            store.close();
            store = null;
        }
        BoxStore.deleteAllFiles(TEST_DIRECTORY);
    }
    
    @Test
    public void exampleTest() {
        // get a box and use ObjectBox as usual
        Box<Note> noteBox = store.boxFor(Note.class);
        assertEquals(...);
    }
    
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
open class NoteTest {

    private var _store: BoxStore? = null
    protected val store: BoxStore
        get() = _store!!

    @Before
    fun setUp() {
        // Delete any files in the test directory before each test to start with a clean database.
        BoxStore.deleteAllFiles(TEST_DIRECTORY)
        _store = MyObjectBox.builder()
            // Use a custom directory to store the database files in.
            .directory(TEST_DIRECTORY)
            // Optional: add debug flags for more detailed ObjectBox log output.
            .debugFlags(DebugFlags.LOG_QUERIES or DebugFlags.LOG_QUERY_PARAMETERS)
            .build()
    }

    @After
    fun tearDown() {
        _store?.close()
        _store = null
        BoxStore.deleteAllFiles(TEST_DIRECTORY)
    }
    
    @Test
    fun exampleTest() {
        // Get a box and use ObjectBox as usual
        val noteBox = store.boxFor(Note::class.java)
        assertEquals(...)
    }

    companion object {
        private val TEST_DIRECTORY = File("objectbox-example/test-db")
    }
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
To help diagnose issues you can enable log output for ObjectBox actions, such as queries, by specifying one or more [debug flags](http://objectbox.io/files/objectbox-java/current/io/objectbox/BoxStoreBuilder.html#debugFlags-int-) when building BoxStore.
{% endhint %}

## Base class for tests

It’s usually a good idea to extract the setup and tear down methods into a base class for your tests. E.g.:

{% tabs %}
{% tab title="Java" %}

```java
public class AbstractObjectBoxTest {

    private static final File TEST_DIRECTORY = new File("objectbox-example/test-db");

    protected BoxStore store;

    @Before
    public void setUp() throws Exception {
        // Delete any files in the test directory before each test to start with a clean database.
        BoxStore.deleteAllFiles(TEST_DIRECTORY);
        store = MyObjectBox.builder()
                // Use a custom directory to store the database files in.
                .directory(TEST_DIRECTORY)
                // Optional: add debug flags for more detailed ObjectBox log output.
                .debugFlags(DebugFlags.LOG_QUERIES | DebugFlags.LOG_QUERY_PARAMETERS)
                .build();
    }

    @After
    public void tearDown() throws Exception {
        if (store != null) {
            store.close();
            store = null;
        }
        BoxStore.deleteAllFiles(TEST_DIRECTORY);
    }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
open class AbstractObjectBoxTest {

    private var _store: BoxStore? = null
    protected val store: BoxStore
        get() = _store!!

    @Before
    fun setUp() {
        // Delete any files in the test directory before each test to start with a clean database.
        BoxStore.deleteAllFiles(TEST_DIRECTORY)
        _store = MyObjectBox.builder()
            // Use a custom directory to store the database files in.
            .directory(TEST_DIRECTORY)
            // Optional: add debug flags for more detailed ObjectBox log output.
            .debugFlags(DebugFlags.LOG_QUERIES or DebugFlags.LOG_QUERY_PARAMETERS)
            .build()
    }

    @After
    fun tearDown() {
        _store?.close()
        _store = null
        BoxStore.deleteAllFiles(TEST_DIRECTORY)
    }

    companion object {
        private val TEST_DIRECTORY = File("objectbox-example/test-db")
    }
}
```

{% endtab %}
{% endtabs %}

## Testing Entities with Relations

{% hint style="info" %}
Kotlin desktop projects only. Since 3.0.0 this is no longer necessary for Android projects (either Kotlin or Java), initialization magic works for them now as well.
{% endhint %}

To test entities that have [relations](/relations), like **ToOne or ToMany** properties, on the local JVM you must initialize them and add a transient BoxStore field.

**See the** [**documentation about "initialization magic"**](/relations#initialization-magic) for an example and what to look out for.

Background: the "initialization magic" is normally done by the ObjectBox plugin using the Android Gradle Plugin Transform API or a Gradle task running after the Java compiler which allows to modify byte-code. However, this does currently not work for Kotlin code in Kotlin desktop projects.


# LiveData (Arch. Comp.)

LiveData is an observable data holder class. Learn to use ObjectBox database with LiveData from Android Architecture Components.

## ObjectBox - LiveData with Android Architecture Components

{% hint style="info" %}
Since 1.2.0. Have a look at [the example project on GitHub](https://github.com/objectbox/objectbox-examples/tree/master/android-app-arch).
{% endhint %}

As an alternative to ObjectBox’ [data observers and reactive queries](/data-observers-and-rx#objectbox-data-observers-and-reactive-extensions), you can opt for the [LiveData](https://developer.android.com/topic/libraries/architecture/livedata.html) approach supplied by Android Architecture Components. ObjectBox comes with `ObjectBoxLiveData`, a class that can be used inside your [ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel.html) classes.

A simple ViewModel implementation for our note example app includes the special `ObjectBoxLiveData` that is constructed using a regular [ObjectBox query](/queries):

```java
public class NoteViewModel extends ViewModel {
    
    private ObjectBoxLiveData<Note> noteLiveData;
    
    public ObjectBoxLiveData<Note> getNoteLiveData(Box<Note> notesBox) {
        if (noteLiveData == null) {
            // query all notes, sorted a-z by their text
            noteLiveData = new ObjectBoxLiveData<>(notesBox.query().order(Note_.text).build());
        }
        return noteLiveData;
    }
}
```

Note that we did choose to pass the box to `getNoteLiveData()` . Instead you could use `AndroidViewModel` , which provides access to the `Application` context, and then call `((App)getApplication()).getBoxStore().boxFor()` inside the ViewModel. However, the first approach has the advantage that our ViewModel has no reference to Android classes. This makes it easier to unit test.

Now, when creating the activity or fragment we get the ViewModel, access its LiveData and finally register to observe changes:

```java
NoteViewModel model = ViewModelProviders.of(this).get(NoteViewModel.class);
model.getNoteLiveData(notesBox).observe(this, new Observer<List<Note>>() {
    @Override
    public void onChanged(@Nullable List<Note>; notes) {
        notesAdapter.setNotes(notes);
    }
});
```

The ObjectBoxLiveData will now subscribe to the query and notify observers when the results of the query change, if there is at least one observer. In this example the activity is notified if a note is added or removed. If all observers are destroyed, the LiveData will cancel the subscription to the query.

If you have used [ObjectBox observers](/data-observers-and-rx#objectbox-data-observers-and-reactive-extensions) in the past this might sound familiar. Well, because it is! ObjectBoxLiveData just wraps a DataObserver on the query you give to it.


# Paging (Arch. Comp.)

The Android Paging Library helps you load and display small data chunks at a time. Learn to use ObjectBox database with the Paging library from Android Architecture Components.

{% hint style="info" %}
Since 2.0.0
{% endhint %}

ObjectBox supports integration with the [Paging library](https://developer.android.com/topic/libraries/architecture/paging/) that is part of Google's [Android Architecture Components](https://developer.android.com/topic/libraries/architecture/). To that end, the [ObjectBox Android library](/getting-started) (`objectbox-android`) provides the `ObjectBoxDataSource` class. It is an implementation of the Paging library's [`PositionalDataSource`](https://developer.android.com/reference/android/arch/paging/PositionalDataSource).

{% hint style="info" %}
Note: the following assumes that you have already [added and set up the Paging library](https://developer.android.com/topic/libraries/architecture/paging/) in your project.
{% endhint %}

### Using ObjectBoxDataSource

Within your `ViewModel`, similar to [creating a `LiveData` directly](/android/livedata-architecture-components), you first [build your ObjectBox query](/queries). But then, you construct an `ObjectBoxDataSource` factory with it instead. This factory is then passed to a `LivePagedListBuilder` to build the actual `LiveData`.

Here is an example of a `ViewModel` class doing just that:

```java
public class NotePagedViewModel extends ViewModel {
    
    private LiveData<PagedList<Note>> noteLiveDataPaged;
    
    public LiveData<PagedList<Note>> getNoteLiveDataPaged(Box<Note> notesBox) {
        if (noteLiveDataPaged == null) {
            // query all notes, sorted a-z by their text
            Query<Note> query = notesBox.query().order(Note_.text).build();
            // build LiveData
            noteLiveDataPaged = new LivePagedListBuilder<>(
                    new ObjectBoxDataSource.Factory<>(query),
                    20 /* page size */
            ).build();
        }
        return noteLiveDataPaged;
    }
}
```

Note that the `LiveData` holds your entity class, here `Note`, wrapped inside a `PagedList`. You observe the `LiveData` as usual in your activity or fragment, then submit the `PagedList` on changes to your `PagedListAdapter` of the Paging library.

We will not duplicate how this works here, see the [Paging library documentation](https://developer.android.com/topic/libraries/architecture/paging/) for details about this.

### Next steps

* Have a look at the [ObjectBox Architecture Components example code](https://github.com/objectbox/objectbox-examples/tree/master/android-app-arch).
* Check out [ObjectBox support for LiveData](/android/livedata-architecture-components).
* Learn how to [build queries](/queries).


# App Bundle, split APKs and LinkageError

Troubleshoot or avoid crashes when using ObjectBox db and Android App Bundle or due to buggy devices. Google-certified devices prevent this crash.

Your app might observe crashes due to `UnsatisfiedLinkError` or (since ObjectBox 2.3.4) `LinkageError` on some devices. This has mainly two reasons:

* If your app uses the [App Bundle](https://developer.android.com/guide/app-bundle) format, the legacy split APK feature or [Multidex](https://developer.android.com/studio/build/multidex) the native library can't be found.
* Or if your app's minimum SDK level is below API 23 (Marshmallow), there are known bugs in Android's native library loading code.

Let us know if the below suggestions do not resolve your crashes in [GitHub issue 605](https://github.com/objectbox/objectbox-java/issues/605).

## App Bundle and split APKs

When using an App Bundle or split APKs Google Play only delivers the split APKs required for each user's device configuration, including its architecture (ABI). If users bypass Google Play to install your app (**"sideloading"**) they might not install all of the required split APKs. If the split APK containing the ObjectBox native library required for the device ABI is missing, your app will crash with `LinkageError` when building BoxStore.

### Using the Play Core library to check for missing splits

{% hint style="info" %}
**Update August 2020:** Google-certified devices (those running Google Play Services) or those running Android 10 (API level 29) or higher prevent users from sideloading split APKs, preventing this crash. Adding the below check is no longer necessary for these devices.
{% endhint %}

Add the [Play Core library](https://developer.android.com/guide/playcore) to the dependencies block:

```java
implementation 'com.google.android.play:core:1.7.3'
```

In the `Application` class add the missing split APKs check before calling `super.onCreate()`:

```java
public class App extends Application {

    @Override
    public void onCreate() {
        if (MissingSplitsManagerFactory.create(this).disableAppIfMissingRequiredSplits()) {
            return; // Skip app initialization.
        }
        super.onCreate();
        // ...
    }
}
```

If a broken installation is detected, users will see a message and Reinstall button asking them to re-install the app from Google Play.

See [how we updated our example app](https://github.com/objectbox/objectbox-examples/pull/53) to use the Play Core library detection.

If the Play Core library should not be used, there are two alternatives:

### Alternative: Catch exception and inform users

You can guard the MyObjectBox build call and for example display an activity with an info message (e.g. direct users to reinstall the app from Google Play, send you an error report, ...):

```java
// guard the build call and set some flag (here setting the boxStore field null)
try {
    boxStore = MyObjectBox.builder()
            .androidContext(context.getApplicationContext())
            .build();
} catch (LinkageError e) {
    boxStore = null;
    Log.e(App.TAG, "Failed to load ObjectBox: " + e.getMessage());
}


// then for example in the main activity check the flag in onCreate and 
// direct to an info/error message without the app crashing:
if (ObjectBox.get() == null) {
    startActivity(new Intent(this, ErrorActivity.class));
    finish();
    return;
}
```

As an example see [how we added this to our Android app example](https://github.com/objectbox/objectbox-examples/commit/e3af09d91181eea11db8959ceddf360f7b35a602).

### Alternative: turn off splitting by ABI

The simplest solution is to always include native libraries for all supported ABIs. However, this will increase the download size of your app for all users.

```
android {
    bundle {
        abi {
            // This property is set to true by default.
            enableSplit = false
        }
    }
}
```

Source: [Android Developers](https://developer.android.com/studio/projects/dynamic-delivery/configure-base#disable_config_apks)

## Buggy devices (API 22 or lower)

On some devices and if your **minimum SDK is below API 23** (Android 6.0 Marshmallow), loading the native library may fail with `LinkageError` due to known bugs in Android's native library loading code. To counter this ObjectBox includes support for the [ReLinker](https://github.com/KeepSafe/ReLinker) tool which will try to extract the native library manually if loading it normally fails.

To enable this, just add ReLinker to your dependencies:

```
// https://github.com/KeepSafe/ReLinker/releases
implementation 'com.getkeepsafe.relinker:relinker:1.4.1'
```

{% hint style="warning" %}
ObjectBox is calling ReLinker via reflection. If you are using ProGuard or Multidex, make sure to add keep rules so that ReLinker code is not stripped from the final app or is not in the primary dex file.
{% endhint %}

For ProGuard add this line:

```
-keep class com.getkeepsafe.relinker.** { *; }
```

For Multidex add a multiDexKeepProguard file to your build file:

```
android {
    buildTypes {
        release {
            multiDexKeepProguard file('multidex-config.pro')
        }
    }
}
```

And in the multidex-config.pro file add the same rule as above:

```
-keep class com.getkeepsafe.relinker.** { *; }
```

{% hint style="info" %}
[Multidex supports two file formats to keep files](https://developer.android.com/studio/build/multidex#keep). We are using the ProGuard format (multiDexKeepProguard property). You can also use the multiDexKeepFile property, but make sure to adapt the rule above to that format.
{% endhint %}

### Enable ReLinker debug log

To enable debug logs for ReLinker you can pass a custom `ReLinkerInstance` when building `BoxStore`:

```java
boxStore = MyObjectBox.builder()
    .androidContext(App.this)
    .androidReLinker(ReLinker.log(new ReLinker.Logger() {
        @Override
        public void log(String message) { Log.d(TAG, message); }
    }))
    .build();
```


# greenDAO Compat

Use existing greenDAO code with ObjectBox and migrate from greenDAO to ObjectBox.

Do you have an existing app that already uses the greenDAO library? We created DaoCompat as a compatibility layer that gives you a greenDAO like API, but behind the scenes uses ObjectBox to store your app's data.

Check out the [DaoCompat documentation](https://greenrobot.org/greendao/documentation/objectbox-compat/) on how to easily migrate your app to ObjectBox. There is also an [example app](https://github.com/objectbox/objectbox-examples/tree/main/android-app-daocompat) available.

Feel free to [ask for help on Stack Overflow](https://stackoverflow.com/questions/tagged/objectbox), or if you encounter a bug with DaoCompat [create an issue](https://github.com/objectbox/objectbox-java/issues).


# Desktop Apps

Besides Android apps, ObjectBox for Java supports desktop apps running on Linux, macOS and Windows written in Java or Kotlin. See how to build and test desktop apps using ObjectBox.

## ObjectBox – Embedded Database for Java Desktop Apps

Just like on Android, ObjectBox stands for a super simple API and high performance. It’s designed for objects and outperforms other database and ORM solutions. Because it is an embedded database, ObjectBox runs in your apps’ process and needs no maintenance. Read on to learn how to create a Java project using ObjectBox. We believe it’s fairly easy. Please let us know your thoughts on it.

## Setup and Usage

See the Getting Started page on how to set up your project, add entities and use the ObjectBox APIs.

{% content-ref url="/pages/-LEUEB3nMDmS4vcFKZju" %}
[Getting Started](/getting-started)
{% endcontent-ref %}

## Examples

There are example command line apps available in [our examples repository](https://github.com/objectbox/objectbox-examples).

## Building Unit Tests

The setup and writing tests is identical to writing unit tests that run on the local JVM for Android, see [Android Local Unit Tests](/android/android-local-unit-tests).


# Kotlin Support

ObjectBox fully supports Kotlin for Android. Learn what to look out for when using ObjectBox with Kotlin, how to use the built-in Kotlin extension functions.

## ObjectBox and Kotlin

ObjectBox comes with full Kotlin support for Android. This allows entities to be modeled in Kotlin classes (regular and data classes). With Kotlin support you can build faster apps even faster.

{% hint style="info" %}
This page assumes that you have added ObjectBox to your project and that you are familiar with basic functionality. The Getting Started page will help you out if you are not. This page discusses additional capabilities for Kotlin only.
{% endhint %}

{% content-ref url="/pages/-LEUEB3nMDmS4vcFKZju" %}
[Getting Started](/getting-started)
{% endcontent-ref %}

## Kotlin Entities

ObjectBox supports **regular and data classes for entities**. However, **`@Id` properties must be var** (not val) because ObjectBox assigns the ID after putting a new entity. They also should be of non-null type `Long` with the special value of zero for marking entities as new.

Can **sealed classes** be entities? Not directly. [Sealed classes](https://kotlinlang.org/docs/reference/sealed-classes.html) are abstract and can't be instantiated. But subclasses of a sealed class should work.

To learn how to create entities, look at these pages:

{% content-ref url="/pages/-LEUEB3nMDmS4vcFKZju" %}
[Getting Started](/getting-started)
{% endcontent-ref %}

{% content-ref url="/pages/-LEUYsjP1BaBoJ\_OPUD3" %}
[Entity Annotations](/entity-annotations)
{% endcontent-ref %}

## Defining Relations in Kotlin Entities

When [defining relations](/relations) in Kotlin, keep in mind that **relation properties must be `var`**. Otherwise they can not be initialized as described in the [relations docs](/relations#initialization-magic). To avoid null checks use a lateinit modifier. When using a data class this requires the relation property to be moved to the body.

{% hint style="warning" %}
For non-Android projects, i.e. if you are using Kotlin for desktop apps, there's an additional setup for for entities necessary, please see <https://docs.objectbox.io/relations#initialization-magic> for details. In the future, we hope to eliminate this requirement.
{% endhint %}

See the Relations page for examples.

{% content-ref url="/pages/-LEV5ggHPqeHx1ezvWSX" %}
[Relations](/relations)
{% endcontent-ref %}

{% hint style="info" %}
Two data classes that have the same property values (excluding those defined in the class body) [are equal and have the same hash code](https://kotlinlang.org/docs/reference/data-classes.html). Keep this in mind when working with ToMany which uses a HashMap to keep track of changes. E.g. adding the same data class multiple times has no effect, it is treated as the same entity.
{% endhint %}

## Using the provided extension functions

To simplify your code, you might want to use the Kotlin extension functions provided by ObjectBox. The library containing them is added automatically if the Gradle plugin detects a Kotlin project.

To add it manually, modify the dependencies section in your app's `build.gradle` file:

```java
dependencies {
    implementation("io.objectbox:objectbox-kotlin:$objectboxVersion")
}
```

Now have a look at what is possible with the extensions compared to standard Kotlin idioms:

Get a box:

```kotlin
// Regular:
val box = store.boxFor(DataClassEntity::class.java)

// With extension:
val box: Box<DataClassEntity> = store.boxFor()
```

### Queries

{% hint style="info" %}
The [new Query API](https://docs.objectbox.io/pages/-LEUom1xpQh_31VZC80C#new-query-api-java-kotlin-3.0) makes below extensions functions unnecessary.
{% endhint %}

Build a query:

```kotlin
// Regular:
val query = box.query().run {
    equal(property, value)
    order(property)
    build()
}

// With extension:
val query = box.query {
    equal(property, value)
    order(property)
}
```

Use the in filter of a query:

```kotlin
// Regular:
val query = box.query().`in`(property, array).build()

// With extension:
val query = box.query().inValues(property, array).build()
```

### Relations

Modify a [ToMany](/relations#to-many-relations):

```kotlin
// Regular:
toMany.apply { 
    reset()
    add(entity)
    removeById(id)
    applyChangesToDb()
}

// With extension:
toMany.applyChangesToDb(resetFirst = true) { // default is false
    add(entity)
    removeById(id)
}
```

### Flow

Get a Flow from a Box or Query subscription (behind the scenes this is based on a [Data Observer](/data-observers-and-rx)):

```kotlin
// Listen to all changes to a Box
val flow = store.subscribe(TestEntity::class.java).toFlow()
// Get the latest query results on any changes to a Box
val flow = box.query().subscribe().toFlow()
```

Something missing? [Let us know](https://github.com/objectbox/objectbox-java/issues/446) what other extension functions you want us to add.

## Coroutines

To run Box operations on a separate Dispatcher wrap them using `withContext`:

```kotlin
suspend fun putNote(
    note: Note, 
    dispatcher: CoroutineDispatcher
) = withContext(dispatcher) {
    boxStore.boxFor(Note::class.java).put(note)
}
```

BoxStore provides an async API to run transactions. There is an extension function available that wraps it in a coroutine:

```kotlin
// Calls callInTxAsync behind the scenes.
val id = boxStore.awaitCallInTx {
    box.put(Note("Hello", 1))
}
```

## Next Steps

* Check out the [Kotlin example on GitHub](https://github.com/objectbox/objectbox-examples/tree/master/android-app-kotlin).
* Continue with [Getting Started](/getting-started).


# ObjectBox Queries

Discover how to use the Query API to create queries with ObjectBox DB. By utilizing these queries, you can retrieve stored objects that meet user-defined criteria.

## Build a query

Use `box.query(condition)` and supply a `condition` on one or more properties to start building a query.

Create a `condition` by accessing a property via the underscore class of the entity, e.g. `User_.firstName.equal("Joe")`.

Use `build()` to create a re-usable query instance. To then retrieve all results use `find()` on the query. More options on retrieving results are discussed later in [#run-a-query](#run-a-query "mention").

Once done, `close()` the query to clean up resources.

Here is a full example to query for all users with the first name “Joe”:

{% tabs %}
{% tab title="Java" %}

```java
Query<User> query = userBox.query(User_.firstName.equal("Joe")).build();
List<User> joes = query.find();
query.close();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val query = userBox.query(User_.firstName.equal("Joe")).build()
val joes = query.find()
query.close()
```

{% endtab %}

{% tab title="Dart" %}

```dart
Query<User> query = userBox.query(User_.firstName.equals('Joe')).build();
List<User> joes = query.find();
query.close();
```

{% endtab %}

{% tab title="Python" %}

```python
query = userBox.query(User.firstName.equals("Joe")).build()
joes = query.find()
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
const query = userBox.query(User_.firstName.equals("Joe")).build();
const joes = query.find();
query.close();
```

{% endtab %}
{% endtabs %}

**To combine multiple conditions** use `and(condition)` and `or(condition)`. This implicitly adds parentheses around the combined conditions, e.g. `cond1.and(cond2)` is logically equivalent to `(cond1 AND cond2)`.

For example to get users with the first name “Joe” that are born later than 2015 and whose last name starts with “O”:

{% tabs %}
{% tab title="Java" %}

```java
Query<User> query = userBox.query(
        User_.firstName.equal("Joe")
                .and(User_.yearOfBirth.greater(2015))
                .and(User_.lastName.startsWith("O"))
).build();
List<User> youngJoes = query.find();
query.close();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val query = userBox.query(
    User_.firstName equal "Joe" and
            (User_.yearOfBirth greater 2015) and
            (User_.lastName startsWith "O")
).build()
val youngJoes = query.find()
query.close()
```

{% endtab %}

{% tab title="Dart" %}

```dart
Query<User> query = userBox
    .query(User_.firstName.equals('Joe')
        .and(User_.yearOfBirth.greaterThan(2015))
        .and(User_.lastName.startsWith('O')))
    .build();

// or use operator overloads:
Query<User> query2 = userBox
    .query(User_.firstName.equals('Joe') &
        User_.yearOfBirth.greaterThan(2015) &
        User_.lastName.startsWith('O'))
    .build();
```

{% endtab %}

{% tab title="Python" %}

```python
query = userBox.query( 
  User.firstName.equals("Joe") & 
  User.yearOfBirth.greater_than(2015) & 
  User.lastName.starts_with('O')
).build()
joes = query.find()
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
const query = userBox.query(
    User_.firstName.equals("Joe")
        .and(User_.yearOfBirth.greaterThan(2015))
        .and(User_.lastName.startsWith("O"))
).build();
const youngJoes = query.find();
query.close();
```

{% endtab %}
{% endtabs %}

This also makes it possible to **make a condition optional:**

{% tabs %}
{% tab title="Java" %}

```java
QueryCondition<User> conditions = User_.firstName.equal("Joe");
if (onlyYoungJoes) {
    conditions = conditions.and(User_.yearOfBirth.greater(2015));
}
Query<User> query = userBox.query(conditions).build();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
var conditions: QueryCondition<User> = User_.firstName equal "Joe"
if (onlyYoungJoes) {
    conditions = conditions and (User_.yearOfBirth greater 2015)
}
val query = userBox.query(conditions).build()
```

{% endtab %}

{% tab title="Dart" %}

```dart
var conditions = User_.firstName.equals('Joe');
if (onlyYoungJoes) {
  conditions = conditions & User_.yearOfBirth.greaterThan(2015);
}
Query<User> query = userBox.query(conditions).build();
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
let conditions = User_.firstName.equals("Joe");
if (onlyYoungJoes) {
    conditions = conditions.and(User_.yearOfBirth.greaterThan(2015));
}
const query = userBox.query(conditions).build();
```

{% endtab %}
{% endtabs %}

**To nest conditions** pass a combined condition to `and()` or `or()`:

{% tabs %}
{% tab title="Java" %}

```java
// equal AND (less OR oneOf)
Query<User> query = box.query(
        User_.firstName.equal("Joe")
                .and(User_.age.less(12)
                        .or(User_.stamp.oneOf(new long[]{1012}))))
        .build();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// equal AND (less OR oneOf)
val query = box.query(
        User_.firstName equal "Joe" and
                (User_.age less 12 or 
                        (User_.stamp oneOf longArrayOf(1012))))
        .build()        
```

{% endtab %}

{% tab title="Dart" %}

```dart
Query<User> query = box.query(
    User_.firstName.equals('Joe')
        .and(User_.age.lessThan(12)
        .or(User_.stamp.oneOf([1012]))))
    .build();
```

{% endtab %}

{% tab title="Python" %}

```python
query = userBox.query(
        (User.firstName.equals("Joe")
            & User.yearOfBirth.greater_than(1970)) | 
        User.lastName.starts_with('O')
    ).build()
joes = query.find()
```

{% hint style="info" %}
one\_of is not yet available in Python.
{% endhint %}
{% endtab %}

{% tab title="TypeScript" %}

```typescript
// equals AND (lessThan OR greaterThan)
const query = box.query(
    User_.firstName.equals("Joe")
        .and(User_.age.lessThan(12)
            .or(User_.age.greaterThan(60)))
).build();
```

{% endtab %}
{% endtabs %}

#### Other notable features

* In Kotlin, instead of `condition1.and(condition2)` you can write ` condition1`` `` `**`and`**` `` ``condition2 ` (similarly ` condition1`` `` `**`or`**` `` ``condition2 `).
* In Dart and Python, instead of `condition1.and(condition2)` you can write ` condition1`` `` `**`&`**` `` ``condition2 ` (similarly ` condition1`` `` `**`|`**` `` ``conditon2 `).
* Use `condition.alias(aliasName)` to set an alias for a `condition` that can later be used to change the parameter value of the condition on the built query.

{% hint style="info" %}
**TypeScript:** String conditions are **case-sensitive by default**. Pass `false` as the second argument for case-insensitive matching: `User_.name.equals("joe", false)`.
{% endhint %}

### Common conditions

Apart from the standard conditions like `equal()`, `notEqual()`, `greater()` and `less()` there are also additional conditions available:

* `isNull()` and `notNull()`,
* `between()` to filter for values that are between the given two,
* `oneOf()` and `notOneOf()` to filter for values that match any in the given array,
* `startsWith()`, `endsWith()` and `contains()` for extended String filtering.

See the API for a full list:

* Property conditions: [Java](https://objectbox.io/docfiles/java/current/io/objectbox/Property.html) and [Dart](https://pub.dev/documentation/objectbox/latest/objectbox/QueryProperty-class.html)
* Relation conditions: [Java](https://objectbox.io/docfiles/java/current/io/objectbox/relation/RelationInfo.html)

{% hint style="info" %}
**Dart only: DateTime caveat**

For Dart `DateTime` is stored as time in milliseconds internally in the database (or nanoseconds for `@Property(type: PropertyType.dateNano)`).

To avoid having to manually convert to `int` when creating a query condition, extra methods that accept `DateTime` exist.

For example, to query an `Order` entity with a `date` field to find all orders in 2023:

```dart
final query = box
    .query(Order_.date.betweenDate(DateTime.utc(2023),
        DateTime.utc(2024).subtract(Duration(milliseconds: 1))))
    .build();
```

{% endhint %}

### Nearest neighbor vector search

A special condition is available for vector properties with an HNSW index. See the dedicated page for details:

{% content-ref url="/pages/OtC8AlACVJfXcCtFY9sC" %}
[On-Device Vector Search](/on-device-vector-search)
{% endcontent-ref %}

### Order results

In addition to specifying conditions, you can order the returned results using the `order()` method. By default this sorts ASCII characters in alphabetical order while ignoring case and numbers in ascending order.

{% tabs %}
{% tab title="Java" %}

```java
Query<User> query = userBox
    .query(User_.firstName.equal("Joe"))
    .order(User_.lastName) // in ascending order, ignoring case
    .build();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val query = userBox
    .query(User_.firstName.equal("Joe"))
    .order(User_.lastName) // in ascending order, ignoring case
    .build()
```

{% endtab %}

{% tab title="Dart" %}

```dart
// in ascending order, ignoring case
final qBuilder = box.query(User_.firstName.equals('Joe')).order(User_.lastName);
final query = qBuilder.build();
```

{% endtab %}

{% tab title="Python" %}
{% hint style="info" %}
Order results feature is not yet available in Python.
{% endhint %}
{% endtab %}

{% tab title="TypeScript" %}

```typescript
// in ascending order
const query = userBox
    .query(User_.firstName.equals("Joe"))
    .order(User_.lastName)
    .build();
```

{% endtab %}
{% endtabs %}

You can also pass flags to `order()` to sort in descending order, to sort case sensitive or to specially treat null values. For example to sort the above results in descending order and case sensitive instead:

{% tabs %}
{% tab title="Java" %}

```java
.order(User_.lastName, QueryBuilder.DESCENDING | QueryBuilder.CASE_SENSITIVE)
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
.order(User_.lastName, QueryBuilder.DESCENDING or QueryBuilder.CASE_SENSITIVE)
```

{% endtab %}

{% tab title="Dart" %}

```dart
.order(User_.lastName, flags: Order.descending | Order.caseSensitive)
```

{% endtab %}

{% tab title="Python" %}
{% hint style="info" %}
Order results feature is not yet available in Python.
{% endhint %}
{% endtab %}

{% tab title="TypeScript" %}

```typescript
import { OrderFlags } from "objectbox";
// ...
.order(User_.lastName, OrderFlags.DESCENDING | OrderFlags.CASE_SENSITIVE)
```

{% endtab %}
{% endtabs %}

Order directives can also be chained. Check the method documentation ([Java](https://objectbox.io/files/objectbox-java/current/io/objectbox/query/QueryBuilder.html#order\(io.objectbox.Property,int\))) for details.

## Run a query

[Queries](/queries) are first created (and not yet executed) by calling `build()` on the `QueryBuilder`.

```java
Query<User> query = builder.build();
```

Once the query is created, it allows various operations, which we will explore in the following sub sections.

### Find objects

There are a couple of find methods to retrieve objects matching the query:

{% tabs %}
{% tab title="Java" %}

```java
// return all entities matching the query
List<User> joes = query.find();

// return only the first result or null if none
User joe = query.findFirst();

// return the only result or null if none, throw if more than one result
User joe = query.findUnique();
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
// return all entities matching the query
const joes = query.find();

// return only the first result or null if none
const joe = query.findFirst();

// return the only result or null if none, throw if more than one result
const uniqueJoe = query.findUnique();

// return only the IDs of matching entities
const ids: bigint[] = query.findIds();

// return the count of matching entities
const count: number = query.count();
```

{% endtab %}
{% endtabs %}

To return all entities matching the query simply call `find()`.

To only return the first result, use `findFirst()`.

If you expect a unique result, call `findUnique()` instead. It will give you a single result or null, if no matching entity was found and throw an exception if there was more than one result.

### Remove objects

To remove all objects matching a query, call `query.remove()`.

{% tabs %}
{% tab title="Java" %}

```java
long removedCount = query.remove();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val removedCount = query.remove()
```

{% endtab %}

{% tab title="Dart" %}

```dart
int removedCount = query.remove();
```

{% endtab %}

{% tab title="Python" %}

```python
removed_count = query.remove()
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
const removedCount: number = query.remove();
```

{% endtab %}
{% endtabs %}

### Reuse Queries and Parameters

If you frequently run the same query you should cache the `Query` object and re-use it. To make a `Query` more reusable you can change the values, or query parameters, of each condition you added even after the `Query` is built. Let's see how.

{% hint style="info" %}
Query is not thread safe. To use a query in a different thread, either build a new query or synchronize access to it. Alternatively, in Java use `query.copy()` or a `QueryThreadLocal` to obtain an instance for each thread.
{% endhint %}

Assume we want to find a list of `User` with specific `firstName` values. First, we build a regular `Query` with an `equal()` condition for `firstName`. Because we have to pass an initial parameter value to `equal()` but plan to override it before running the `Query` later, we just pass an empty string:

{% tabs %}
{% tab title="Java" %}

```java
// build a query
Query<User> query = userBox.query(User_.firstName.equal("")).build();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// build a query
val query = userBox.query(User_.firstName.equal("")).build()
```

{% endtab %}

{% tab title="Dart" %}

```dart
// build a query
final query = userBox.query(User_.firstName.equals('')).build();
```

{% endtab %}

{% tab title="Python" %}

```python
# build a query
query = userBox.query(User.firstName.equals('')).build();
```

{% endtab %}

{% tab title="TypeScript" %}
{% hint style="info" %}
Query parameter reuse via `setParameter()` is not yet available in TypeScript. Rebuild the query with new values instead.
{% endhint %}
{% endtab %}
{% endtabs %}

Now at some later point, we want to run the `Query` with an actual value for the `equals` condition on`firstName` :

{% tabs %}
{% tab title="Java" %}

```java
// Change firstName parameter to "Joe" and get results
List<User> joes = query.setParameter(User_.firstName, "Joe").find();

// Change firstName parameter to "Jake" and get results
List<User> jakes = query.setParameter(User_.firstName, "Jake").find();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// Change firstName parameter to "Joe" and get results
val joes = query.setParameter(User_.firstName, "Joe").find()

// Change firstName parameter to "Jake" and get results
val jakes = query.setParameter(User_.firstName, "Jake").find()
```

{% endtab %}

{% tab title="Dart" %}

```dart
// Change firstName parameter to "Joe" and get results
query.param(User_.firstName).value = 'Joe';
final joes = query.find();

// Change firstName parameter to "Jake" and get results
final jakes = (query..param(User_.firstName).value = 'Jake').find();
```

{% endtab %}

{% tab title="Python" %}

```python
# Change firstName parameter to "Joe" and get results
joes = query.set_parameter_string(User.firstName, "Joe").find()

# Change firstName parameter to "Jake" and get results
jakes = query.set_parameter_srting(User.firstName, "Jake").find()
```

{% endtab %}
{% endtabs %}

You might already be wondering what happens if you have more than one condition using `firstName`? For this purpose you can **assign each parameter an alias** while specifying the condition:

{% tabs %}
{% tab title="Java" %}

```java
// assign alias "name" to the equal query parameter
Query<User> query = userBox
    .query(User_.firstName.equal("").alias("name"));
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// assign alias "name" to the equal query parameter
val query = userBox
    .query(User_.firstName.equal("").alias("name"))
```

{% endtab %}

{% tab title="Dart" %}

```dart
// assign alias "name" to the equals query parameter
final query = userBox.query(User_.firstName.equals('', alias: 'name')).build();
```

{% endtab %}

{% tab title="Python" %}

```python
# Assign alias "name" to the equals query parameter
query = userBox.query(User.firstName.equals('').alias("name")).build();
```

{% endtab %}
{% endtabs %}

Then, when setting a new parameter value pass the alias instead of the property:

{% tabs %}
{% tab title="Java" %}

```java
// Change parameter with alias "name" to "Joe", get results
List<User> joes = query.setParameter("name", "Joe").find();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// Change parameter with alias "name" to "Joe" and get results
val joes = query.setParameter("name", "Joe").find()
```

{% endtab %}

{% tab title="Dart" %}

```dart
// Change parameter with alias "name" to "Joe" and get results
final joes = (query..param(User_.firstName, alias: 'name').value = 'Joe').find();
```

{% endtab %}

{% tab title="Python" %}

```python
# Change parameter with alias "name" to "Joe" and get results
joes = query.set_parameter_alias_string("name", "Joe").find()
```

{% endtab %}
{% endtabs %}

### Limit, Offset, and Pagination

Sometimes you only need a subset of a query, for example, the first 10 elements to display in your user interface. This is especially helpful (and resource-efficient) when you have a high number of entities and you cannot limit the result using query conditions only.

{% tabs %}
{% tab title="Java" %}

```java
// offset by 10, limit to at most 5 results
List<User> joes = query.find(10, 5);
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// offset by 10, limit to at most 5 results
val joes = query.find(10, 5)
```

{% endtab %}

{% tab title="Dart" %}

```dart
// offset by 10, limit to at most 5 results
query
  ..offset = 10
  ..limit = 5;
List<User> joes = query.find();
```

{% endtab %}

{% tab title="Python" %}

<pre class="language-python"><code class="lang-python"># Offset by 10, limit to at most 5 results
<strong>joes = query \
</strong><strong>    .offset(10)
</strong><strong>    .limit(5)
</strong><strong>    .find()
</strong></code></pre>

{% endtab %}

{% tab title="TypeScript" %}

```typescript
// offset by 10, limit to at most 5 results
const joes = query.offset(10).limit(5).find();
```

{% endtab %}
{% endtabs %}

`offset:` The first `offset` results are skipped.

`limit:` At most `limit` results are returned.

### Lazy-load results (Java)

{% hint style="info" %}
Only Java/Kotlin
{% endhint %}

To avoid loading query results right away, Query offers `findLazy()` and `findLazyCached()` which return a [`LazyList`](https://objectbox.io/files/objectbox-java/current/io/objectbox/query/LazyList.html) of the query results.

[LazyList](https://objectbox.io/files/objectbox-java/current/io/objectbox/query/LazyList.html) is a thread-safe, unmodifiable list that reads entities lazily only once they are accessed. Depending on the find method called, the lazy list will be cached or not. Cached lazy lists store the previously accessed objects to avoid loading entities more than once. Some features of the list are limited to cached lists (e.g. features that require the entire list). See the [LazyList class documentation](https://objectbox.io/files/objectbox-java/current/io/objectbox/query/LazyList.html) for more details.

### Stream results (Dart)

{% hint style="info" %}
Only Dart
{% endhint %}

Instead of reading the whole result (list of objects) using `find()` you can stream it using `stream()` :

{% tabs %}
{% tab title="Dart" %}

```dart
Query<User> query = userBox.query().build();
Stream<User stream = query.stream();
await stream.forEach((User user) => print(user));
query.close();
```

{% endtab %}
{% endtabs %}

### Observe or listen to changes

To learn how to observe or listen to changes to the results of a query, see the data observers page:

{% content-ref url="/pages/-LEUvjeTbF5f5toRc\_dW" %}
[Data Observers & Rx](/data-observers-and-rx)
{% endcontent-ref %}

## Query a single property

If you only want to return the values of a particular property and not a list of full objects you can use a [PropertyQuery](https://objectbox.io/files/objectbox-java/current/io/objectbox/query/PropertyQuery.html). After building a query, simply call `property(Property)` to define the property followed by the appropriate find method.

For example, instead of getting all `User`s, to just get their email addresses:

{% tabs %}
{% tab title="Java" %}

```java
String[] emails = userBox.query().build()
    .property(User_.email)
    .findStrings();
    
// or use .findString() to return just the first result
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val emails = userBox.query().build()
    .property(User_.email)
    .findStrings()
    
// or use .findString() to return just the first result
```

{% endtab %}

{% tab title="Dart" %}

```dart
final query = userBox.query().build();
List<String> emails = query.property(User_.email).find();
query.close();
```

{% endtab %}

{% tab title="TypeScript" %}
{% hint style="info" %}
Property queries are not yet available in TypeScript. Use `find()` and map the results instead:

```typescript
const emails = query.find().map(user => user.email);
```

{% endhint %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
Note: the returned array of property values is **not in any particular order**, even if you did specify an order when building the query.
{% endhint %}

### Handle null values

**By default, null values are not returned.** However, you can specify a replacement value to return if a property is null:

{% tabs %}
{% tab title="Java" %}

```java
// includes 'unknown' for each null email
String[] emails = userBox.query().build()
    .property(User_.email)
    .nullValue("unknown")
    .findStrings();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// includes 'unknown' for each null email
val emails = userBox.query().build()
    .property(User_.email)
    .nullValue("unknown")
    .findStrings()
```

{% endtab %}

{% tab title="Dart" %}

```dart
final query = userBox.query().build();
// includes 'unknown' for each null email
List<String> emails = query.property(User_.email).find(replaceNullWith: 'unknown');
query.close();
```

{% endtab %}
{% endtabs %}

### Distinct and unique results

The property query can also only return distinct values:

{% tabs %}
{% tab title="Java" %}

```java
PropertyQuery pq = userBox.query().build().property(User_.firstName);

// returns ['joe'] because by default, the case of strings is ignored.
String[] names = pq.distinct().findStrings();

// returns ['Joe', 'joe', 'JOE']
String[] names = pq.distinct(StringOrder.CASE_SENSITIVE).findStrings();

// the query can be configured to throw there is more than one value
String[] names = pq.unique().findStrings();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val pq = userBox.query().build().property(User_.firstName)

// returns ['joe'] because by default, the case of strings is ignored.
val names = pq.distinct().findStrings()

// returns ['Joe', 'joe', 'JOE']
val names = pq.distinct(StringOrder.CASE_SENSITIVE).findStrings()

// the query can be configured to throw there is more than one value
val names = pq.unique().findStrings()
```

{% endtab %}

{% tab title="Dart" %}

```dart
final query = userBox.query().build();
PropertyQuery<String> pq = query.property(User_.firstName);
pq.distinct = true;

// returns ['Joe', 'joe', 'JOE'] 
List<String> names = pq.find();

// returns ['joe']
pq.caseSensitive = false;
List<String> names = pq.find(); 
query.close();
```

{% endtab %}
{% endtabs %}

### Aggregate values

Property queries ([JavaDoc](https://objectbox.io/files/objectbox-java/current/io/objectbox/query/PropertyQuery.html) and [Dart API docs](https://pub.dev/documentation/objectbox/latest/objectbox/PropertyQuery-class.html)) also offer aggregate functions to directly calculate the minimum, maximum, average, sum and count of all found values:

* `min()` / `minDouble()`: Finds the minimum value for the given property over all objects matching the query.
* `max()` / `maxDouble()`: Finds the maximum value.
* `sum()` / `sumDouble()`: Calculates the sum of all values. *Note: the non-double version detects overflows and throws an exception in that case.*
* `avg()` : Calculates the average (always a double) of all values.
* `count()`: returns the number of results. This is faster than finding and getting the length of the result array. Can be combined with `distinct()` to count only the number of distinct values.

## Query a related entity (links)

After creating a relation between entities, you might want to add a query condition for a property that only exists in the related entity. In SQL this is solved using JOINs. But as ObjectBox is not a SQL database we built something very similar: links. Links are based on [Relations ](/relations)- see the doc page for the introduction.

Assume there is a `Person` that can be associated with multiple `Address` entities:

{% tabs %}
{% tab title="Java" %}

```java
@Entity
public class Person {
    @Id long id;
    String name;
    ToMany<Address> addresses;
}

@Entity
public class Address {
    @Id long id;
    String street;
    String zip;
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Entity
class Person {
    @Id
    var id: Long = 0
    var name: String? = null
    lateinit var addresses: ToMany<Address>
}

@Entity
class Address {
    @Id
    var id: Long = 0
    var street: String? = null
    var zip: String? = null
}
```

{% endtab %}

{% tab title="Dart" %}

```dart
@Entity()
class Person {
    int id;
    String name;
    final addresses = ToMany<Address>();
}

@Entity()
class Address {
    int id;
    String street;
    String zip;
}
```

{% endtab %}
{% endtabs %}

To get a `Person` with a certain name that also lives on a specific street, we need to query the associated `Address` entities of a `Person`. To do this, use the "link" method of the query builder to tell that the `addresses` relation should be queried. Then add a condition for `Address`:

{% tabs %}
{% tab title="Java" %}

```java
// get all Person objects named "Elmo"...
QueryBuilder<Person> builder = personBox
    .query(Person_.name.equal("Elmo"));
// ...which have an address on "Sesame Street"
builder.link(Person_.addresses)
    .apply(Address_.street.equal("Sesame Street"));
List<Person> elmosOnSesameStreet = builder.build().find();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// get all Person objects named "Elmo"...
val builder = personBox
    .query(Person_.name.equal("Elmo"))
// ...which have an address on "Sesame Street"
builder.link(Person_.addresses)
    .apply(Address_.street.equal("Sesame Street"))
val elmosOnSesameStreet = builder.build().find()
```

{% endtab %}

{% tab title="Dart" %}

```dart
// get all Person objects named "Elmo"...
QueryBuilder<Person> builder = personBox
    .query(Person_.name.equals('Elmo'));
// ...which have an address on "Sesame Street"
builder.linkMany(Person_.addresses, Address_.street.equals('Sesame Street'));
Query<Person> query = builder.build();
List<Person> elmosOnSesameStreet = query.find();
query.close();
```

{% endtab %}
{% endtabs %}

What if we want to get a list of `Address` instead of `Person`? If you know ObjectBox relations well, you would probably add a `@Backlink` relation to `Address` and build your query using the "link" method as shown above. This approach works when using the Java API, but not for the Dart API:

{% tabs %}
{% tab title="Java" %}

```java
@Entity
public class Address {
    // ...
    @Backlink(to = "addresses")
    ToMany<Person> persons;
}

// get all Address objects with street "Sesame Street"...
QueryBuilder<Address> builder = addressBox
    .query(Address_.street.equal("Sesame Street"));
// ...which are linked from a Person named "Elmo"
builder.link(Address_.persons)
    .apply(Person_.name.equal("Elmo"));
List<Address> sesameStreetsWithElmo = builder.build().find();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Entity
class Address {
    // ...
    @Backlink(to = "addresses")
    lateinit var persons: ToMany<Person>
}

// get all Address objects with street "Sesame Street"...
val builder = addressBox
    .query(Address_.street.equal("Sesame Street"))
// ...which are linked from a Person named "Elmo"
builder.link(Address_.persons)
    .apply(Person_.name.equal("Elmo")
val sesameStreetsWithElmo = builder.build().find()
```

{% endtab %}

{% tab title="Dart" %}

```dart
@Entity()
class Address {
    ...
    
    @Backlink()
    final persons = ToMany<Person>();
}

// To query the address ToMany, use the backlinkMany() query builder method shown
// in the example below.
// Background: for Dart ObjectBox doesn't generate the Address_.persons meta
// property, required to build queries.
```

{% endtab %}
{% endtabs %}

An alternative, that also works using the Dart API, is to use the available "backlink" query method to create the "backlink" on-demand to the `addresses` relation from `Person` just for a query:

{% tabs %}
{% tab title="Java" %}

```java
// get all Address objects with street "Sesame Street"...
QueryBuilder<Address> builder = addressBox
    .query(Address_.street.equal("Sesame Street"));
// ...which are linked from a Person named "Elmo"
builder.backlink(Person_.addresses)
    .apply(Person_.name.equal("Elmo"));
List<Address> sesameStreetsWithElmo = builder.build().find();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// get all Address objects with street "Sesame Street"...
val builder = addressBox
    .query(Address_.street.equal("Sesame Street"))
// ...which are linked from a Person named "Elmo"
builder.backlink(Person_.addresses)
    .apply(Person_.name.equal("Elmo"))
val sesameStreetsWithElmo = builder.build().find()
```

{% endtab %}

{% tab title="Dart" %}

```dart
// get all Address objects with street "Sesame Street"...
QueryBuilder<Address> builder = 
    addressBox.query(Address_.street.equals('Sesame Street'));
// ...which are linked from a Person named "Elmo"
builder.backlinkMany(Person_.addresses, Person_.name.equals('Elmo'));
Query<Address> query = builder.build();
List<Address> sesameStreetsWithElmo = query.find();
query.close();
```

{% endtab %}
{% endtabs %}

## Eager-load relations

{% hint style="info" %}
Only Java/Kotlin. Relations are not yet available in TypeScript.
{% endhint %}

By default [relations](/relations) are loaded lazily: when you first access a `ToOne` or `ToMany` property it will perform a database lookup to get its data. On each subsequent access it will use a cached version of that data.

{% tabs %}
{% tab title="Java" %}

```java
List<Customer> customers = customerBox.query().build().find();
// Customer has a ToMany called orders.
// First access: this will cause a database lookup.
Order order = customers.get(0).orders.get(0);
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val customers = customerBox.query().build().find()
// Customer has a ToMany called orders
val order = customers[0].orders[0] // first access: causes a database lookup
```

{% endtab %}
{% endtabs %}

While this initial lookup is fast, you might want to prefetch `ToOne` or `ToMany` values before the query results are returned. To do this call the [`QueryBuilder.eager`](https://objectbox.io/files/objectbox-java/current/io/objectbox/query/QueryBuilder.html) method when building your query and pass the `RelationInfo` objects associated with the `ToOne` and `ToMany` properties to prefetch:

{% tabs %}
{% tab title="Java" %}

```java
List<Customer> customers = customerBox.query()
    .eager(Customer_.orders) // Customer has a ToMany called orders.
    .build()
    .find();
// First access: this will cause a database lookup.
Order order = customers.get(0).orders.get(0);
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val customers = customerBox.query()
    .eager(Customer_.orders) // Customer has a ToMany called orders
    .build()
    .find()
customers[0].orders[0] // first access: this will NOT cause a database lookup
```

{% endtab %}
{% endtabs %}

Eager loading only works one level deep. If you have **nested relations** and you want to prefetch relations of all children, you can instead add a query filter as described below. Use it to simply access all relation properties, which triggers them to lookup there values as described above.

## Query filters

{% hint style="info" %}
Only Java/Kotlin. For Dart and TypeScript, use the built-in array filter methods (`where()` in Dart, `filter()` in TypeScript).
{% endhint %}

Query filters come into play when you are looking for objects that need to match complex conditions, which cannot be fully expressed with the QueryBuilder class. Filters are written in Java and thus can express any complexity. Needless to say, that database conditions can be matched more efficiently than Java-based filters. Thus you will get the best results when you use both together:

1. Narrow down results using standard database conditions to a reasonable number (use QueryBuilder to get “candidates”)
2. Now filter those candidates using the [QueryFilter](https://objectbox.io/docfiles/java/current/io/objectbox/query/QueryFilter.html) Java interface to identify final results

A QueryFilter implementation looks at one candidate object at a time and returns true if the candidate is a result or false if not.

Example:

```java
// Reduce object count to reasonable value.
songBox.query(Song_.bandId.equal(bandId))
        // Filter is performed on candidate objects.
        .filter((song) -> song.starCount * 2 > song.downloads);
```

Notes on performance: 1) ObjectBox creates objects very fast. 2) The virtual machine is tuned to garbage collect short-lived objects. Notes 1) and 2) combined makes a case for filtering because ObjectBox creates candidate objects of which some are not used and thus get garbage collected quickly after their creation.

### Query filters and ToMany relation

The ToMany class offers additional methods that can be convenient in query filters:

* hasA: returns true if one of the elements matches the given QueryFilter
* hasAll: returns true if all of the elements match the given QueryFilter
* getById: return the element with the given ID (value of the property with the @Id annotation)

## Debug queries

To see what query is actually executed by ObjectBox:

{% tabs %}
{% tab title="Java" %}

```java
// Set the LOG_QUERY_PARAMETERS debug flag
BoxStore store = MyObjectBox.builder()
    .debugFlags(DebugFlags.LOG_QUERY_PARAMETERS)
    .build();
    
// Execute a query
query.find();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// Set the LOG_QUERY_PARAMETERS debug flag
val store = MyObjectBox.builder()
    .debugFlags(DebugFlags.LOG_QUERY_PARAMETERS)
    .build()
    
// Execute a query
query.find()
```

{% endtab %}

{% tab title="Dart" %}

```dart
print(query.describeParameters());
```

{% endtab %}

{% tab title="TypeScript" %}
{% hint style="info" %}
Query debugging is not yet available in TypeScript.
{% endhint %}
{% endtab %}
{% endtabs %}

Then in your console (or logcat on Android) you will see log output like:

```
Parameters for query #2:
(firstName ==(i) "Joe"
 AND age < 12)
```


# On-Device Vector Search

Local on-device approximate nearest neighbor (ANN) search on high-dimensional vector properties

{% hint style="info" %}
Vector Search is currently available for Python, C, C++, Dart/Flutter, Java/Kotlin and Swift. Other languages will follow soon.
{% endhint %}

**Vector search** is the task of searching for objects whose vector is near to a given input query vector. Applications include semantic/similarity search (often performs better than full text search (FTS)), multi-modal search (text, images, video), recommendation engines and various use cases in AI.

You can use ObjectBox as a plain vector database and store only vectors and IDs. Or, you can have your entire data model stored in ObjectBox since it is also a full database. Choose anything in between that best suits your needs.

To use ObjectBox as a vector database follow these 3 simple steps:

1. Define your data model along with a vector index,
2. Insert your data/vectors,
3. Search for nearest neighbors.

## An Example: Cities and their Location

To illustrate these steps, we will use a simplified example using cities throughout the next sections. Each city has a location expressed as latitude and longitude. And thus, we can search cities that are close to a certain point.

<figure><img src="/files/GgHBwrjM5aRmCeckwFl6" alt="" width="563"><figcaption></figcaption></figure>

In the diagram above, let's look at which are the closest cities to the red point, which is located at latitude 53.0 and longitude 15.0. One can intuitively see that Berlin is closest, followed by Copenhagen and Vienna. In search terms, Berlin, Copenhagen and Vienna are the 3 nearest neighbors to the given point.

A city's location has only 2 dimensions, which is easy to grasp. While even 3D space can still be quite intuitive, we humans typically have a hard time when dimensions increase beyond that. But this is exactly where vector databases shine: they handle high-dimensional data with typically hundreds or thousands of dimensions.

## Data Model and Vector Index

In order to enable efficient vector search, a vector database needs to "index" the data. This is true for ordinary data too, just that a vector index is a "special" index. ObjectBox relies on Hierarchical Navigable Small Worlds (HNSW), a state-of-the-art algorithm for approximate nearest neighbor (ANN) search that performs very fast and is very scalable.

{% hint style="info" %}
**Hierarchical Navigable Small Worlds (HNSW)** *- Background Information*

HNSW spans a graph over all vectors by connecting each vector node to its closest neighbors. To search for the nearest neighbors of a given vector, you start at an arbitrary node and check which of its neighbors are closer to the given vector. This repeats until the closest nodes are identified. To make this scalable, HNSW uses layers: higher layers have less nodes to get closer to the destination vector faster. Once the closest node in a layer is identified, the algorithm moves down one layer. This repeats until the lowest layer which contains all nodes is reached and the actual approximate nearest neighbors are found.

**Approximate nearest neighbor (ANN) search**: with high-dimensional vectors, exact nearest neighbor search is extremely time consuming (see also: [curse of dimensionality](https://en.wikipedia.org/wiki/Curse_of_dimensionality)). Thus, approximate solutions like HNSW are the way to go in this domain. Typically, they come with a quality/performance trade-off. In the case of HNSW you have parameters to control that.

<img src="/files/aV8wbyGCEoMKpzCBRPGS" alt="" data-size="original">

If you are interested in scientific details, check out the [HNSW paper](https://arxiv.org/abs/1603.09320).
{% endhint %}

Defining the data model in ObjectBox is straight-forward: in one of the supported programming languages, declare a City class and "annotate" (tag) it as an ObjectBox "Entity" (a persistable object type; check the [Getting started](/getting-started) guide for details). The class shall have the following members (properties): an obligatory ID, a name, and a location. The latter is expressed as a vector; after all we want to demonstrate vector search. This vector only has 2 dimensions: latitude and longitude.

So, this is what a City data class with an HNSW index definition can look like:

{% tabs %}
{% tab title="Python" %}

```python
@Entity()
class City:
    id = Id
    name = String
    location = Float32Vector(index=HnswIndex(
        dimensions=2,
        distance_type=VectorDistanceType.EUCLIDEAN
    ))
```

{% endtab %}

{% tab title="Dart" %}

```dart
@Entity()
class City {
  @Id()
  int id = 0;

  String? name;

  @HnswIndex(dimensions: 2, distanceType: VectorDistanceType.geo)
  @Property(type: PropertyType.floatVector)
  List<double>? location;
  
  City(this.name, this.location);
}
```

{% endtab %}

{% tab title="Java" %}

```java
@Entity
public class City {
    @Id 
    long id = 0;

    @Nullable 
    String name;

    @HnswIndex(dimensions = 2, distanceType = VectorDistanceType.GEO)
    float[] location;
    
    public City(@Nullable String name, float[] location) {
        this.name = name;
        this.location = location;
    }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Entity
data class City(
    @Id var id: Long = 0,
    var name: String? = null,
    @HnswIndex(dimensions = 2, distanceType = VectorDistanceType.GEO) 
    var location: FloatArray? = null
)
```

{% endtab %}

{% tab title="Swift" %}

```swift
// objectbox: entity
class City {
    var id: Id = 0
    
    var name: String?
    
    // objectbox:hnswIndex: dimensions=2, distanceType="geo"
    var location: [Float]?    
}

// The syntax for all supported options is:
// objectbox:hnswIndex: dimensions=2, neighborsPerNode=30, indexingSearchCount=100, flags="debugLogs", distanceType="euclidean", reparationBacklinkProbability=0.95, vectorCacheHintSizeKB=2097152

// flags may be a comma-separated list of debugLogs, debugLogsDetailed, reparationLimitCandidates, vectorCacheSimdPaddingOff
// distanceType may be one of euclidean, geo, cosine, dotProduct, dotProductNonNormalized
```

{% endtab %}

{% tab title="C++" %}
For C++, you define the data model using FlatBuffer schema files (see the [getting started guide](https://cpp.objectbox.io/getting-started) for details):

{% code title="city.fbs" %}

```
table City {
    id: ulong;
    name: string;
    /// objectbox: index=hnsw, hnsw-dimensions=2
    /// objectbox: hnsw-distance-type=Euclidean
    location: [float];
}


```

{% endcode %}

Once the ObjectBox Generator was run, it creates a City struct like this:

```cpp
struct City {
    obx_id id;
    std::string name;
    std::vector<float> location;
}
```

{% endtab %}
{% endtabs %}

As a starting point the index configuration only needs the number of dimensions. To optimize the index, you can supply additional options via the annotation later once you got things up and running:

* **dimensions (required)**: how many dimensions of the vector to use for indexing. This is a fixed value that depends on your specific use case (e.g. on your embedding model) and you will typically only use vectors of that exact dimension. For special use cases, you can insert vectors with a higher dimension. However, if the vector of an inserted object has less dimensions, it is completely ignored for indexing (it cannot be found).
* **distanceType**: the algorithm used to determine the distance between two vectors. By default, (squared) Euclidean distance is used: `d(v, w) = length(v - w)` Other algorithms, based on cosine, Haversine distance and dot product, are available.
* **neighborsPerNode** (aka "M" in HNSW terms): the maximum number of connections per node (default: 30). A higher number increases the graph connectivity which can lead to better results, but higher resources usage. Try e.g. 16 for faster but less accurate results, or 64 for more accurate results.
* **indexingSearchCount** (aka "efConstruction" in HNSW terms): the number of neighbors searched for while indexing (default: 100). The default value serves as a starting point that can likely be optimized for specific datasets and use cases. The higher the value, the more accurate the search, but the longer the indexing will take. If indexing time is not a major concern, a value of at least 200 is recommended to improve search quality.

There are also some advanced options available:

* **flags** to turn on debug log output, to turn off SIMD padding, or to limit graph reparation when nodes are removed.
* **reparationBacklinkProbability**: when a node is removed, its neighborhood is repaired. Use this to configure the probability of adding backlinks between repaired nodes (defaults to 1.0, which is always).
* **vectorCacheHintSizeKB**: a non-binding hint for the maximum size of the vector cache (default: 2 GB). Note: memory is only allocated for caching as needed. E.g. smaller data sets will reserve less memory.

## Insert Vector Objects

Vector objects are inserted like any other data objects in ObjectBox (the indexing is done automatically behind the scenes):

{% tabs %}
{% tab title="Python" %}

```python
store = Store()
box = store.box(City)
box.put(City(name="Barcelona", location=[41.385063, 2.173404]))
box.put(City(name="Nairobi", location=[-1.292066, 36.821945]))
box.put(City(name="Salzburg", location=np.array([47.809490, 13.055010])))
```

In Python, vector values can be plain Python lists or numpy arrays (the property type must be compatible with numpy array `dtype`).

{% hint style="info" %}
**Performance note:** for inserting multiple objects at once, wrap a [transaction](/transactions) around the `put` commands.
{% endhint %}
{% endtab %}

{% tab title="Dart" %}

<pre class="language-dart"><code class="lang-dart">final box = store.box&#x3C;City>();
box.putMany([
<strong>  City("Barcelona", [41.385063, 2.173404]),
</strong>  City("Nairobi", [-1.292066, 36.821945]),
  City("Salzburg", [47.809490, 13.055010]),
]);
</code></pre>

{% endtab %}

{% tab title="Java" %}

```java
final Box<City> box = store.boxFor(City.class);
box.put(
        new City("Barcelona", new float[]{41.385063F, 2.173404F}),
        new City("Nairobi", new float[]{-1.292066F, 36.821945F}),
        new City("Salzburg", new float[]{47.809490F, 13.055010F})
);
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val box = store.boxFor(City::class)
box.put(
    City(name = "Barcelona", location = floatArrayOf(41.385063f, 2.173404f)),
    City(name = "Nairobi", location = floatArrayOf(-1.292066f, 36.821945f)),
    City(name = "Salzburg", location = floatArrayOf(47.809490f, 13.055010f))
)
```

{% endtab %}

{% tab title="Swift" %}

```swift
let box: Box<City> = store.box()
try box.put([
  City("Barcelona", [41.385063, 2.173404]),
  City("Nairobi", [-1.292066, 36.821945]),
  City("Salzburg", [47.809490, 13.055010]),
])
```

{% endtab %}

{% tab title="C++" %}

```cpp
cityBox.put({
             City{0, "Barcelona", {41.385063F, 2.173404F}},
             City{0, "Nairobi", {-1.292066F, 36.821945F}},
             City{0, "Salzburg", {47.809490F, 13.055010F}}
});
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Note:** for the City example, it is easy to obtain the vector values. For more complex use cases it usually takes an additional step to create the vectors. Often an AI model is used, which is covered in a section further down.
{% endhint %}

## Perform a nearest neighbor search

(Approximate) nearest neighbor search is part of the standard [ObjectBox Query API](/queries). For vector properties with an HNSW index, a special **"nearest neighbor" query condition** is available when building a query. It accepts a **query vector** to search neighbors for, and the maximum number of results to return. See the method documentation for additional details.

Once the query is built, one of its "find" methods is called to execute it. For vector search, a special set of **"find with scores" methods** is available, that also return the distance (the "score") to the queried vector. Additionally, only the "find with scores" methods will return the results ordered by the distance (nearest first). And as with other find methods, you can either only retrieve result IDs or complete objects.

{% tabs %}
{% tab title="Python" %}

```python
# Query vector
madrid = [40.416775, -3.703790]  

# Prepare a Query object to search for the 2 closest neighbors:
query = box.query(City.location.nearest_neighbor(madrid, 2)).build()

# Retrieve IDs
results = query.find_ids_with_scores()
for id_, score in results:
    print(f"City ID: {id_}, distance: {score}")

# Retrieve objects
results = query.find_with_scores()
for object_, score in results: 
    print(f"City: {object_.name}, distance: {score}")
```

{% endtab %}

{% tab title="Dart" %}

```dart
final madrid = [40.416775, -3.703790]; // query vector
// Prepare a Query object to search for the 2 closest neighbors:
final query = box
    .query(City_.location.nearestNeighborsF32(madrid, 2))
    .build();

// Combine with other conditions as usual
final query = box
    .query(City_.location.nearestNeighborsF32(madrid, 2)
    .and(City_.name.startsWith("B")))
    .build();
    
// Retrieve IDs
final results = query.findIdsWithScores();
for (final result in results) {
  print("City ID: ${result.id}, distance: ${result.score}");
}

// Retrieve objects
final results = query.findWithScores();
for (final result in results) {
  print("City: ${result.object.name}, distance: ${result.score}");
}
```

{% endtab %}

{% tab title="Java" %}

```java
final float[] madrid = {40.416775F, -3.703790F}; // query vector
// Prepare a Query object to search for the 2 closest neighbors:
final Query<City> query = box
        .query(City_.location.nearestNeighbors(madrid, 2))
        .build();

// Combine with other conditions as usual
final Query<City> query = box
        .query(City_.location.nearestNeighbors(madrid, 2)
                .and(City_.name.startsWith("B")))
        .build();

// Retrieve IDs
final List<IdWithScore> results = query.findIdsWithScores();
for (IdWithScore result : results) {
    System.out.printf("City ID: %d, distance: %f%n", result.getId(), result.getScore());
}

// Retrieve objects
final List<ObjectWithScore<City>> results = query.findWithScores();
for (ObjectWithScore<City> result : results) {
    System.out.printf("City: %s, distance: %f%n", result.get().name, result.getScore());
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val madrid = floatArrayOf(40.416775f, -3.703790f) // query vector
// Prepare a Query object to search for the 2 closest neighbors:
val query = box
    .query(City_.location.nearestNeighbors(madrid, 2))
    .build()

// Combine with other conditions as usual
val query = box
    .query(
        City_.location.nearestNeighbors(madrid, 2)
            .and(City_.name.startsWith("B"))
    )
    .build()

// Retrieve IDs
val results = query.findIdsWithScores()
for (result in results) {
    println("City ID: ${result.id}, distance: ${result.score}")
}

// Retrieve objects
val results = query.findWithScores()
for (result in results) {
    println("City: ${result.get().id}, distance: ${result.score}")
}
```

{% endtab %}

{% tab title="Swift" %}

```swift
let madrid = [40.416775, -3.703790] // query vector
// Prepare a Query object to search for the 2 closest neighbors:
let query = try box
    .query { City.location.nearestNeighbors(queryVector: madrid, maxCount: 2) }
    .build()

// Combine with other conditions as usual
final query = box
    .query { City.location.nearestNeighbors(queryVector: madrid, maxCount: 2)
             && City.name.startsWith("B") }
    .build()
    
// Retrieve IDs
let results = try query.findIdsWithScores()
for result in results {
    print("City ID: \(result.id), distance: \(result.score)")
}

// Retrieve objects
let results = try query.findWithScores()
for result in results {
    print("City: \(result.object.name), distance: \(result.score)")
}
```

{% endtab %}

{% tab title="C++" %}

```cpp
float madrid[] = {40.416775f, -3.703790f};
obx::Query<City> query = cityBox
        .query(City_::location.nearestNeighbors(madrid, 1))
        .build();
std::vector<std::pair<City, double>> citiesWithScores =
    queryCityByLocation_.findWithScores();

// Print the results
printf("%3s  %-18s  %-19s %-10s\n", "ID", "Name", "Location", "Score");
for (const auto& pair : citiesWithScores) {
    const City& city = pair.first;
    printf("%3" PRIu64 "  %-18s  %-9.2f %-9.2f %5.2f\n", city.id, city.name.c_str(),
           city.location[0], city.location[1], pair.second);
}
```

{% endtab %}
{% endtabs %}

And that's it! You have successfully performed your first vector search with ObjectBox. 🎉

{% hint style="info" %}
**Note**: if results do not need to be in any particular order, use the standard find methods that do not come with scores. Performance-wise they may have a tiny advantage, but again, do not expect the nearest elements to be the first results.
{% endhint %}

## AI Embeddings

Vector search is a central component in AI applications. In this context you will often be confronted with the term "embeddings". So, let's have a closer look:

<figure><img src="/files/hPDeB5l4gj5K51hnUgHH" alt="" width="375"><figcaption></figcaption></figure>

To make complex data (e.g. texts, images, videos, ...) accessible, it is transformed into an N-dimensional vector representation. This is called an **embedding**. An embedding has a certain meaning (or semantics) based on a given model and is associated with its source document. For example with multi-modal models, the word "cat" would be transformed to a vector that is nearby a vector that originated from a picture of a cat. This is the power of embeddings: they capture the essence of the data they represent. For AI Large Language Models (LLMs) these vector embeddings also mimic some "understanding" and semantics become "computable".

<figure><img src="/files/UxLM6NtMedLCYxtmwDVh" alt=""><figcaption></figcaption></figure>

Note that ObjectBox works with vectors, so it is your responsibility to create these vectors. E.g. you need access to some model that transforms data into vectors (embeddings). These models can be online or offline.

Typically, you will also use the model to transform the query. Let's say you have associated all your pictures with a vector by a multi-modal model and stored all vectors into ObjectBox. Now using the same model, you create a search vector to find similar pictures. The input to get this vector can be a single word like "cat", a sentence like "birthday party with friends", or an image showing a specific car.

## RAG

To take the previous section on embeddings a bit further, let's enter Retrieval Augmented Generation (RAG). RAG allows interactions between an LLM and external sources to produce better and individual results. Vector databases like ObjectBox are extremely efficient in that regard as they speak the "native language" of LLMs: vector embeddings. In contrast, a standard web search is magnitudes slower. In short, you can make individual data available in ObjectBox and wire it to an LLM to make this knowledge available. This wiring is already done by various libraries such as LangChain.

<figure><img src="/files/yXVOR9MYs9H7N89oaXWv" alt=""><figcaption></figcaption></figure>

### LangChain

For Python, we offer the package "langchain-objectbox" that integrates ObjectBox into the LangChain framework:

```bash
pip install langchain-objectbox --upgrade
```

Once installed, you can use ObjectBox as a local on-device vector store. It implements `VectorStore` and thus you can instantiate it with e.g. `from_documents` or `from_texts` or there asynchronous variants. Example:

```python
from langchain_objectbox.vectorstores import ObjectBox

objectbox = ObjectBox.from_texts(texts, embeddings,
       embedding_dimensions=768)
```

## Current Limitations and Future Plans

Current limitations:

* Python APIs are non-final and subject to change

Expect more features to come:

* Releases for all programming languages supported by ObjectBox
* Closer integration into queries to prefilter objects
* Performance improvements, e.g. on ARM CPUs
* Additional distance functions
* Quantization and support for non-float vectors

## Vector Search FAQ

#### How does this compare to libraries like FAISS?

ObjectBox Vector Search offers significant advantages by combining the capabilities of a vector search engine with the robustness of a full-featured database:

1. **No Memory Limitations**
   * Unlike in-memory libraries like FAISS, ObjectBox uses disk storage when data exceeds available memory, enabling scalability.
   * Smart caching ensures frequently accessed data remains in memory for optimal performance.
2. **Instant Readiness**
   * There's no need for an initial data load when starting the application. Simply open the database, and you can immediately begin performing vector searches.
3. **Efficient Updates**
   * When data changes (e.g., adding, modifying, or deleting entries), ObjectBox only persists the changes (deltas).
   * In contrast, most libraries rewrite the entire dataset, if they even support persistent storage.
4. **ACID Transactions**
   * ObjectBox ensures all updates are safely persisted, offering robust data integrity.
   * This eliminates concerns about data corruption during unexpected events, such as power outages.
5. **Unified Data Storage**
   * Vector data is rarely standalone; it often ties to related objects or metadata.
   * ObjectBox enables you to store your entire data model, keeping all related data in one place. For example:
     * An embedding model can generate vectors for images. These vectors can be stored alongside related properties like creation date, tags, filenames, etc.
     * Additionally, associated objects like users (e.g., the creator or those who liked the image) can also be stored within the same database.

With ObjectBox Vector Search, you get a powerful, flexible, and scalable solution tailored for modern applications where data relationships matter.


# Data Observers & Rx

How to observe box and query changes using ObjectBox with Java or Dart, how to integrate with RxJava.

On this page:

* [ObjectBox Java - Data Observers and Reactive Extensions](#objectbox-for-java-data-observers-and-reactive-extensions)
* [ObjectBox Dart - Reactive Queries](#flutter-dart)

## ObjectBox Java - Data Observers and Reactive Extensions

ObjectBox for Java makes it easy for your app to react to data changes by providing:

* data observers,
* reactive extensions,
* and an optional library to work with RxJava.

This makes setting up data flows easy while taking care of threading details.

## Reactive Observers: A First Example

Let’s start with an example to demonstrate what you can do with reactive data observers:

```java
// Keep a reference until the subscription is cancelled
// to avoid garbage collection.
private DataSubscription subscription;

// ...

// Build a regular query.
Query<Task> query = taskBox.query().equal(Task_.complete, false).build();
// Subscribe to its results.
subscription = query.subscribe()
     .on(AndroidScheduler.mainThread())
     .observer(data -> updateResultDisplay(data));

// Cancel to no longer receive updates (e.g. leaving screen).
subscription.cancel();

private void updateResultDisplay(List<Task> tasks) {
   // Do something with the given tasks.
}
```

The first line creates a regular query to get `Task` objects where `task.complete == false`. The second line connects an observer to the query. This is what happens:

* the query is executed in the background
* once the query finishes the observer gets the result data
* whenever changes are made to `Task` objects in the future, the query will be executed again
* once updated query results are in, they are propagated to the observer
* the observer is called on Android’s main thread

Now, let’s dive into the details.

## Data Observers Basics

When objects change, ObjectBox notifies subscribed data observers. They can either subscribe to changes of certain object types (via BoxStore) or to query results. To create a data observer you need to implement the generic `io.objectbox.reactive.DataObserver` interface:

```java
public interface DataObserver<T> {
    void onData(T data);
}
```

This observer will be called by ObjectBox when necessary: typically shortly after subscribing and when data changes.

{% hint style="info" %}
Note: `onData()` is called asynchronously and decoupled from the thread causing the data change (like the thread that committed a transaction).
{% endhint %}

### Observing General Changes

`BoxStore` allows a `DataObserver` to subscribe to object types. Let’s say we have a to-do list app where `Task` objects get added. To get notified when `Task` objects are added in another place in our app we can do the following:

{% tabs %}
{% tab title="Java" %}

```java
DataObserver<Class<Task>> observer = new DataObserver<Class<Task>>() {
    @Override public void onData(Class<Task> data) {
        // TODO Do something, e.g. run a query.
        // Just observing a query can also be done 
        // more easily, read on!
        List<Task> results = store.boxFor(Task.class)
            .query(Task_.text.contains("TODO"))
            .build()
            .find(offset, limit);
    }
};
// Keep the subscription while used 
// to avoid garbage collection of the observer.
subscription = boxStore.subscribe(Task.class).observer(observer);
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val observer = object : DataObserver<Class<Task>> {
    override fun onData(data: Class<Task>) {
        // TODO Do something, e.g. run a query.
        // Just observing a query can also be done 
        // more easily, read on!
        val results = store.boxFor(Task::class)
            .query(Task_.text.contains("TODO"))
            .build()
            .find(offset, limit)
    }
}
// Keep the subscription while used 
// to avoid garbage collection of the observer.
subscription = store.subscribe(Task::class.java).observer(observer)
```

{% endtab %}
{% endtabs %}

Here `onData()` is not called with anything useful as data. If you need more than being notified, like to get a list of `Task` objects following the above example, read on to learn how to observe queries.

{% hint style="info" %}
Note: there is also `subscribe()` which takes no arguments. It subscribes the observer to receive changes for all available object classes.
{% endhint %}

### Observing Queries

ObjectBox let’s you [build queries](/queries) to find the objects matching certain criteria. Queries are an essential part of ObjectBox: whenever you need a specific set of data, you will probably use a query.

Combining queries and observers results in a convenient and powerful tool: query observers will automatically deliver fresh results whenever changes are made to entities in a box. Let’s say you display a list of to-do tasks in your app. You can use a DataObserver to get all tasks that are not yet completed and pass them to a method updateUi() (note that we are using lambda syntax here):

```java
Query<Task> query = taskBox.query().equal(Task_.completed, false).build();
subscription = query.subscribe().observer(data -> updateResultDisplay(data));
```

So when is our observer lambda called? Immediately when an observer is subscribed, the query will be run in a separate thread. Once the query result is available, it will be passed to the observer. This is the first call to the observer.

Now let’s say a task gets changed and stored in ObjectBox. It doesn't matter where and how; it might be the user who marked a task as completed, or some backend thread putting additional tasks during synchronization with a server. In any case, the query will notify all observers with (potentially) updated query results.

Note that this pattern can greatly simplify your code: there is a single place where your data comes in to update your user interface. There is no separate initialization code, no wiring of events, no re-running queries, etc.

See the [subscribe()-method](https://objectbox.io/files/objectbox-java/current/io/objectbox/query/Query.html) documentation for more details.

### Canceling Subscriptions

When you call `observer()`, it returns a subscription object implementing the `io.objectbox.reactive.DataSubscription` interface:

```java
public interface DataSubscription {
    void cancel();
    boolean isCanceled();
}
```

Keep a reference to the `DataSubscription` for as long as results should be received, otherwise it can be garbage collected at any point. Also call `cancel()` on it once the observer should not be notified anymore, e.g. when leaving the current screen:

```java
// Keep a reference for as long as updates should be received.
DataSubscription subscription = boxStore.subscribe().observer(myObserver);

// To no longer receive updates (e.g. leaving screen):
subscription.cancel();
```

If you have more than one query subscription, you might find it useful to create a `DataSubscriptionList` instance instead to keep track of multiple `DataSubscription` objects. Pass the list to the `query.subscribe(subList)` overload. A basic example goes like this:

```java
private DataSubscriptionList subscriptions = new DataSubscriptionList();

protected void onStart() {
  super.onStart();
  Query<A> queryA = boxA.query().build();
  queryA.subscribe(subscriptions).observe(someObserverForAs);
  Query<B> queryB = boxB.query.build();
  queryB.subscribe(subscriptions).observe(someObserverForBs);
}

protected void onStop() {
  super.onStop();
  // Cancels both subscriptions (for A and B).
  subscriptions.cancel();
}
```

{% hint style="info" %}
Note: On Android, you would typically create the subscription in one of the `onCreate()/onStart()/onResume()` lifecycle methods and cancel it in its counterpart `onDestroy()/onStop()/onPause()`.
{% endhint %}

### Observers and Transactions

Observer notifications occur after a transaction is committed. For some scenarios it is especially important to know transaction bounds. If you call `box.put()` or `remove()` individually, an implicit transaction is started and committed. For example, this code fragment would trigger data observers on `User.class` twice:

```java
box.put(friendUser);
box.put(myUser);

// Log:
// User friendUser put.
// Observers of User called.
// User myUser put.
// Observers of User called.
```

There are several ways to combine several operations into one transaction, for example using one of the `runInTx()` or `callInTx()` methods in the BoxStore class. For our simple example, we can simply use an overload of `put()` accepting multiple objects:

```java
box.put(friendUser, myUser);

// Log:
// Users friendUser and myUser put.
// Observers of User called.
```

This results in a single transaction and thus in a single `DataObserver` notification.

## Reactive Extensions

In the first part you saw how data observers can help you keep your app state up to date. But there is more: ObjectBox comes with simple and convenient reactive extensions for typical tasks. While most of these are inspired by RxJava, they are not actually based on RxJava. ObjectBox brings its own features because not all developers are familiar with RxJava (for the RxJava ObjectBox library see below). We do not want to impose the complexity (Rx is almost like a new language to learn) and size of RxJava (\~10k methods) on everyone. So, let’s keep it simple and neat for now.

### Thread Scheduling

On Android, UI updates must occur on the main thread only. Luckily, ObjectBox allows to switch the observer from a background thread over to the main thread. Let’s take a look on a revised version of the to-do task example from above:

```java
Query<Task> query = taskBox.query().equal(Task_.complete, false).build();
// updateResultDisplay is always called on the Android main thread.
subscription = query.subscribe()
    .on(AndroidScheduler.mainThread())
    .observer(data -> updateResultDisplay(data));
```

Where is the difference? The additional `on()` call is all that is needed to tell where we want our observer to be called. `AndroidScheduler.mainThread()` is a built-in scheduler implementation. Alternatively, you can create an `AndroidScheduler` using a custom `Looper`, or build a fully custom scheduler by implementing the `io.objectbox.reactive.Scheduler` interface.

### Transforming Data

Maybe you want to transform the data before you hand it over to an observer. Let’s say, you want to keep track of the count of all stored objects for each type. The BoxStore subscription gives you the classes of the objects, and this example shows you how to transform them into actual object counts:

```java
subscription = boxStore.subscribe()
    .transform(clazz -> return boxStore.boxFor(clazz).count())
    .observer(count -> updateCount(count));
```

Note that the transform operation takes a `Class` object and returns a `Long` number. Thus the `DataObserver` receives the object count as a `Long` parameter in `onData()`.

While the lambda syntax is nice and brief, let’s look at the `io.objectbox.reactive.Transformer` interface for clarification of what the `transform()` method expects as a parameter:

```java
public interface DataTransformer<FROM, TO> {
    TO transform(FROM source) throws Exception;
}
```

Some additional notes on transformers:

* Transforms are not required to actually “transform” any data. Technically, it is fine to return the same data that is received and just do some processing with (or without) it.
* Transformers are always executed asynchronously. It is fine to perform long lasting operations.

### ErrorObserver

Maybe you noticed that a transformer may throw any type of exception. Also, a `DataObserver` might throw a `RuntimeException`. In both cases, you can provide an `ErrorObserver` to be notified about an exception that occurred. The `io.objectbox.reactive.ErrorObserver` is straight-forward:

```java
public interface ErrorObserver {
    void onError(Throwable th);
}
```

To specify your `ErrorObserver`, simply call the `onError()` method after `subscribe()`.

### Single Notifications vs. Only-Changes

When you subscribe to a query, the `DataObserver` gets both of the following by default:

* Initial query results (right after subscribing)
* Updated query results (underlying data was changed)

Sometimes you may by interested in only one of those. This is what the methods `single()` and `onlyChanges()` are for (call them after `subscribe()`). Single subscriptions are special in the way that they are cancelled automatically once the observer is notified. You can still cancel them manually to ensure no call to the observer is made at a certain point.

### Weak References

Sometimes it may be nice to have a weak reference to a data observer. Note that for the sake of a deterministic flow, it is advisable to cancel subscriptions explicitly whenever possible. If that does not scare you off, use `weak()` after `subscribe()`.

### Threading overview

To summarize threading as discussed earlier:

* Query execution runs on a background thread (exclusive for this task)
* `DataTransformer` runs on a background thread (exclusive for this task)
* `DataObserver` and `ErrorObserver` run on a background thread unless a scheduler is specified via the `on()` method.

## ObjectBox RxJava Extension Library

By design, there are zero dependencies on any Rx libraries in the core of ObjectBox. As you saw before ObjectBox gives you simple means to transform data, asynchronous processing, thread scheduling, and one time (single) notifications. However, you still might want to integrate with the mighty RxJava 2 (we have no plans to support RxJava 1). For this purpose we created the ObjectBox RxJava extension library:

```groovy
implementation "io.objectbox:objectbox-rxjava:$objectboxVersion"
```

It provides the classes `RxQuery` and `RxBoxStore`. Both offer static methods to subscribe using RxJava means.

For general object changes, you can use `RxBoxStore` to create an `Observable`. `RxQuery` allows to subscribe to query objects using:

* Flowable
* Observable
* Single

Example usage:

```java
// Keep a reference until the disposable is disposed
// to avoid garbage collection.
private Disposable disposable;

// ...

Query query = box.query().build();
disposable = RxQuery.observable(query).subscribe(this);
```

The extension library is open-source and available [GitHub](https://github.com/objectbox/objectbox-java/tree/master/objectbox-rxjava).

## ObjectBox Dart - Reactive Queries <a href="#flutter-dart" id="flutter-dart"></a>

You can build a Stream from a query to get notified any time there is a change to the box of any of the queried entities:

```dart
Stream<Query<Note>> watchedQuery = box.query(condition).watch();
final sub1 = watchedQuery.listen((Query<Note> query) {
  // This gets triggered any time a box of any of the queried entities
  // has changes, e.g. objects are put or removed.
  // Call any query method here, for example:
  print(query.count());
  print(query.find());
});
...
sub1.cancel(); // Cancel the subscription after your code is done.
```

For a Flutter app you typically want to get the latest results immediately when listening to the stream, and also get a list of objects instead of a query instance:

```dart
// Build and watch the query,
// set triggerImmediately to emit the query immediately on listen.
return box.query(condition)
    .watch(triggerImmediately: true)
    // Map it to a list of objects to be used by a StreamBuilder.
    .map((query) => query.find());
```


# Relations

ObjectBox Relations: Learn how to create and update to-one and to-many relations between entities in ObjectBox and improve performance.

## ObjectBox - Relations

{% hint style="info" %}
Prefer to dive right into code? Check out our

* [Kotlin Android example app](https://github.com/objectbox/objectbox-examples/tree/main/android-app-kotlin) using relations,
* [Java relations playground](https://github.com/objectbox/objectbox-examples/tree/main/android-app-relations) Android app,
* [Flutter relations example](https://github.com/objectbox/objectbox-dart/tree/main/objectbox/example/flutter/objectbox_demo_relations) app.
  {% endhint %}

{% hint style="info" %}
The Python API does not yet support relations.
{% endhint %}

Objects may reference other objects, for example using a simple reference or a list of objects. In database terms, we call those references **relations**. The object defining a relation we call the **source** object, the referenced object we call the **target** object. So a relation has a direction.

If there is one target object, we call the relation **to-one.** And if there can be multiple target objects, we call it **to-many**. Relations are lazily initialized: the actual target objects are fetched from the database when they are first accessed. Once the target objects are fetched, they are cached for further accesses.

## To-One Relations

<figure><img src="/files/1HgVMFAF52umjgSeAeLJ" alt="to-one-relations"><figcaption><p>To-one Relations</p></figcaption></figure>

You define a to-one relation using the `ToOne` class, a smart proxy to the target object. It gets and caches the target object transparently. For example, an order is typically made by one customer. Thus, we could model the `Order` class to have a to-one relation to the `Customer` like this:

{% tabs %}
{% tab title="Java" %}

```java
// Customer.java
@Entity
public class Customer {
    
    @Id public long id;
    
}

// Order.java
@Entity
public class Order {
    
    @Id public long id;
    
    public ToOne<Customer> customer;
    
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Entity
data class Customer(
        @Id var id: Long = 0
)

@Entity
data class Order(
        @Id var id: Long = 0
) {
    lateinit var customer: ToOne<Customer>
}
```

{% hint style="info" %}
For Kotlin desktop (Linux, macOS, Windows) apps, additional code is required. See [#initialization-magic](#initialization-magic "mention").
{% endhint %}
{% endtab %}

{% tab title="Dart" %}

```dart
@Entity()
class Customer {
  int id;
}

@Entity()
class Order {
  int id;
  
  final customer = ToOne<Customer>();
}
```

{% endtab %}
{% endtabs %}

Now let’s add a customer and some orders. **To set** the related customer object, call `setTarget()` (or assign `target` in Kotlin) on the ToOne instance **and put** the order object:

{% tabs %}
{% tab title="Java" %}

```java
Customer customer = new Customer();
Order order = new Order();
order.customer.setTarget(customer);
// Puts order and customer:
long orderId = boxStore.boxFor(Order.class).put(order);
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val customer = Customer()
val order = Order()
order.customer.target = customer
// Puts order and customer:
val orderId = boxStore.boxFor(Order::class.java).put(order)
```

{% endtab %}

{% tab title="Dart" %}

```dart
final customer = Customer();
final order = Order();

// set the relation
order.customer.target = customer;
// Or you could create the target object in place:
// order.customer.target = Customer();

// Save the order and customer to the database
int orderId = store.box<Order>().put(order);
```

{% endtab %}
{% endtabs %}

If the customer object does not yet exist in the database (i.e. its ID is zero), `ToOne` will put it (so there will be two puts, one for `Order`, one for `Customer`). If it already exists, `ToOne` will only create the relation (so there's only one put for `Order`, as explicitly written in the code). **See further below for details about** [**updating relations**](#updating-relations)**.**

{% hint style="info" %}
**Note:** if your related entity uses manually assigned IDs with `@Id(assignable = true)` ObjectBox won't know if a target object is a new one or an existing one, therefore it will NOT insert it, `customerBox.put(customer)` would have to be called manually (considering the previous example). See below about [updating ToOne](#updating-toone) for details.
{% endhint %}

Have a look at the following code how you can **get** (read) the customer of an order:

{% tabs %}
{% tab title="Java" %}

```java
Order order = boxStore.boxFor(Order.class).get(orderId);
Customer customer = order.customer.getTarget();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val order = boxStore.boxFor(Order::class.java)[orderId]
val customer = order.customer.target
```

{% endtab %}

{% tab title="Dart" %}

```dart
final order = store.box<Order>().get(orderId);
final customer = order.customer.target;
```

{% endtab %}
{% endtabs %}

This will do a database call on the first access (lazy loading). It uses lookup by ID, which is very fast in ObjectBox. If you only need the ID instead of the whole target object, you can completely avoid this database operation because it's already loaded: use `order.customer.targetId/getTargetId()`.

We can also **remove** the relationship to a customer:

{% tabs %}
{% tab title="Java" %}

```java
order.customer.setTarget(null);
boxStore.boxFor(Order.class).put(order);
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
order.customer.target = null
boxStore.boxFor(Order::class.java).put(order)
```

{% endtab %}

{% tab title="Dart" %}

```dart
order.customer.target = null; // same as  .targetId = 0;
store.box<Order>().put(order);
```

{% endtab %}
{% endtabs %}

Note that this does not remove the customer from the database, it just dissolves the relationship.

### How ToOne works behind the scenes

If you look at your model in `objectbox-models/default.json` (or `lib/bjectbox-model.json` in Dart) you can see, a ToOne property is not actually stored. Instead, the ID of the target object is saved in a virtual property named by default like the ToOne property appended with *Id*.

### Rename the ToOne target ID property

To change the default name of the target ID property created for a ToOne relation, use the `TargetIdProperty` annotation:

{% tabs %}
{% tab title="Java" %}

```java
@Entity
public class Order {
    // Change from default "customerId" to "customerRef"
    @TargetIdProperty("customerRef")
    public ToOne<Customer> customer;
    // Optional: expose target ID property (using changed name)
    public long customerRef;
}
```

{% endtab %}

{% tab title="Dart" %}

```dart
@Entity()
class Order {
    // Change from default "customerId" to "customerRef"
    @TargetIdProperty("customerRef")
    final customer = ToOne<Customer>();
}
```

{% endtab %}
{% endtabs %}

### Expose the ToOne target ID property

{% hint style="info" %}
Only Java/Kotlin
{% endhint %}

You can directly access the target ID property by defining a `long` (or `Long` in Kotlin) property in your entity class with the expected name:

{% tabs %}
{% tab title="Java" %}

```java
@Entity
public class Order {
    @Id public long id;
    
    public long customerId; // ToOne target ID property
    public ToOne<Customer> customer;
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Entity
data class Order(
        @Id var id: Long = 0,
        var customerId: Long = 0
) {
    lateinit var customer: ToOne<Customer>
}
```

{% endtab %}
{% endtabs %}

## Initialization Magic

{% hint style="info" %}
Only Java/Kotlin
{% endhint %}

Did you notice that the `ToOne` field `customer` was never initialized in the code example above? Why can the code still use `customer` without any NullPointerException? Because the field actually is initialized – the initialization code just is not visible in your sources.

**The ObjectBox Gradle plugin will transform your entity class (supported for Android projects and Java JVM projects)** to do the proper initialization in constructors before your code is executed. Thus, even in your constructor code, you can just assume `ToOne` and `ToMany`/ `List` properties have been initialized and are ready for you to use.

{% hint style="info" %}
**If your setup does not support transformations**, currently **Kotlin JVM** (Linux, macOS, Windows) projects, **add the below modifications** yourself. You also will have to call `box.attach(entity)` before modifying ToOne or ToMany properties.
{% endhint %}

{% tabs %}
{% tab title="Java" %}

```java
@Entity
public class Example {
​    
    // Initialize ToOne and ToMany manually.
    ToOne<Order> order = new ToOne<>(this, Example_.order);   
    ToMany<Order> orders = new ToMany<>(this, Example_.orders);
 
    // Add a BoxStore field.
    transient BoxStore __boxStore;
    
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Entity
class Example() {

    // Initialize ToOne and ToMany manually.
    var order = ToOne<Order>(this, Example_.order)   
    var orders = ToMany<Order>(this, Example_.orders)

    // Add a BoxStore field.
    @JvmField
    @Transient
    @Suppress("PropertyName")
    var __boxStore: BoxStore? = null

}
```

{% endtab %}
{% endtabs %}

## Improve Performance

{% hint style="info" %}
Only Java/Kotlin
{% endhint %}

To improve performance when ObjectBox constructs your entities, you should **provide an all-properties constructor**.

For a ToOne you have to add an id parameter, typically named like the ToOne field appended with `Id` . Check your `objectbox-models/default.json` file to find the correct name.

An example:

```java
@Entity
public class Order {
    
    @Id public long id;
    
    public ToOne<Customer> customer;
    
    public Order() { /* default constructor */ }
    
    public Order(long id, long customerId /* virtual ToOne id property */) {
        this.id = id;
        this.customer.setTargetId(customerId);
    }
    
}
```

## To-Many Relations

To define a to-many relation, you can use a property of type `ToMany`. As the ToOne class, the `ToMany` class helps you to keep track of changes and to apply them to the database.

Note that **to-many relations are resolved lazily** on first access, and then **cached** in the source entity inside the `ToMany` object. So subsequent calls to any method, like `size()` of the `ToMany`, do not query the database, even if the relation was changed elsewhere. To get the latest data fetch the source entity again or call `reset()` on the `ToMany`.

There is a slight difference if you require a one-to-many (1:N) or many-to-many (N:M) relation. A 1:N relation is like the example above where a customer can have multiple orders, but an order is only associated with a single customer. An example for an N:M relation is students and teachers: students can have classes by several teachers but a teacher can also instruct several students.

### One-to-Many (1:N)

<figure><img src="/files/KSqNVJ3jTobeMnFxxwnO" alt="Onte-to-Many"><figcaption><p>One-to-Many (1:N)</p></figcaption></figure>

To define a one-to-many relation, you need to annotate your relation property with `@Backlink`. It links back to a to-one relation in the target object. Using the customer and orders example, we can modify the customer class to have a to-many relation to the customer's orders:

{% tabs %}
{% tab title="Java" %}

```java
// Customer.java
@Entity
public class Customer {
    
    @Id public long id;
    
    @Backlink(to = "customer")
    public ToMany<Order> orders;
    
}

// Order.java
@Entity
public class Order {
    
    @Id public long id;
    public String name;
    
    public ToOne<Customer> customer;
    
    public Order(String name) {
        this.name = name;
    }
    
    public Order() {
    }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Entity
data class Customer(
        @Id var id: Long = 0
) {    
    @Backlink(to = "customer")
    lateinit var orders: ToMany<Order>
}

@Entity
data class Order(
        @Id var id: Long = 0,
        var name: String? = ""
) {
    lateinit var customer: ToOne<Customer>
}
```

{% hint style="info" %}
For Kotlin desktop (Linux, macOS, Windows) apps, additional code is required. See [#initialization-magic](#initialization-magic "mention").
{% endhint %}
{% endtab %}

{% tab title="Dart" %}

```dart
@Entity()
class Customer {
  int id;

  @Backlink('customer')
  final orders = ToMany<Order>();
}

@Entity()
class Order {
  int id;

  final customer = ToOne<Customer>();
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
When using `@Backlink` it is recommended to explicitly specify the linked to relation using `to`. It is possible to omit this if there is only one matching relation. However, it helps with code readability and avoids a compile-time error if at any point another matching relation is added (in the above case, if another `ToOne<Customer>` is added to the Order class).
{% endhint %}

Let’s **add** some orders together with a new customer. `ToMany` implements the Java List interface, so we can simply add orders to it:

{% tabs %}
{% tab title="Java" %}

```java
Customer customer = new Customer();
customer.orders.add(new Order("Order #1"));
customer.orders.add(new Order("Order #2"));
// Puts customer and orders:
long customerId = boxStore.boxFor(Customer.class).put(customer);
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val customer = Customer()
customer.orders.add(Order("Order #1"))
customer.orders.add(Order("Order #2"))
// Puts customer and orders:
val customerId = boxStore.boxFor(Customer::class.java).put(customer)
```

{% hint style="info" %}
Two data classes that have the same property values (excluding those defined in the class body) [are equal and have the same hash code](https://kotlinlang.org/docs/reference/data-classes.html). Keep this in mind when working with `ToMany` which uses a `HashMap` to keep track of changes. E.g. adding the same data class multiple times has no effect, it is treated as the same entity.
{% endhint %}
{% endtab %}

{% tab title="Dart" %}

```dart
Customer customer = Customer();
customer.orders.add(Order()); // Order #1
customer.orders.add(Order()); // Order #2
// Puts customer and orders:
final customerId = store.box<Customer>().put(customer);
```

{% endtab %}
{% endtabs %}

If the order entities do not yet exist in the database, `ToMany` will put them. If they already exist, it will only create the relation (but not put them). See further below for details about [updating relations](#updating-relations).

{% hint style="info" %}
**Note:** if your entities use manually assigned IDs with `@Id(assignable = true)` the above will not work. See below about [updating ToMany](#updating-tomany) for details.
{% endhint %}

We can easily **get** the orders of a customer back by accessing the list of orders:

{% tabs %}
{% tab title="Java" %}

```java
Customer customer = boxStore.boxFor(Customer.class).get(customerId);
for (Order order : customer.orders) {
    // Do something with each order.
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val customer = boxStore.boxFor(Customer::class.java).get(customerId)
for (order in customer.orders) {
    // Do something with each order.
}
```

{% endtab %}

{% tab title="Dart" %}

```dart
Customer customer = store.box<Customer>().get(customerId);
// you can use any List<> functions, for example:
final valueSum = customer.orders.fold(0, (sum, order) => sum + order.value);
// though you could use property queries and their .sum() function for that
```

{% endtab %}
{% endtabs %}

**Removing** orders from the relation works as expected:

{% tabs %}
{% tab title="Java" %}

```java
// Remove the relation to the first order in the list
Order order = customer.orders.remove(0);
boxStore.boxFor(Customer.class).put(customer);
// Optional: also remove the order entity from its box:
// boxStore.boxFor(Order.class).remove(order);
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// Remove the relation to the first order in the list
val order = customer.orders.removeAt(0)
boxStore.boxFor(Customer::class.java).put(customer)
// Optional: also remove the order entity from its box:
// boxStore.boxFor(Order::class.java).remove(order)
```

{% endtab %}

{% tab title="Dart" %}

```dart
// Remove the relation to the first order in the list
Order order = customer.orders.removeAt(0);
store.box<Customer>().put(customer);
// Optional: also remove the order entity from its box:
// store.box<Order>().remove(order);
```

{% endtab %}
{% endtabs %}

### Many-to-Many (N:M)

<figure><img src="/files/RgBxoHejjJeWJA2isJUt" alt="Many-to-Many"><figcaption><p>Many-to-Many (N:M)</p></figcaption></figure>

To define a many-to-many relation you simply add a property using the `ToMany` class. Assuming a students and teachers example, this is how a simple student class that has a to-many relation to teachers can look like:

{% tabs %}
{% tab title="Java" %}

```java
// Teacher.java
@Entity
public class Teacher{
    
    @Id public long id;
    public String name;
    
    public Teacher(String name) {
        this.name = name;
    }
    
    public Teacher() {
    }
}

// Student.java
@Entity
public class Student{
    
    @Id public long id;
    
    public ToMany<Teacher> teachers;
    
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Entity
data class Teacher(
        @Id var id: Long = 0,
        var name: String? = ""
)

@Entity
data class Student(
        @Id var id: Long = 0
) {
    lateinit var teachers: ToMany<Teacher>
}
```

{% hint style="info" %}
For Kotlin desktop (Linux, macOS, Windows) apps, additional code is required. See [#initialization-magic](#initialization-magic "mention").
{% endhint %}
{% endtab %}

{% tab title="Dart" %}

```dart
@Entity()
class Teacher{
  int id;
}

@Entity()
class Student{
  int id;
  
  final teachers = ToMany<Teacher>();
}
```

{% endtab %}
{% endtabs %}

**Adding** the teachers of a student works exactly like with a list:

{% tabs %}
{% tab title="Java" %}

```java
Teacher teacher1 = new Teacher("Teacher 1");
Teacher teacher2 = new Teacher("Teacher 2");

Student student1 = new Student();
student1.teachers.add(teacher1);
student1.teachers.add(teacher2);

Student student2 = new Student();
student2.teachers.add(teacher2);

// Puts students and teachers:
boxStore.boxFor(Student.class).put(student1, student2);
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val teacher1 = Teacher("Teacher 1")
val teacher2 = Teacher("Teacher 2")

val student1 = Student()
student1.teachers.add(teacher1)
student1.teachers.add(teacher2)

val student2 = Student()
student2.teachers.add(teacher2)

// Puts students and teachers:
boxStore.boxFor(Student::class.java).put(student1, student2)
```

{% endtab %}

{% tab title="Dart" %}

```dart
Teacher teacher1 = Teacher();
Teacher teacher2 = Teacher();

Student student1 = Student();
student1.teachers.add(teacher1);
student1.teachers.add(teacher2);

Student student2 = Student();
student2.teachers.add(teacher2);

// saves students as well as teachers in the database
store.box<Student>().putMany([student1, student2]);
```

{% endtab %}
{% endtabs %}

If the teacher entities do not yet exist in the database, `ToMany` will also put them. If they already exist, `ToMany` will only create the relation (but not put them). See further below for details about [updating relations](#updating-relations).

{% hint style="info" %}
**Note:** if your entities use manually assigned IDs with `@Id(assignable = true)` the above will not work. See below about [updating ToMany](#updating-tomany) for details.
{% endhint %}

To **get** the teachers of a student we just access the list:

{% tabs %}
{% tab title="Java" %}

```java
Student student = boxStore.boxFor(Student.class).get(studentId);
for (Teacher teacher : student.teachers) {
    // Do something with each teacher.
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val student = boxStore.boxFor(Student::class.java).get(studentId)
for (teacher in student.teachers) {
    // Do something with each teacher.
}
```

{% endtab %}

{% tab title="Dart" %}

```dart
Student student = store.box<Student>().get(studentId);
// you can use any List<> functions, for example:
student.teachers.forEach((Teacher teacher) => ...);
```

{% endtab %}
{% endtabs %}

And if a student drops out of a class, we can **remove** a teacher from the relation:

{% tabs %}
{% tab title="Java" %}

```java
student.teachers.remove(0);
// Simply put the student again:
// boxStore.boxFor(Student.class).put(student);
// Or more efficient (than writing the thole object), store just the relations:
student.teachers.applyChangesToDb();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
student.teachers.removeAt(0)
// Simply put the student again:
// boxStore.boxFor(Student::class.java).put(student)
// Or more efficient (than writing the thole object), store just the relations:
student.teachers.applyChangesToDb()
```

{% endtab %}

{% tab title="Dart" %}

```dart
student.teachers.removeAt(0)
// Simply put the student again:
// store.box<Student>().put(student);
// Or more efficient (than writing the thole object), store just the relations:
student.teachers.applyToDb();
```

{% endtab %}
{% endtabs %}

#### Access Many-To-Many in the reverse direction

Following the above example, you might want an easy way to find out what students a teacher has. Instead of having to perform a query, you can just add a to-many relation to the teacher and annotate it with the `@Backlink` annotation:

{% tabs %}
{% tab title="Java" %}

```java
// Teacher.java
@Entity
public class Teacher{
    
    @Id public long id;
    
    // Backed by the to-many relation in Student:
    @Backlink(to = "teachers")
    public ToMany<Student> students;
    
}

// Student.java
@Entity
public class Student{
    
    @Id public long id;
    
    public ToMany<Teacher> teachers;
    
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Entity
data class Teacher(
        @Id var id: Long = 0
) {
    // Backed by the to-many relation in Student:
    @Backlink(to = "teachers")
    lateinit var students: ToMany<Student>
}

@Entity
data class Student(
        @Id var id: Long = 0
) {
    lateinit var teachers: ToMany<Teacher>
}
```

{% hint style="info" %}
For Kotlin desktop (Linux, macOS, Windows) apps, additional code is required. See [#initialization-magic](#initialization-magic "mention").
{% endhint %}
{% endtab %}

{% tab title="Dart" %}

```dart
@Entity()
class Teacher{
  int id;
  
  // Backed by the to-many relation in Student:
  @Backlink()
  final students = ToMany<Student>();
}

@Entity()
public class Student{
  int id;
  
  final teachers = ToMany<Teacher>();
}
```

{% endtab %}
{% endtabs %}

### Using the List interface for to-many

{% hint style="info" %}
Only for Java/Kotlin
{% endhint %}

Instead of the `ToMany` type it is also possible to use `List` (or `MutableList` in Kotlin) for a to-many property. At runtime the property will still be a `ToMany` instance (`ToMany` does implement the `List` interface) due to the initialization magic described above, or if manually initialized as seen in the example below.

{% tabs %}
{% tab title="Java" %}

```java
// Teacher.java
@Entity
public class Teacher{    
    @Id public long id;    
}

// Student.java
@Entity
public class Student{    
    @Id public long id;    
    public List<Teacher> teachers = new ToMany<>(this, Student_.teachers);   
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Entity
data class Teacher(
        @Id var id: Long = 0
)

@Entity
data class Student(
        @Id var id: Long = 0
) {
    var teachers: MutableList<Teacher> = ToMany<>(this, Student_.teachers)
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
This may be helpful when trying to deserialize an object that contains a to-many from JSON. However, note that if the JSON deserializer replaces the `ToMany` instance with e.g. an `ArrayList` during put the to-many property is skipped. It is then up to you to create the relation.
{% endhint %}

```java
Box<Student> studentBox = store.boxFor(Student.class);
Student student = new Student();
Teacher teacher = new Teacher();

// Simulate what a JSON deserialzer would do:
// replace ToMany instance with ArrayList.
student.teachers = new ArrayList();
student.teachers.add(teacher);
// Put will skip the teachers property.
studentBox.put(student);
System.out.println(store.boxFor(Teacher.class).count());
// prints 0

// Need to manually create the relation.
Student student2 = studentBox.get(student.id);
student2.teachers.addAll(student.teachers);
studentBox.put(student2);
```

## Updating Relations

The ToOne and ToMany relation classes assist you to persist the relation state. They keep track of changes and apply them to the database once you **put the Object containing them**. ObjectBox supports relation updates for new (not yet persisted; ID == 0) and existing (persisted before; ID != 0) Objects.

For convenience, ToOne and ToMany will put Objects added to them if they do **not yet exist** (ID == 0). If they already **exist** (their ID != 0, or you are using `@Id(assignable = true)`), only the relation will be created or destroyed (internally the Object ID is added to or removed from the relation). In that case, to put changes to the properties of related Objects use their specific Box instead:

{% tabs %}
{% tab title="Java" %}

```java
// update a related entity using its box
Order orderToUpdate = customer.orders.get(0);
orderToUpdate.text = "Revised description";
// DOES NOT WORK
// boxStore.boxFor(Customer.class).put(customer);
// WORKS
boxStore.boxFor(Order.class).put(orderToUpdate);
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// update a related entity using its box
Order orderToUpdate = customer.orders.getAt(0);
orderToUpdate.text = "Revised description";
// DOES NOT WORK
// boxStore.boxFor(Customer::class.java).put(customer);
// WORKS
boxStore.boxFor(Order::class.java).put(orderToUpdate);
```

{% endtab %}

{% tab title="Dart" %}

```dart
// update a related entity using its box
Order orderToUpdate = customer.orders[0];
orderToUpdate.text = 'Revised description';
// DOES NOT WORK - the change to the order is not saved
// store.box<Customer>().put(customer);
// WORKS
store.box<Order>().put(orderToUpdate);
```

{% endtab %}
{% endtabs %}

### Updating ToOne

The ToOne class offers the following methods to update the relation:

* `setTarget(target)` makes the given (new or existing) Object the new relation target; pass `null` to clear the relation.
* `setTargetId(targetId)` sets the relation target based on the given ID of an existing Object; pass `0` (zero) to clear the relation.
* Java/Kotlin only: `setAndPutTarget(target)` makes the given (new or existing) Object the new relation target and puts the owning, source Object and if needed the target Object.

{% tabs %}
{% tab title="Java" %}

```java
// Option 1: set target and put.
order.customer.setTarget(customer);
// Or set target via Object ID: 
// order.customer.setCustomerId(customer.getId());
orderBox.put(order);

// Option 2: combined set target and put.
order.customer.setAndPutTarget(customer);
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// Option 1: set target and put.
order.customer.setTarget(customer)
// Or set target via Object ID: 
// order.customer.setCustomerId(customer.getId())
orderBox.put(order)

// Option 2: combined set target and put.
order.customer.setAndPutTarget(customer)
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
**Note:** attach the Box before calling `setAndPutTarget()` on a new (not put) Object owning a ToOne:

```java
Order order = new Order();
orderBox.attach(order);
order.customer.setAndPutTarget(customer);
```

{% endhint %}

{% hint style="warning" %}
**Note:** if the target Object class uses manually assigned IDs with `@Id(assignable = true)` it will not be put when the Object that owns the relation is put:

```java
Customer customer = new Customer();
// If ID is manually assigned, put target Object first
customer.id = 12;
customerBox.put(customer);
// Then can safely set as target
order.customer.setTarget(customer);
// Or set target via Object ID
// order.customer.setCustomerId(customer.getId());
orderBox.put(order);
```

This is because **ObjectBox only puts related entities with an ID of 0**. See the [documentation about IDs](/advanced/object-ids) for background information.
{% endhint %}

### Updating ToMany

The `ToMany` relation class is based on a standard `List` with added change tracking for Objects. As [mentioned above](#updating-relations), it will put new Objects (ID == 0) that are added to it once the Object owning it is put. And when removing Objects from it, just the relation is cleared, the Objects are not removed from their Box.

See the documentation on [One-to-Many](#one-to-many-1-n) and [Many-to-Many](#many-to-many-n-m) above for details.

{% hint style="warning" %}
**Note (Java/Kotlin only):** if your entities are using manually assigned IDs with `@Id(assignable = true)` additional steps are required. Read on for details:
{% endhint %}

If the owning, source Object uses `@Id(assignable = true)` attach its Box before modifying its ToMany:

```java
// If source has a manually assigned ID attach Box first
customer.id = 12;
customerBox.attach(customer);
// Then can safely modify ToMany
customer.orders.add(order);
customerBox.put(customer);
```

If the target Object, like `Order` above, is using manually assigned IDs put the target Objects before adding them to the ToMany relation:

```java
// If ID is manually assigned put target Object first
order.id = 42;
orderBox.put(order);
// Then can safely add target Object to ToMany
customer.orders.add(order);
customerBox.put(customer);
```

The above steps are required because, when putting the Object owning the ToMany only the relation is updated. This is because **ObjectBox only puts target Objects with an ID of 0**. See the [documentation about IDs](/advanced/object-ids) for the background information.

## Example: Extending the Model with an Address

A typical extension to the customer/order example we have started earlier would be to add an `Address` type. While you could model street, ZIP and so on directly into `Customer`, it usually makes sense to normalize that out into a separate entity. And when you think about the usual shopping sites, they all let you have multiple addresses...

So, typically, this is how you would model that:

* an address is bound to a customer; add a `ToOne<Customer> customer` to `Address`
* an order is shipped to one address, and might have a diverging billing address optionally; add `ToOne<Address> shippingAddress` and `ToOne<Address> billingAddress` to `Order`
* For each `ToOne` you could have a matching `ToMany` on the other side of the relation (backlink)

## Example: Modelling Tree Relations

You can model a tree relation with a to-one and a to-many relation pointing to itself:

{% tabs %}
{% tab title="Java" %}

```java
@Entity
public class TreeNode {
    @Id long id;
    
    ToOne<TreeNode> parent;
    
    @Backlink
    ToMany<TreeNode> children;
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Entity
data class TreeNode(
        @Id var id: Long = 0
) {
    lateinit var parent: ToOne<TreeNode>
    
    @Backlink
    lateinit var children: ToMany<TreeNode>
}
```

{% hint style="info" %}
For Kotlin desktop (Linux, macOS, Windows) apps, additional code is required. See [#initialization-magic](#initialization-magic "mention").
{% endhint %}
{% endtab %}
{% endtabs %}

This lets you navigate a tree nodes parent and children:

{% tabs %}
{% tab title="Java" %}

```java
TreeNode parent = treeNode.parent.getTarget();
List<TreeNode> children = treeNode.children;
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val parent: TreeNode = treeNode.parent.target
val children: List<TreeNode> = treeNode.children
```

{% endtab %}
{% endtabs %}


# ObjectBox Admin

The ObjectBox Admin web app (formerly Data Browser) is an easy way to view what's happening in your database of your app using a web browser. Browse your app's data and gain insights.

## ObjectBox Admin Web App

The ObjectBox Admin web app allows you to

* view data objects and schema of your database inside a regular web browser,
* display additional information,
* and download objects in JSON format.

Admin comes in two variants: as a standalone [desktop app (Docker image)](#run-via-docker) and embedded in the [Android](#admin-for-android) library.

The web app runs directly on your device or on your development machine. Behind the scenes, this is done by embedding a lightweight HTTP server into ObjectBox. It runs completely local (no Cloud whatsoever) and you can simply open the Admin in your Browser.

<figure><img src="/files/yzFIdZMbA1ClH7Dsmvzi" alt=""><figcaption><p>The Admin web app with the Data page open.</p></figcaption></figure>

## Run via Docker

Latest changes (2025-07-05):

* Add class and dependency diagrams to the schema page (view and download)
* Improved data view for large vectors by displaying only the first elements and the full vector in a dialog
* Detects images stored as bytes and shows them as such (PNG, GIF, JPEG, SVG, WEBP)

To run Admin on a desktop operating system, e.g. your development machine, you can launch ObjectBox Admin instantaneously using the official [ObjectBox Admin Docker image](https://hub.docker.com/r/objectboxio/admin) `objectboxio/admin`.\
\
**This requires a running Docker Engine or Docker Desktop.** If not done, install and start [Docker Engine](https://docs.docker.com/engine/) (Linux, Windows/WSL2) or [Docker Desktop](https://docs.docker.com/get-docker/) (Windows, macOS) capable of running a Linux/x86\_64 image.

{% hint style="warning" %}
There's a known issue on macOS and Windows with Docker and changes to the filesystem. Thus, to see changes made to the database, Admin needs to be restarted.
{% endhint %}

{% tabs %}
{% tab title="Bash Shell script" %}
**Recommended, but the below script needs to be run from a Linux distribution (for example Ubuntu through WSL2 on Windows) or macOS. To run from Windows PowerShell, see the "Run Docker manually" tab.**

{% file src="/files/o91fMIK85RLLIp6iYgvE" %}
`objectbox-admin.sh` Shell Front-End for ObjectBox Admin Docker Image
{% endfile %}

Download the script `objectbox-admin.sh` via the link above. Make it executable (e.g. `chmod +x objectbox-admin.sh`). Copy it to some place (e.g.`/usr/local/bin`) and then run it.

Then you can have a quick look at the options of the script:

{% code overflow="wrap" %}

```bash
$ objectbox-admin.sh --help

usage: objectbox-admin.sh [options] [<database-directory>]

<database-directory> ( defaults to ./objectbox ) should contain an objectbox "data.mdb" file.

Available (optional) options:
 [--port <port-number>] Mapped bind port to localhost (defaults to 8081)
```

{% endcode %}

Basically you can optionally select the path to an ObjectBox database and the mapping of the local HTTP port (e.g. to open multiple Admins to analyze multiple databases).

So to run the script, either change to the directory where the `objectbox` directory with the database file `data.mdb` exists:

```bash
cd objectbox-c/examples/cpp-gen
objectbox-admin.sh
```

Or pass the path to it as an argument:

```
objectbox-admin.sh objectbox-c/examples/cpp-gen
```

If you see the error `failed: port is already allocated.` try to use a different local port. E.g. to use port `8082`:

```
objectbox-admin.sh --port 8082
```

Note: If you run the script for the first time Docker will download the ObjectBox Admin image automatically from Docker Hub. If run again the download is skipped as the image has been cached in your local image repository.
{% endtab %}

{% tab title="Run Docker manually" %}
The Docker image is available at `objectboxio/admin` on Docker Hub. We recommend to use the `latest` tag.

**Linux/macOS/Windows WSL2 command line**

{% code overflow="wrap" %}

```shell
docker run --rm -it --volume /path/to/db:/db -u $(id -u):$(id -g) --publish 8081:8081 objectboxio/admin:latest
```

{% endcode %}

Replace `/path/to/db` with the actual path to the directory containing the `data.mdb` file.

If you need to **use a different local port** other than 8081, modify the first number of `-p` accordingly. E.g. `-p 8082:8081` lets you open the web-app at `http://localhost:8082`.

**Windows PowerShell/Command Prompt**

Note: Make sure Docker Desktop runs [with WSL2 instead of Hyper-V](https://docs.docker.com/desktop/install/windows-install/).

{% code overflow="wrap" %}

```powershell
docker run --rm -it --volume C:\path\to\db:/db --publish 8081:8081 objectboxio/admin:latest
```

{% endcode %}

Replace `C:\path\to\db` with the actual path to the directory containing the `data.mdb` file. Note this uses Windows-style backslashes.

Note the user id (`-u`) mapping is omitted on Windows. The port can be changed as written above.
{% endtab %}
{% endtabs %}

Once Admin has started, open the local URL printed by the script (typically `http://127.0.0.1:8081`) in your browser. You should see the Data page for an entity type.

## Admin for Android

{% hint style="info" %}
Works for Android apps built with ObjectBox for Java or Flutter
{% endhint %}

We strongly recommend using Admin **only for debug builds** as it ships with additional resources and configuration not intended for production code.

{% tabs %}
{% tab title="Java" %}
Modify the app's Gradle build file to add the dependency and change **the “io.objectbox” plugin to be applied after the dependencies block:**

{% code title="app/build.gradle(.kts)" %}

```java
dependencies {
    // Manually add objectbox-android-objectbrowser only for debug builds,
    // and objectbox-android for release builds.
    debugImplementation("io.objectbox:objectbox-android-objectbrowser:$objectboxVersion")
    releaseImplementation("io.objectbox:objectbox-android:$objectboxVersion")
}

// Apply the plugin after the dependencies block so it picks up 
// and does not add objectbox-android.
apply plugin: 'io.objectbox'
// Or using Kotlin DSL:
apply(plugin = "io.objectbox")
```

{% endcode %}

If the plugin is not applied afterwards, the build will fail with a duplicate files error (like `Duplicate files copied in APK lib/armeabi-v7a/libobjectbox.so`) because the plugin fails to detect and adds the `objectbox-android` library.
{% endtab %}

{% tab title="Flutter" %}
Modify the Gradle build file of the Flutter Android app to exclude the variant added by the objectbox\_flutter\_libs package and add the dependency.

For a Kotlin buildscript (KTS):

{% code title="android/app/build.gradle.kts" %}

```groovy
// Tell Gradle to exclude the Android library (without Admin)
// that is added by the objectbox_flutter_libs package for debug builds.
configurations {
    named("debugImplementation") {
        exclude(group = "io.objectbox", module = "objectbox-android")
    }
}

dependencies {
    // Add the Android library with ObjectBox Admin only for debug builds.
    // Note: when the objectbox package updates, check if the Android
    // library below needs to be updated as well.
    // TODO Replace <version> with the one noted in the release notes (https://github.com/objectbox/objectbox-dart/releases)
    debugImplementation("io.objectbox:objectbox-android-objectbrowser:<version>")
}
```

{% endcode %}

Or when using the old Groovy syntax:

{% code title="android/app/build.gradle" %}

```groovy
// Tell Gradle to exclude the Android library (without Admin)
// that is added by the objectbox_flutter_libs package for debug builds.
configurations {
    debugImplementation {
        exclude group: 'io.objectbox', module: 'objectbox-android'
    }
}

dependencies {
    // Add the Android library with ObjectBox Admin only for debug builds.
    // Note: when the objectbox package updates, check if the Android
    // library below needs to be updated as well.
    // TODO Replace <version> with the one noted in the release notes (https://github.com/objectbox/objectbox-dart/releases)
    debugImplementation("io.objectbox:objectbox-android-objectbrowser:<version>")
}
```

{% endcode %}

To avoid a version mismatch on updates, we suggest to change the dependency on the [objectbox Dart package](https://pub.dev/packages/objectbox) from a range of versions to a concrete version:

```yaml
dependencies:
  # Note: when updating objectbox, check the release notes (https://github.com/objectbox/objectbox-dart/releases)
  # if objectbox-android-objectbrowser in android/app/build.gradle(.kts) has to be updated.
  objectbox: x.y.z # TODO Replace with valid version
  objectbox_flutter_libs: any
```

{% endtab %}
{% endtabs %}

Finally, after creating the store, to start Admin:

{% tabs %}
{% tab title="Java" %}
Create an `Admin` instance and call `start`:

```java
boxStore = MyObjectBox.builder().androidContext(this).build();
if (BuildConfig.DEBUG) {
    boolean started = new Admin(boxStore).start(this);
    Log.i("ObjectBoxAdmin", "Started: " + started);
}
```

{% endtab %}

{% tab title="Flutter" %}
Create an `Admin` instance and keep a reference, optionally close it once done using the web app:

```dart
if (Admin.isAvailable()) {
  // Keep a reference until no longer needed or manually closed.
  admin = Admin(store);
}

// (Optional) Close at some later point.
admin.close();
```

{% endtab %}
{% endtabs %}

<details>

<summary>Info: added Android manifest permissions</summary>

For your information, these are the permissions the `objectbox-android-objectbrowser` dependency **automatically adds** to `AndroidManifest.xml`:

```xml
<!-- Required to provide the web interface -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- Required to run keep-alive service when targeting API 28 or higher -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<!-- When targeting API level 33 or higher to post the initial Admin notification -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
```

If the dependency **is only used in debug builds** as recommended above, these permissions will **not be added to your release build.**

</details>

### Browse data on your test device

When Admin is started it will print the URL where to access the web app to the logs, e.g. something like:

```
ObjectBox Admin running at URL: http://127.0.0.1:8090/index.html
```

The URL can be opened on the device or emulator. To open the web app on your dev machine, see the instructions below.

For ObjectBox for Java, the app also displays a notification to access Admin. (Don't see the notification on Android 13 or newer? Try to manually turn on notifications for the app!) Tapping it will launch a service to keep the app alive and opens the Admin web app in the web browser on the device.

Stop the keep-alive service from the notification.

### Browse data on your dev machine

To open the web app on your development machine find the Admin URL log message as noted above.

Then, on your dev machine, [use the ADB command to forward the port](https://developer.android.com/studio/command-line/adb#forwardports) (or whichever you like) to that port of your device. If the default port 8090 is used, the command looks like this:

```bash
adb forward tcp:8090 tcp:8090
```

Then open the web app URL in a web browser on your dev machine.

## Download Objects

To download all objects of the currently viewed box tap the download all button at the very bottom. The exported data is in JSON format.

<figure><img src="/files/-MNvvBAqztJegxEoCarS" alt=""><figcaption><p>The download option for objects.</p></figcaption></figure>


# Transactions

ObjectBox is a fully transactional database satisfying ACID properties. ObjectBox database gives you an easy way to develop safe and efficient data applications; single or multi-threaded.

## ObjectBox - Transactions

A transaction can group several operations into a single unit of work that either executes completely or not at all. If you are looking for a more detailed introduction to transactions in general, please consult other resources like Wikipedia on [database transactions](https://en.wikipedia.org/wiki/Database_transaction). For ObjectBox transactions continue reading:

You may not notice it, but almost all interactions with ObjectBox involve transactions. For example, if you call `put` a write transaction is used. Also if you `get` an object or query for objects, a read transaction is used. All of this is done under the hood and transparent to you. It may be fine to completely ignore transactions altogether in your app without running into any problems. With more complex apps however, it’s usually worth learning transaction basics to make your app more consistent and efficient.

## TL;DR - a quick summary on Transactions

* Accessing data always happens inside an implicit transaction, the API hides this detail for convenience.
* You should use an explicit transaction for non-trivial operations for better speed and atomicity.
* Transactions manage multi-threading; e.g. a transaction is tied to a thread and vice versa.
* Read(-only) transactions never get blocked or block a write transaction.
* There can only be a single write transaction at any time; they run strictly one after the other (sequential).
* Sequential execution simplifies user code that is run in write transactions and makes it safer.
* Keep write transactions short to optimize throughput, e.g. prepare data before entering it.

## Explicit Transactions

We learned that all ObjectBox operations run in implicit transactions – unless an explicit transaction is in progress. In the latter case, multiple operations share the (explicit) transaction. In other words, with explicit transactions, you control the transaction boundary. Doing so can greatly improve efficiency and consistency in your app.

The advantage of explicit transactions over the bulk put operations is that you can perform any number of operations and use objects of multiple boxes. In addition, you get a consistent (transactional) view on your data while the transaction is in progress.

Example for a write transaction:

{% tabs %}
{% tab title="Java" %}
The class [BoxStore](https://objectbox.io/docfiles/java/current/io/objectbox/BoxStore.html) offers the following methods to perform explicit transactions:

* **runInTx:** Runs the given runnable inside a transaction.
* **runInReadTx:** Runs the given runnable inside a read(-only) transaction. Unlike write transactions, multiple read transactions can run at the same time.
* **runInTxAsync:** Runs the given Runnable as a transaction in a separate thread. Once the transaction completes the given callback is called (callback may be null).
* **callInTx:** Like runInTx(Runnable), but allows returning a value and throwing an exception.

```java
boxStore.runInTx(() -> {
    for (User user : allUsers) {
        if (modify(user)) {
            box.put(user);
        } else {
            box.remove(user);
        }
    }
});
```

{% endtab %}

{% tab title="Kotlin" %}
The class [BoxStore](https://objectbox.io/docfiles/java/current/io/objectbox/BoxStore.html) offers the following methods to perform explicit transactions:

* **runInTx:** Runs the given runnable inside a transaction.
* **runInReadTx:** Runs the given runnable inside a read(-only) transaction. Unlike write transactions, multiple read transactions can run at the same time.
* **runInTxAsync:** Runs the given Runnable as a transaction in a separate thread. Once the transaction completes the given callback is called (callback may be null).
* **callInTx:** Like runInTx(Runnable), but allows returning a value and throwing an exception.

```java
boxStore.runInTx {
    for (user in allUsers) {
        if (modify(user)) {
            box.put(user)
        } else {
            box.remove(user)
        }
    }
}
```

{% endtab %}

{% tab title="Dart" %}
The class [Store](https://pub.dev/documentation/objectbox/latest/objectbox/Store-class.html) offers the following methods to perform explicit transactions:

* [**runInTransaction**](https://pub.dev/documentation/objectbox/latest/objectbox/Store/runInTransaction.html)**:** Runs the given function inside a read or write transaction, depending on the given mode. Note that unlike write transactions, multiple read transactions can run at the same time.
* [**runInTransactionAsync**](https://pub.dev/documentation/objectbox/latest/objectbox/Store/runInTransactionAsync.html)**:** Spawns an isolate to run the given callback within a read or write transaction, depending on the given mode.

```dart
store.runInTransaction(TxMode.write, () {
  for (var user in allUsers) {
    if (modify(user)) {
      box.put(user);
    } else {
      box.remove(user.id);
    }
  }
});
```

{% endtab %}

{% tab title="Python" %}
The `Store` class provides `read_tx` and `write_tx` methods for creating read/write transactions which should be called in a `with` statement:

```python
with store.write_tx():
    for user in allUsers:
        if modify(user):
            box.put(user)
        else:
            box.remove(user)
```

{% endtab %}
{% endtabs %}

## Transaction Costs

Understanding transactions is essential to master database performance. If you just remember one sentence on this topic, it should be this one: a write transaction has its price.

Committing a transaction involves syncing data to physical storage, which is a relatively expensive operation for databases. Only when the file system confirms that all data has been stored in a durable manner (not just memory cached), the transaction can be considered successful. This file sync required by a transaction may take a couple of milliseconds. Keep this in mind and try to group several operations (e.g.`put`calls) in one transaction.

Consider this example:

{% tabs %}
{% tab title="Java" %}

```java
// DON'T DO THIS!
for (User user : allUsers) {
    modify(user);
    box.put(user);
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// DON'T DO THIS!
for (user in allUsers) {
    modify(user)
    box.put(user)
}
```

{% endtab %}

{% tab title="Dart" %}

```dart
// DON'T DO THIS!
for (var user in allUsers) {
    modify(user);
    box.put(user);
}
```

{% endtab %}
{% endtabs %}

Do you see the performance issue with that code? An implicit write transaction is created for each user. This will consume a lot of resources, especially for a high number of user objects. It is much more efficient to put all users at once, in a single transaction:

{% tabs %}
{% tab title="Java" %}

```java
for (User user : allUsers) {
    modify(user);
}
box.put(allUsers);
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
for (user in allUsers) {
    modify(user)
}
box.put(allUsers)
```

{% endtab %}

{% tab title="Dart" %}

```dart
for (var user in allUsers) {
    modify(user);
}
box.putMany(allUsers);
```

{% endtab %}
{% endtabs %}

Much better! Even if you would have a database with 1,000 users, the latter example would use a single write transaction to store all user objects at once. The first code example would use 1,000 (!) implicit transactions, causing a massive slow down.

## Read Transactions

In ObjectBox, read transactions are cheap. In contrast to write transactions, there is no commit and thus no expensive sync to the file system. Operations like `get` , `count` , and queries run inside an implicit read transaction if they are not called when already inside an explicit transaction (read or write). Note that it is illegal to `put` when inside a read transaction: an exception will be thrown.

While read transactions are much cheaper than write transactions, there is still some overhead to starting a read transaction. Thus, for a high number of reads (e.g. hundreds, in a loop), you can improve performance by grouping those reads in a single read transaction (see explicit transactions below).

## Multiversion Concurrency

ObjectBox gives developers [Multiversion concurrency control (MVCC)](https://en.wikipedia.org/wiki/Multiversion_concurrency_control) semantics. This allows multiple concurrent readers (read transactions) which can execute immediately without blocking or waiting. This is guaranteed by storing multiple versions of (committed) data. Even if a write transaction is in progress, a read transaction can read the last consistent state immediately. Write transactions are executed sequentially to ensure a consistent state. Thus, it is advised to keep write transactions short to avoid blocking other pending write transactions. For example, it is usually a bad idea to do networking or complex calculations while inside a write transaction. Instead, do any expensive operation and prepare objects before entering a write transaction.

Note that you do not have to worry about making write transactions sequential yourself. If multiple threads want to write at the same time (e.g. via `put` or `runInTx`), one of the threads will be selected to go first, while the other threads have to wait. It works just like a lock or `synchronized` in Java.

### Locking inside a Write Transaction

Avoid locking (e.g. via `synchronized` or `java.util.concurrent.locks`) when inside a write transaction when possible. Because write transactions run exclusively, they effectively acquire a write lock internally. As with all locks, you need to pay close attention when multiple locks are involved. Always obtain locks in the same order to avoid deadlocks. If you acquire a lock “X” inside a transaction, you must ensure that your code does not start another write transaction while having the lock “X”.


# Advanced

Guides and documentation for advanced use cases of ObjectBox.


# Advanced Setup

Additional configuration options when creating an ObjectBox database.

This page contains:

[#objectbox-for-java-advanced-setup](#objectbox-for-java-advanced-setup "mention")

[#objectbox-for-flutter-dart-advanced-setup](#objectbox-for-flutter-dart-advanced-setup "mention")

## ObjectBox for Java - Advanced Setup

This page assumes you have [added ObjectBox ](/getting-started)to your project.

To then change the default behavior of the ObjectBox plugin and processor read on for advanced setup options.

### Manually Add Libraries

The ObjectBox Gradle plugin adds required libraries and the annotation processor to your projects dependencies automatically, but you can also add them manually.

In your app's Gradle build script:

{% tabs %}
{% tab title="Android (Kotlin)" %}

```java
dependencies {
    // All below added automatically by the plugin:
    // Java library
    implementation("io.objectbox:objectbox-java:$objectboxVersion")
    // Kotlin extension functions
    implementation("io.objectbox:objectbox-kotlin:$objectboxVersion")
    // Annotation processor
    kapt("io.objectbox:objectbox-processor:$objectboxVersion")
    // Native library for Android
    implementation("io.objectbox:objectbox-android:$objectboxVersion")
}
```

{% endtab %}

{% tab title="Android (Java)" %}

```java
dependencies {
    // All below added automatically by the plugin:
    // Java library
    implementation("io.objectbox:objectbox-java:$objectboxVersion")
    // Annotation processor
    annotationProcessor("io.objectbox:objectbox-processor:$objectboxVersion")
    // Native library for Android
    implementation("io.objectbox:objectbox-android:$objectboxVersion")
}
```

{% endtab %}

{% tab title="JVM (Java)" %}

<pre class="language-groovy"><code class="lang-groovy">dependencies {
    // All below added automatically by the plugin:
<strong>    // Java library
</strong>    implementation("io.objectbox:objectbox-java:$objectboxVersion")
    // Annotation processor
    annotationProcessor("io.objectbox:objectbox-processor:$objectboxVersion")
    // One of the JVM database libraries required for your system
    implementation("io.objectbox:objectbox-linux:$objectboxVersion")
    implementation("io.objectbox:objectbox-macos:$objectboxVersion")
    implementation("io.objectbox:objectbox-windows:$objectboxVersion")
    // Not added automatically:
    // Since 2.9.0 we also provide ARM support for the Linux library
    implementation("io.objectbox:objectbox-linux-arm64:$objectboxVersion")       
    implementation("io.objectbox:objectbox-linux-armv7:$objectboxVersion")
}
</code></pre>

{% endtab %}

{% tab title="JVM (Kotlin)" %}

```groovy
dependencies {
    // All below added automatically by the plugin:
    // Java library
    implementation("io.objectbox:objectbox-java:$objectboxVersion")
    // Kotlin extension functions
    implementation("io.objectbox:objectbox-kotlin:$objectboxVersion")
    // Annotation processor
    kapt("io.objectbox:objectbox-processor:$objectboxVersion")
    // One of the JVM database libraries required for your system
    implementation("io.objectbox:objectbox-linux:$objectboxVersion")
    implementation("io.objectbox:objectbox-macos:$objectboxVersion")
    implementation("io.objectbox:objectbox-windows:$objectboxVersion")
    // Not added automatically:
    // Since 2.9.0 we also provide ARM support for the Linux library
    implementation("io.objectbox:objectbox-linux-arm64:$objectboxVersion")       
    implementation("io.objectbox:objectbox-linux-armv7:$objectboxVersion")
}
```

{% endtab %}
{% endtabs %}

### Add libraries for distribution

For JVM apps, by default, the ObjectBox Gradle plugin only adds the native (Linux, macOS or Windows) library **required to run on your current system**. If your app wants to support multiple platforms, **manually add all of the required native libraries** listed above when you **distribute** your app.

Note that when the Java API is first used, it will extract the actual database library file (such as a `.dll`, `.dylib` or `.so` file) from the included JAR to the working directory. If your application does not have permission to do so it may be required to package and install the library files directly with your application.

### **Processor Options**

In your app’s Gradle build script, the following processor options, explained below, are available:

{% tabs %}
{% tab title="Android (Kotlin)" %}

```java
kapt {
    arguments {
        arg("objectbox.modelPath", "$projectDir/schemas/objectbox.json")
        arg("objectbox.myObjectBoxPackage", "com.example.custom")
        arg("objectbox.debug", true)
    }
}
```

{% endtab %}

{% tab title="Android (Java)" %}

```java
// Groovy DSL (build.gradle)
android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [ 
                        "objectbox.modelPath" : "$projectDir/schemas/objectbox.json".toString(),
                        "objectbox.myObjectBoxPackage" : "com.example.custom",
                        "objectbox.debug" : "true"
                ]
            }
        }
    }
}

// Kotlin DSL (build.gradle.kts)
android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                arguments.put("objectbox.modelPath", "$projectDir/schemas/objectbox.json")
                arguments.put("objectbox.myObjectBoxPackage", "com.example.custom")
                arguments.put("objectbox.debug", "true")
            }
        }
    }
}
```

{% endtab %}

{% tab title="JVM (Java)" %}

```groovy
// Groovy DSL
tasks.withType(JavaCompile) {
    options.compilerArgs += [ "-Aobjectbox.modelPath=$projectDir/schemas/objectbox.json" ]
    options.compilerArgs += [ "-Aobjectbox.myObjectBoxPackage=com.example.custom" ]
    options.compilerArgs += [ "-Aobjectbox.debug=true" ]
}

// Kotlin DSL
tasks.withType<JavaCompile>() {
    options.compilerArgs.add("-Aobjectbox.modelPath=$projectDir/schemas/objectbox.json")
    options.compilerArgs.add("-Aobjectbox.myObjectBoxPackage=com.example.custom")
    options.compilerArgs.add("-Aobjectbox.debug=true")
}
```

{% endtab %}

{% tab title="JVM (Kotlin)" %}

```groovy
kapt {
    arguments {
        arg("objectbox.modelPath", "$projectDir/schemas/objectbox.json")
        arg("objectbox.myObjectBoxPackage", "com.example.custom")
        arg("objectbox.debug", true)
    }
}
```

{% endtab %}
{% endtabs %}

#### Change the Model File Path

By default, the ObjectBox model file is stored in `module-name/objectbox-models/default.json`. You can change the file path and name by passing the `objectbox.modelPath` argument to the ObjectBox annotation processor.

#### Change the MyObjectBox package

{% hint style="info" %}
Since 1.5.0
{% endhint %}

By default, the MyObjectBox class is generated in the same or a parent package of your entity classes. You can define a specific package by passing the `objectbox.myObjectBoxPackage` argument to the ObjectBox annotation processor.

#### Enable Debug Mode

You can enable debug output for the annotation processor if you encounter issues while setting up your project and entity classes.

In your app’s `build.gradle` file, enable the `objectbox.debug` option and then run Gradle with the `--info` option to see the debug output.

To enable debug mode for the ObjectBox Gradle plugin:

```groovy
// Enable debug output for the plugin
// Groovy DSL
objectbox {
    debug = true
}

// Kotlin DSL
configure<io.objectbox.gradle.ObjectBoxPluginExtension> {
    debug.set(true)
}
```

### Enable DaoCompat mode

ObjectBox can help you migrate from greenDAO by generating classes with a greenDAO-like API.

See the [DaoCompat documentation](http://greenrobot.org/greendao/documentation/objectbox-compat/) on how to enable and use this feature.

## ObjectBox for Flutter/Dart - Advanced Setup

### Change the generated files directory

To customize the directory (relative to the package root) where the files generated by ObjectBox are written, add the following to your `pubspec.yaml`:

```
objectbox:
  # Writes objectbox-model.json and objectbox.g.dart to lib/custom (and test/custom).
  output_dir: custom
  # Or optionally specify the lib and test output folder separately.
  # output_dir:
  #   lib: custom
  #   test: other
```


# Object IDs

Explanation of Object IDs and how they are used and assigned in ObjectBox.

## ObjectBox - Object IDs

**Objects must have an ID property of type `long`.** You are free to use the wrapper type `java.lang.Long`, but we advise against it in most cases. `long` IDs are enforced to make ObjectBox very efficient internally.

If your application requires other ID types (such as a string UID given by a server), you can model them as standard properties and use [queries](/queries) to look up entities by your application-specific ID.

## Object ID: new vs. persisted entities

When you create new entity objects (on the language level), they are not persisted yet and their ID is (zero). Once an entity is put (persisted), ObjectBox will assign an ID to the entity. You can access the ID property right after the call to `put()`.

Those are also applied the other way round: ObjectBox uses the **ID as a state indication** of whether an entity is new (zero) or already persisted (non-zero). This is used internally, e.g. for relations that heavily rely on IDs.

## Special Object IDs

Object IDs may be any `long` value, with two exceptions:

* **0 (zero):** Objects with an ID of zero (and `null` if the ID is of type `Long`) are considered new (not persisted before). Putting such an object will always insert a new object and assign an unused ID to it.
* **0xFFFFFFFFFFFFFFFF (-1 in Java):** This value is reserved for internal use by ObjectBox and may not be used by the app.

## Object ID assignment (default)

By default, object IDs are assigned by ObjectBox. For each new object, ObjectBox will assign an unused ID that is above the current highest ID value used in a box. For example, if there are two objects with ID 1 and ID 100 in a box the next object that is put will be assigned ID 101.

Also, note that this will mean in some circumstances IDs from deleted objects may be reused. So it is best to not rely on a specific ID getting assigned.

**By default, only ObjectBox may assign IDs.** If you try to put an object with an ID greater than the currently highest ID, ObjectBox will throw an error.

## Manually assigned Object IDs

{% hint style="info" %}
This is not recommended. Check if using a [unique indexed property](/entity-annotations#object-ids-id) is a viable alternative.
{% endhint %}

If your code **needs to assign IDs by itself** you can change the `@Id` annotation to:

{% tabs %}
{% tab title="Java" %}

```java
@Id(assignable = true)
long id;
```

{% endtab %}

{% tab title="Dart" %}

```dart
@Id(assignable: true)
int id = 0;
```

{% endtab %}
{% endtabs %}

This will allow putting an entity with any valid ID (see [#special-object-ids](#special-object-ids "mention")). If the `@Id` field is writable, it can still be set to zero to let ObjectBox auto-assign a new ID.

{% hint style="warning" %}
**Warning:** manually assigning IDs breaks automatic state detection (new vs. persisted entity based on the ID). Therefore, **entities with manually assigned IDs should be put immediately and the Box may have to be attached manually**, especially when working with relations.

For details see the documentation about [updating relations](/relations#updating-relations).
{% endhint %}

## String ID alias (future work)

Check [this issue](https://github.com/objectbox/objectbox-java/issues/167) on Github for status.


# Custom Types

How to store types that are not built-in, recommendations for storing enums, and using property converters.

ObjectBox supports a wide range of [built-in property types](/property-types). For types that aren't directly supported, you can use converters to map them to a built-in type.

## Flex property converters

In ObjectBox for Java/Kotlin, [Flex properties](/property-types#flex-properties) use converters internally. You can exchange converters to fine-tune the conversion process. To **override the default converter**, use `@Convert`. For example to use another built-in `FlexObjectConverter` subclass:

{% tabs %}
{% tab title="Java" %}

<pre class="language-java"><code class="lang-java">// StringLongMapConverter restores any integers always as Long
@Convert(converter = StringLongMapConverter.class, dbType = byte[].class)
<strong>@Nullable Map&#x3C;String, Object> stringMap;
</strong></code></pre>

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// StringLongMapConverter restores any integers always as Long
@Convert(converter = StringLongMapConverter::class, dbType = ByteArray::class)
var stringMap: MutableMap<String, Any>? = null
```

{% endtab %}
{% endtabs %}

You can also write a custom converter like shown below.

## Convert annotation and property converter

To add support for a custom type, you need to provide a conversion to one of the ObjectBox built-in types. For example, you could define a color in your entity using a custom `Color` class and map it to an `Integer`. Or you can map the popular `org.joda.time.DateTime` from Joda Time to a `Long`.

Here is an example mapping an `enum` to an integer:

{% tabs %}
{% tab title="Java" %}

```java
@Entity
public class User {
    @Id
    public long id;
    
    @Convert(converter = RoleConverter.class, dbType = Integer.class)
    public Role role;
    
    public enum Role {
        DEFAULT(0), AUTHOR(1), ADMIN(2);
        
        final int id;
        
        Role(int id) {
            this.id = id;
        }
    }

    public static class RoleConverter implements PropertyConverter<Role, Integer> {
        @Override
        public Role convertToEntityProperty(Integer databaseValue) {
            if (databaseValue == null) {
                return null;
            }
            for (Role role : Role.values()) {
                if (role.id == databaseValue) {
                    return role;
                }
            }
            return Role.DEFAULT;
        }
    
        @Override
        public Integer convertToDatabaseValue(Role entityProperty) {
            return entityProperty == null ? null : entityProperty.id;
        }
    }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
@Entity
data class User(
        @Id
        var id: Long = 0,
        @Convert(converter = RoleConverter::class, dbType = Int::class)
        var role: Role? = null
)

enum class Role(val id: Int) {
    DEFAULT(0), AUTHOR(1), ADMIN(2);
}

class RoleConverter : PropertyConverter<Role?, Int?> {
    override fun convertToEntityProperty(databaseValue: Int?): Role? {
        if (databaseValue == null) {
            return null
        }
        for (role in Role.values()) {
            if (role.id == databaseValue) {
                return role
            }
        }
        return Role.DEFAULT
    }

    override fun convertToDatabaseValue(entityProperty: Role?): Int? {
        return entityProperty?.id
    }
}
```

{% endtab %}

{% tab title="Dart" %}

```dart
enum Role {
  unknown,
  author,
  admin
}

@Entity()
class User {
  int id;

  // The Role type is not supported by ObjectBox.
  // So ignore this field...
  @Transient()
  Role? role;

  // ...and define a field with a supported type,
  // that is backed by the role field.
  int? get dbRole {
    _ensureStableEnumValues();
    return role?.index;
  }

  set dbRole(int? value) {
    _ensureStableEnumValues();
    if (value == null) {
      role = null;
    } else {
      role = Role.values[value]; // throws a RangeError if not found

      // or if you want to handle unknown values gracefully:
      role = value >= 0 && value < Role.values.length
          ? Role.values[value]
          : Role.unknown;
    }
  }

  User(this.id);

  void _ensureStableEnumValues() {
    assert(Role.unknown.index == 0);
    assert(Role.author.index == 1);
    assert(Role.admin.index == 2);
  }
}
```

{% endtab %}
{% endtabs %}

### Things to look out for

If you define your custom type or converter **inside a Java or Kotlin entity class**, it must be **static or respectively not an inner class**.

Don’t forget to **handle null values** correctly – usually, you should return null if the input is null.

Database types in the sense of the converter are the primitive (built-in) types offered by ObjectBox, as mentioned in the beginning. It is recommended to **use a primitive type that is easily convertible** (int, long, byte array, String, …).

You must **not interact with the database** (such as using `Box` or `BoxStore`) inside the converter. The converter methods are called within a transaction, so for example, getting or putting entities to a box will fail.

{% hint style="info" %}
Note: For optimal performance, **ObjectBox will use a single converter instance** for all conversions. Make sure the converter does not have any other constructor besides the parameter-less default constructor. Also, make it thread-safe, because it might be called concurrently on multiple entities.
{% endhint %}

## List/Array types

You can use a converter with List types. For example, you could convert a List of Strings to a JSON array resulting in a single string for the database. At the moment it is not possible to use an array with converters (you can track this [feature request](https://github.com/greenrobot/ObjectBox/issues/42)).

{% hint style="info" %}
ObjectBox has [built-in support for many list and array types](/property-types#lists-and-arrays), including String lists/arrays and typed numeric arrays.
{% endhint %}

## How to convert Enums correctly

Enums are popular with data objects like entities. When persisting enums, there are a couple of best practices:

* **Do not persist the enum’s ordinal or name:** Both are unstable, and can easily change the next time you edit your enum definitions.
* **Use stable ids:** Define a custom property (integer or string) in your enum that is guaranteed to be stable. Use this for your persistence mapping.
* **Prepare for the unknown:** Define an UNKNOWN enum value. It can serve to handle null or unknown values. This will allow you to handle cases like an old enum value getting removed without crashing your app.

## Custom types in queries

`QueryBuilder` is unaware of custom types. You have to **use the primitive DB type for queries**.

So for the Role example above you would get users with the role of admin with the query condition `.equal(UserProperties.Role, 2)`.


# Entity Inheritance

How to inherit properties from entity super classes.

## ObjectBox - Entity Super Classes

{% hint style="info" %}
Only available for Java/Kotlin at the moment
{% endhint %}

ObjectBox allows entity inheritance to share persisted properties in super classes. The base class can be an entity or non-entity class. For this purpose the `@Entity` annotation is complemented by the `@BaseEntity` annotation. There are three types of super classes, which are defined via annotations:

* **No annotation:** The base class and its properties are not considered for persistence.
* **@BaseEntity:** Properties are considered for persistence in sub classes, but the base class itself cannot be persisted.
* **@Entity:** Properties are considered for persistence in sub classes, and the base class itself is a normally persisted entity.

For example:

{% tabs %}
{% tab title="Java" %}

```java
// Superclass:
@BaseEntity
public abstract class Base {
    
    @Id long id;
    String baseString;
    
    public Base() {
    }
    
    public Base(long id, String baseString) {
        this.id = id;
        this.baseString = baseString;
    }
}

// Subclass:
@Entity
public class Sub extends Base {
    
    String subString;
    
    public Sub() {
    }
    
    public Sub(long id, String baseString, String subString) {
        super(id, baseString);
        this.subString = subString;
    }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// Note: Kotlin data classes do not support inheritance,
// so this example uses regular Kotlin classes.

// Superclass:
@BaseEntity
abstract class Base {
    @Id
    var id: Long = 0
    var baseString: String? = null

    constructor()
    constructor(id: Long, baseString: String?) {
        this.id = id
        this.baseString = baseString
    }
}

// Subclass:
@Entity
class Sub : Base {
    var subString: String? = null

    constructor()
    constructor(id: Long, 
                baseString: String?,
                subString: String?) : super(id, baseString) {
        this.subString = subString
    }
}
```

{% endtab %}
{% endtabs %}

The model for Sub, Sub\_, will now include all properties: `id` , `baseString` and `subString` .

It is also possible to inherit properties from another entity:

{% tabs %}
{% tab title="Java" %}

```java
// Entities inherit properties from super entities.
@Entity
public class SubSub extends Sub {
    
    String subSubString;
    
    public SubSub() {
    }
    
    public SubSub(long id, String baseString,
                  String subString, String subSubString) {
        super(id, baseString, subString);
        this.subSubString = subSubString;
    }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// Entities inherit properties from super entities.
@Entity
class SubSub : Sub {
    var subSubString: String? = null

    constructor()
    constructor(id: Long,
                baseString: String?,
                subString: String?,
                subSubString: String?) : super(id, baseString, subString) {
        this.subSubString= subSubString
    }
}
```

{% endtab %}
{% endtabs %}

## Notes on usage

* It is possible to have classes in the inheritance chain that are not annotated with @BaseEntity. Their properties will be ignored and will not become part of the entity model.
* It is not generally recommend to have a base entity class consisting of an ID property only. E.g. Java imposes an additional overhead to construct objects with a sub class.
* Depending on your use case using interfaces may be more straightforward.

## Restrictions

* Superclasses annotated with **@BaseEntity can not be part of a library**.
* There are **no polymorphic queries** (e.g. you cannot query for a base class and expect results from sub classes).
* Currently any superclass, whether it is an @Entity or @BaseEntity, **can not have any relations** (like a ToOne or ToMany property).

{% tabs %}
{% tab title="Java" %}

```java
// THIS DOES NOT WORK
@BaseEntity
public abstract class Base {
    @Id long id;
    ToOne<OtherEntity> other; 
    ToMany<OtherEntity> others; 
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// THIS DOES NOT WORK
@BaseEntity
abstract class Base {
    @Id
    var id: Long = 0
    lateinit var other: ToOne<OtherEntity>
    lateinit var others: ToMany<OtherEntity>
}
```

{% endtab %}
{% endtabs %}


# Data Model Updates

How to rename entities and properties, change property types in ObjectBox.

## ObjectBox - Data Model Updates

ObjectBox manages its data model (schema) mostly automatically. The data model is defined by the entity classes you define. When you **add or remove** entities or properties of your entities, **ObjectBox takes care** of those changes without any further action from you.

For other changes like **renaming or changing the type**, ObjectBox needs **extra information** to make things unambiguous. This is done by setting a unique ID (UIDs) as an annotation, as we will see below.

## UIDs

ObjectBox keeps track of entities and properties by assigning them unique IDs (UIDs). All those UIDs are stored in a file `objectbox-models/default.json` (Java, Kotlin) or `lib/objectbox-model.json` (Dart) which you should add to your version control system (e.g. git). If you are interested, we have [in-depth documentation on UIDs and concepts](/advanced/meta-model-ids-and-uids). But let’s continue with how to rename entities or properties.

**In short:** To make UID-related changes, put an `@Uid` annotation (Java, Kotlin) or `@Entity(uid: 0)/@Property(uid: 0)` (Dart) on the entity or property and build the project to get further instructions. Repeat for each entity or property to change.

## Renaming Entities and Properties

So why do we need that UID annotation? If you simply rename an entity class, ObjectBox only sees that the old entity is gone and a new entity is available. This can be interpreted in two ways:

* The old entity is removed and a new entity should be added, the old data is discarded. This is the **default behavior** of ObjectBox.
* The entity was renamed, the old data should be re-used.

So to tell ObjectBox to do a rename instead of discarding your old entity and data, you need to make sure it knows that this is the same entity and not a new one. You do that by attaching the internal UID to the entity.

The same is true for properties.

Now let’s walk through how to do that. The process works the same if you want to rename a property:

### How-to and Example (Java/Kotlin and Dart)

**Step 1:** Add an empty UID to the entity/property you want to rename:

{% tabs %}
{% tab title="Java/Kotlin" %}

```java
@Entity
@Uid
public class MyName { ... }
```

{% endtab %}

{% tab title="Dart" %}

```dart
@Entity(uid: 0)
class MyName { ... }
```

{% endtab %}
{% endtabs %}

**Step 2:** Build the project (in Dart, run `pub run build_runner build`). The build will fail with an error message that gives you the current UID of the entity/property:

{% tabs %}
{% tab title="Java/Kotlin" %}

```
error: [ObjectBox] UID operations for entity "MyName": 
  [Rename] apply the current UID using @Uid(6645479796472661392L) -
  [Change/reset] apply a new UID using @Uid(4385203238808477712L)
```

{% endtab %}

{% tab title="Dart" %}

```
@Entity(uid: 0) found on "MyName" - you can choose one of the following actions:
    [Rename] apply the current UID using @Entity(uid: 6645479796472661392)
    [Change/reset] apply a new UID using @Entity(uid: 4385203238808477712)
```

{% endtab %}
{% endtabs %}

**Step 3:** Apply the UID from the \[Rename] section of the error message to your entity/property:

{% tabs %}
{% tab title="Java/Kotlin" %}

```java
@Entity
@Uid(6645479796472661392L)
public class MyName { ... }
```

{% endtab %}

{% tab title="Dart" %}

```dart
@Entity(uid: 6645479796472661392)
class MyName { ... }
```

{% endtab %}
{% endtabs %}

**Step 4:** The last thing to do is the actual rename on the language level (Java, Kotlin, etc.):

{% tabs %}
{% tab title="Java/Kotlin" %}

```java
@Entity
@Uid(6645479796472661392L)
public class MyNewName { ... }
```

{% endtab %}

{% tab title="Dart" %}

```dart
@Entity(uid: 6645479796472661392)
class MyNewName { ... }
```

{% endtab %}
{% endtabs %}

**Step 5:** Build the project again, it should now succeed. You can now use your renamed entity/property as expected and all existing data will still be there.

**Repeat** the steps above to rename another entity or property.

{% hint style="info" %}
Note: Instead of the above you can also find the UID of the entity/property in the model JSON mentioned in the introduction. You can add the UID value to the annotation yourself, before renaming the entity/property, and skip the intermediate error where ObjectBox just prints it for you. This can be **faster when renaming multiple properties**.
{% endhint %}

### How-to and Example (Python)

Since in Python there isn't a build step, the steps for renaming an Entity are different. Say we want to rename the Entity `MyName` to `MyNewName`:

```python
@Entity()
class MyName:
    id = Id
    some_property = Int
```

**Step 1**: Find out the UID of the entity `MyName`:

<pre class="language-python"><code class="lang-python"><strong>print(MyName._uid)
</strong># 6645479796472661392
</code></pre>

**Step 2**: Rename `MyName` to `MyNewName` and explicitly specify the old UID:

```python
@Entity(uid=6645479796472661392)
class MyNewName:
    id = Id
    some_property = Int
```

This makes possible for Objectbox to associate the old entity to the new one, and retain persisted data. If you don't specify the old UID, Objectbox will discard the old data and add a fresh new entity called `MyNameName` to the schema.

## Changing Property Types

{% hint style="warning" %}
ObjectBox does not support migrating existing property data to a new type. You will have to take care of this yourself, e.g. by keeping the old property and adding some migration logic.
{% endhint %}

There are two solutions to changing the type of a property:

* Add a new property with a different name (this only works if the property has no @Uid annotation already):

```java
// old:
String year;
// new:
int yearInt;
```

* Set a new UID for the property so ObjectBox treats it as a new property. Let’s walk through how to do that:

### How-to and Example

**Step 1:** Add the `@Uid` annotation to the property where you want to change the type:

{% tabs %}
{% tab title="Java/Kotlin" %}

```java
@Uid
String year;
```

{% endtab %}

{% tab title="Dart" %}

```dart
@Property(uid: 0)
String year;
```

{% endtab %}
{% endtabs %}

**Step 2:** Build the project. The build will fail with an error message that gives you a newly created UID value:

{% tabs %}
{% tab title="Java/Kotlin" %}

```
error: [ObjectBox] UID operations for property "MyEntity.year": 
  [Rename] apply the current UID using @Uid(6707341922395832766L) -
  [Change/reset] apply a new UID using @Uid(9204131405652381067L)
```

{% endtab %}

{% tab title="Dart" %}

```
@Property(uid: 0) found on "year" - you can choose one of the following actions:
    [Rename] apply the current UID using @Property(uid: 6707341922395832766)
    [Change/reset] apply a new UID using @Property(uid: 9204131405652381067)
```

{% endtab %}
{% endtabs %}

**Step 3:** Apply the UID from the \[Change/reset] section to your property:

{% tabs %}
{% tab title="Java/Kotlin" %}

```java
@Uid(9204131405652381067L)
int year;
```

{% endtab %}

{% tab title="Dart" %}

```dart
@Property(uid: 9204131405652381067)
String year;
```

{% endtab %}
{% endtabs %}

**Step 4:** Build the project again, it should now succeed. You can now use the property in your entity as if it were a new one.

**Repeat** the steps above to change the type of another property.


# Meta Model, IDs, and UIDs

Explaining the ObjectBox meta model file and how to resolve model file conflicts.

Unlike relational databases like SQLite, ObjectBox does not require you to create a database schema. That does not mean ObjectBox is schema-less. For efficiency reasons, ObjectBox manages a **meta model** of the data stored. This meta model is actually ObjectBox’s equivalent of a schema. It includes known object types including all properties, indexes, etc. A key difference to relational schemas is that ObjectBox tries to manage its meta model automatically. In some cases it needs your help. That’s why we will look at some details.

## IDs

In the ObjectBox meta model, everything has an ID and a UID. IDs are used internally in ObjectBox to reference entities, properties, and indexes. For example, you have an entity “User” with the properties “id” and “name”. In the meta model the entity (type) could have the ID 42, and the properties the IDs 1 and 2. Property IDs must only be unique within their entity.

{% hint style="info" %}
Note: do not confuse object IDs with meta model IDs: object IDs are the values of the @Id property (see Object IDs in [basics](/getting-started)). In contrast, all objects are instances of the entity type associated with a single meta model ID.
{% endhint %}

ObjectBox assigns meta model IDs sequentially (1, 2, 3, 4, …) and keeps track of the last used ID to prevent ID collisions.

## UIDs

As a rule of thumb, for each meta model ID there’s a corresponding UID. They complement IDs and are often used in combination (e.g. in the JSON file). While IDs are assigned sequentially, UIDs are a random long value. The job of UIDs is detecting and resolving concurrent modifications of the meta model.

A UID is unique across entities, properties, indexes, etc. Thus unlike IDs, an UID already used for an entity may not be used for a property. As a precaution to avoid side effects, ObjectBox keeps track of “retired” UIDs to ensure previously used but now abandoned UIDs are not used for new artifacts.

## JSON for consistent IDs

ObjectBox stores a part of its meta model in a JSON file. This file should be available to every developer and thus checked into a source version control system (e.g. git). The main purpose of this JSON file is to ensure consistent IDs and UIDs in the meta model across devices.

This JSON file is stored in

* `objectbox-models/default.json` for Android or Java projects,
* `lib/objectbox-model.json` for Dart or Flutter projects.

For example, look at a file from the [ObjectBox example project](https://github.com/objectbox/objectbox-examples):

```java
{
  "_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.",
  "_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.",
  "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
  "entities": [
    {
      "id": "1:6645479796472661392",
      "lastPropertyId": "5:8452815412120793251",
      "name": "Note",
      "properties": [
        {
          "id": "1:9211738071025439652",
          "name": "id",
          "type": 6,
          "flags": 1
        },
        {
          "id": "2:8804670454579230281",
          "name": "text",
          "type": 9
        },
        {
          "id": "4:1260602348787983453",
          "name": "date",
          "indexId": "1:9194998581973594219",
          "type": 10,
          "flags": 8
        },
        {
          "id": "5:8452815412120793251",
          "name": "authorId",
          "indexId": "2:5206397068544851461",
          "type": 11,
          "flags": 520,
          "relationTarget": "Author"
        }
      ],
      "relations": []
    },
    {
      "id": "2:8096888707432154961",
      "lastPropertyId": "2:388290850215565763",
      "name": "Author",
      "properties": [
        {
          "id": "1:2456363380762264329",
          "name": "id",
          "type": 6,
          "flags": 1
        },
        {
          "id": "2:388290850215565763",
          "name": "name",
          "type": 9
        }
      ],
      "relations": []
    }
  ],
  "lastEntityId": "2:8096888707432154961",
  "lastIndexId": "2:5206397068544851461",
  "lastRelationId": "0:0",
  "lastSequenceId": "0:0",
  "modelVersion": 5,
  "modelVersionParserMinimum": 5,
  "retiredEntityUids": [],
  "retiredIndexUids": [],
  "retiredPropertyUids": [],
  "retiredRelationUids": [],
  "version": 1
}
```

As you can see, the “id” attributes combine the ID and UID using a colon. This protects against faulty merges. When applying the meta model to the database, ObjectBox will check for consistent IDs and UIDs.

## Meta Model Synchronization

At build time, ObjectBox gathers meta model information from the entities (@Entity classes) and the JSON file. The complete meta model information is written into the generated class `MyObjectBox`.

Then, at runtime, the meta model assembled in MyObjectBox is synchronized with the meta model inside the ObjectBox database (file). UIDs are the primary keys to synchronize the meta model with the database. The synchronization involves a couple of consistency checks that may fail when you try to apply illegal meta data.

## Stable Renames using UIDs

At some point you may want to rename an entity class or just a property. Without further information, ObjectBox will remove the old entity/property and add a new one with the new name. This is actually a valid scenario by itself: removing one property and adding another. To tell ObjectBox it should do a rename instead, you need to supply the property's previous UID.

Add an `@Uid` annotation without any value to the entity or property you want to rename and trigger a project build. The build will fail with a message containing the UID you need to apply to the `@Uid` annotation.

Also check out this [how-to guide](/advanced/data-model-updates) for hands-on information on renaming and resetting.

## Resolving Meta Model Conflicts

In the section on UIDs, we already hinted at the possibility of meta model conflicts. This means the meta model defined in the `objectbox-models/default.json` file conflicts with the model of the existing database file.

When creating a Store the meta model is passed and verified against the database file. If a conflict exists an exception or error is thrown, for example:

```
// If lastEntityId in the model file is incorrect:
io.objectbox.exception.DbSchemaException:
 DB's last entity ID 4 is higher than 3 from model

// If the UID of an entity in the model file has changed:
io.objectbox.exception.DbSchemaException:
 Incoming entity ID 1:6645479796472661392 does not match existing UID 8096888707432154961
```

Such a conflict can be caused by

* developers changing the meta model at the same time (e.g. in different version control system branches), typically by adding entity classes or properties.
* the `objectbox-models/default.json` model file getting accidentally deleted, it is then generated on the next build with a new set of UIDs.
* a database file with a different model getting restored by [Android Auto Backup](https://developer.android.com/guide/topics/data/autobackup) or getting [supplied as the initial database file](https://objectbox.io/docfiles/java/current/io/objectbox/BoxStoreBuilder.html#initialDbFile\(java.io.File\)).

There are basically two ways to resolve this:

* Resolve conflicts in or reconstruct the `default.json` meta model file to match the model of an existing database file.
* Keep or create a fresh `default.json` model file and delete the database file. However, this will lose all existing data.

The two options are explained in detail below.

### Manual conflict resolution

Usually, it is preferred to edit the JSON file to resolve conflicts and fix the meta model. This involves the following steps:

* **Ensure IDs are unique:** in the JSON file the id attribute has values in the format “ID:UID”. If you have duplicate IDs after a VCS merge, you should assign a new ID (keep the UID part!) to one of the two. Typically, the new ID would be “last used ID + 1”.
* **Update last ID values:** for entities, update the attribute `lastEntityId`; for properties, update the attribute `lastPropertyId` of the enclosing entity back to the ID:UID with the ID indicated by the error message.
* **Check for other ID references:** do a text search for the UID and check if the ID part is correct for all UID occurrences

To illustrate this with an example, let's assume the last assigned entity ID was 41. Thus the next entity ID will be 42. Now, the developers Alice and Bob add a new entity without knowing of each other. Alice adds a new entity “Ant” which is assigned the entity ID 42. At the same time, Bob adds the entity “Bear” which is also assigned the ID 42. After both developers committed their code, the ID 42 does not unique identify an entity type (“Ant” or “Bear”?). Furthermore, in Alice’s ObjectBox the entity ID 42 is already wired to “Ant” while Bob’s ObjectBox maps 42 to “Bear”. UIDs make this situation resolvable. Let’s say the UID is 12345 for “Ant” and 9876 for “Bear”. Now, when Bob pulls Alice’s changes, he is able to resolve the conflict. He manually assigns the entity ID 43 to “Bear” and updates the lastEntityId attribute accordingly to “43:9876” (ID:UID). After Bob commits his changes, both developers are able to continue with their ObjectBox files.

### The Nuke Option

During initial development, it may be an option to just delete the meta model and all databases. This will cause a fresh start for the meta model, e.g. all UIDs will be regenerated. Follow these steps:

* Delete the JSON file (objectbox-models/default.json)
* Build the project to generate a new JSON file from scratch
* Commit the recreated JSON file to your VCS (e.g. git)
* **Delete all previously created ObjectBox databases** (e.g. for Android, delete the app’s data or uninstall the app)

While this is a simple approach, it has its obvious disadvantages. For example for a published app, all existing data would be lost.


# Changelogs

The changelogs for each programming language are available at GitHub:

* [Java changelog (GitHub releases)](https://github.com/objectbox/objectbox-java/releases)
* [Dart changelog](https://github.com/objectbox/objectbox-dart/blob/main/objectbox/CHANGELOG.md)
* [Python changelog (GitHub releases)](https://github.com/objectbox/objectbox-python/releases)

For other languages, please check there respective doc pages:

* [C and C++](https://cpp.objectbox.io/)
* [Swift](https://swift.objectbox.io/)
* [Go](https://golang.objectbox.io/)

## Changelog History before 4.0.3

Older entries (4.0.2 and earlier) are available in the [release history](/release-history).


# FAQ

Answers to questions specific to ObjectBox for Java and Dart

## **Does ObjectBox support Kotlin? RxJava?**

ObjectBox comes with full [Kotlin support](https://docs.objectbox.io/kotlin-support) including data classes. And yes, it supports [RxJava and reactive queries without RxJava](https://docs.objectbox.io/data-observers-and-rx).

## **Does ObjectBox support object relations?**

Yes. ObjectBox comes with strong relation support and offers features like “eager loading” for optimal performance.

## **Does ObjectBox support multi-module projects? Can entities be spread across modules?**

The ObjectBox Gradle plugin only looks for entities in the current module, it does not search library modules. However, you can have a separate database (`MyObjectBox` file) for each module. Just make sure to pass different database names when building your BoxStore.

## **Is ObjectBox a “zero copy” database? Are properties fetched lazily?**

It depends. Internally and in [the C API](https://github.com/objectbox/objectbox-c), ObjectBox does zero-copy reads. Java objects require a single copy only. However, copying data is only a minor factor in overall performance. In ObjectBox, objects are POJOs (plain objects), and all properties will be properly initialized. Thus, there is no run time penalty for accessing properties and values do not change in unexpected ways when the database updates.

## **Are there any threading constrictions?**

No. The objects you get from ObjectBox are POJOs (plain objects). You are safe to pass them around in threads.

## Should I use ObjectBox on the main thread (or UI thread)?

It depends. In most cases no IO operations (which is what ObjectBox does) should be run on the main thread. This avoids (even rare) hangs of your app.

However, in some cases it might be alright. While ObjectBox and the underlying OS and file system can give no hard guarantees, reading (e.g. Box.get(id)) small amounts of data is typically very fast and should have no notable impact on observed performance of your app. This is because in ObjectBox reads, unlike writes, are not blocked by other operations.

## **On which platforms does ObjectBox run?**

ObjectBox supports **Android 5.0 (API level 21)** or newer and works on most device architectures (armeabi-v7a, arm64-v8a, x86 and x86\_64). An Android library is available for Java (also Kotlin) and Flutter projects.

ObjectBox supports **iOS 12** or newer on **64-bit devices** only. An iOS library is available for [Flutter](https://pub.dev/packages/objectbox) or [Swift](https://objectbox.io/swift-database-for-ios/) projects.

ObjectBox also runs on Linux (x86\_64, arm64, armv7), Windows (x86\_64) and macOS 10.15 or newer (x86\_64, Apple M1) with support for [Kotlin, Java, Dart, Flutter, Go, C, Swift and Python](https://objectbox.io/offline-first-mobile-database/).

## **Can I use ObjectBox on the desktop/server?**

Yes, you can ObjectBox on the desktop/server side. Contact us for details if you are interested in running ObjectBox in client/server mode or containerized!

## **Can I use ObjectBox on smart IoT devices?**

Yes. You can run the ObjectBox database on any IoT device that runs Linux. We also offer Go and C APIs. Check our [cross-platform tutorial](https://objectbox.io/cross-platform-sync-example/) and see how easy it is to sync data across platforms in real time with ObjectBox.

## **How do I rename object properties or classes?**

If you only do a rename on the language level, ObjectBox will by default remove the old and add a new entity/property. [To do a rename, you must specify the UID](https://docs.objectbox.io/advanced/data-model-updates).

## **How much does ObjectBox add to my APK size?**

The Google Play **download size** increases by around 2.3 MB (checked for ObjectBox 3.0.0) as a native library for each [supported architecture](#on-which-platforms-does-objectbox-run) is packaged. If you [build multiple APKs](https://developer.android.com/studio/build/configure-apk-splits) split by ABI or use [Android App Bundle](https://developer.android.com/guide/app-bundle/) it only increases around 0.6 MB.

{% hint style="info" %}
Tip: Open your APK or AAB in Android Studio and have a look at the lib folder to see the raw file size and download size added.
{% endhint %}

When building with minimum API level 23 (Android 6.0), the **raw file (APK or AAB) size** increases more, by around 6.1 MB. This is because the Android Plugin adds `extractNativeLibs="false"` to your `AndroidManifest.xml` [as recommended by Google](https://developer.android.com/topic/performance/reduce-apk-size#extract-false). This turns off compression. However, this allows Google Play to optimally compress APKs before downloading them to each device (see download size above) and reduces the size of your app updates (on Android 6.0 or newer). [Read this Android developers post for details](https://medium.com/androiddevelopers/smallerapk-part-8-native-libraries-open-from-apk-fc22713861ff). It also avoids issues that might occur when extracting the libraries.

If you rather have a smaller APK/App Bundle instead of smaller app downloads and updates (e.g. when distributing in other stores) you can override the flag in your `AndroidManifest.xml`:

{% hint style="info" %}
This is also [currently recommended](https://docs.flutter.dev/deployment/android#building-the-app-for-release) in any case **when building a Flutter** app with minimum API level 23 (Android 6.0).
{% endhint %}

```
<application
    ...
    // not recommended for non-Flutter apps, increases app update size
    android:extractNativeLibs="true"
    tools:replace="android:extractNativeLibs"
    ...   
</applicaton>
```

More importantly, ObjectBox adds little to the APK method count since it’s mostly written in native code.

## **Can I ship my app with a pre-built database?**

Yes. ObjectBox stores all data in a single database file. Thus, you just need to prepare a database file and copy it to the correct location on the first start of your app (before you touch ObjectBox’s API).

{% hint style="info" %}
For Java, there is an **experimental** `initialDbFile()` method when building BoxStore. [Let us know](https://github.com/objectbox/objectbox-java/issues/310) if this is useful!
{% endhint %}

The database file is called `data.mdb` and is typically located in a subdirectory called `objectbox` (or any name you passed to BoxStoreBuilder). On Android, the DB file is located inside the app’s files directory inside `objectbox/objectbox/`. Or `objectbox/<yourname>` if you assigned the custom name `<yourname>` using BoxStoreBuilder.

## How does ObjectBox use disk space? And, can I reclaim disk space?

In most cases, ObjectBox uses disk space quite optimally. Only once you add more data, the database file grows as required. When you delete data, file areas are marked as unused internally and will be reused by ObjectBox. Note that re-using existing file areas is much more efficient than shrinking and growing the file. In practice, once used file storage will be used again in the future; especially considering that stored data has the tendency to get more over time.

ObjectBox relies on multi-version concurrency storage based on "copy on write". This allows e.g. to read the previous state while a write transaction is active. A counter-intuitive consequence is that deleting data can actually increase disk usage because the old data is still referenced. But of course, forthcoming transactions can reuse the internally reclaimed space.

{% hint style="info" %}
The storage layout on disk is optimized for performance. Database structures and concepts like B+ trees, multi-version concurrency and indexes use more space than storing data e.g. in a text file. Advantages like scalable data operations easily make up for it. Also keep in mind that in many cases data stored in a database is a small proportion compared to media files.
{% endhint %}

Non-standard use cases may require a temporary peak in data storage space that is followed by a permanent drop of storage space. To reclaim disk space for those cases, you need to delete the database files and restore them later; e.g. from the cloud or from a second store, which you set up to put the objects you want to keep.

{% hint style="warning" %}
Deleting the database files deletes the contained data permanently. If you want to restore old data, it's your responsibility to backup and restore.
{% endhint %}

While we don't recommend deleting the entire database, the API offers some methods to do so: first, `close()` the `BoxStore` and then delete the database files using `BoxStore.deleteAllFiles(objectBoxDirectory)`. To avoid having to close `BoxStore` delete files before building it, e.g. during app start-up.

```java
// If BoxStore is in use, close it first.
store.close();

BoxStore.deleteAllFiles(new File(BoxStoreBuilder.DEFAULT_NAME));

// TODO Build a new BoxStore instance.
```

{% hint style="info" %}
`BoxStore.removeAllObjects()` does not reclaim disk space. It keeps the allocated disk space so it returns fast and to avoid the performance hit of having to allocate the same disk space when data is put again.
{% endhint %}

## **Answers to other questions**

{% content-ref url="/pages/-LStL89QLKScKNmzP29c" %}
[Troubleshooting](/troubleshooting)
{% endcontent-ref %}

Questions that apply to all supported platforms and languages are answered in the [general ObjectBox FAQ](https://objectbox.io/faq/).

If you believe to have found a bug or missing feature, please create an issue.

For Java/Kotlin: <https://github.com/objectbox/objectbox-java/issues>

For Flutter/Dart: <https://github.com/objectbox/objectbox-dart/issues>

If you have a usage question regarding ObjectBox, please post on Stack Overflow. <https://stackoverflow.com/questions/tagged/objectbox>


# Troubleshooting

Solutions for common issues with ObjectBox for Java and Dart

## **Unresolved reference: MyObjectBox (class not found, etc.)**

MyObjectBox is a generated class, so make sure the project builds successfully. Resolve any other build errors.

Check that the [project is configured correctly](/getting-started#adding-objectbox-to-your-android-project). E.g. for Kotlin, check that the kapt plugin is applied (`apply plugin: 'kotlin-kapt'`) before the ObjectBox plugin.

`android-apt` is known to cause problems, try to remove it.

## **Merge conflict or** DbSchemaException **after concurrent data model modifications**

If your team makes concurrent modifications to the data model (e.g. adding/removing entities or properties) it may clash with your changes. Read the [meta model docs](/advanced/meta-model-ids-and-uids#resolving-meta-model-conflicts) on how to resolve the conflicts.

## DbSchemaException: incoming ID does not match existing UID

Creating a Store throws a `DbSchemaException` and a message like

* Incoming *entity* ID does not match existing UID
* Incoming *property* ID does not match existing UID
* Incoming *index* ID does not match existing UID

This means there is a conflict between the data model defined in your code (using `@Entity` classes) and the data model of the existing database file.

For example for an entity, this message indicates the unique identifier (UID) of an entity is not the same as before. Entity UIDs are auto-assigned by ObjectBox and stored in `objectbox-models/default.json` for Java or `lib/objectbox-model.json` for Dart. Look for the `id` property which contains the UID in the second part, the first part is the ID (the format is `ID:UID`).

Read the [meta model docs](/advanced/meta-model-ids-and-uids#resolving-meta-model-conflicts) on why this can happen and how to resolve such conflicts.

## DbSchemaException **after switching git branch (no concurrent data model modifications)**

See below.

## DbSchemaException: DB's last ID is higher

Creating a Store throws a `DbSchemaException` and a message like

* DB's last *entity* ID is higher than from model
* DB's last *property* ID is higher than the incoming one
* DB's last *index* ID is higher than from model
* DB's last *relation* ID is higher than from model

This means, there is a conflict between the data model defined in your code (using `@Entity` classes) and the data model of the existing database file.

For example for an entity, this message indicates that the database already contains one or more entities with IDs higher than the lowest one of the model in your code. Entity IDs are auto-assigned by ObjectBox and stored in `objectbox-models/default.json` for Java or `lib/objectbox-model.json` for Dart. Look for the `lastEntityId` value, the highest used entity ID is contained in the first part, the second part is the UID (the format is `ID:UID`).

Read the [meta model docs](/advanced/meta-model-ids-and-uids#resolving-meta-model-conflicts) on why this can happen and how to resolve such conflicts.

## DbFullException: Could not put

This is thrown when applying a transaction (e.g. putting an object) would exceed the [maxSizeInKByte](https://objectbox.io/docfiles/java/current/io/objectbox/BoxStoreBuilder.html#maxSizeInKByte\(long\)) (Java)/[maxDBSizeInKB](https://pub.dev/documentation/objectbox/latest/objectbox/Store/Store.html) (Dart) configured for the store.

By default, this is 1 GB, which should be sufficient for most applications. In general, a maximum size prevents the database from growing indefinitely when something goes wrong (for example data is put in an infinite loop).

This value can be changed, so increased or also decreased, each time when opening a store:

{% tabs %}
{% tab title="Java" %}

```kotlin
MyObjectBox.builder()
    .androidContext(context)
    .maxSizeInKByte(1024 * 1024 /* 1 GB */)
    .build()
```

{% endtab %}

{% tab title="Dart" %}

```dart
Store(getObjectBoxModel(), maxDBSizeInKB: 1024 * 1024 /* 1 GB */);
```

{% endtab %}
{% endtabs %}

## **Couldn’t find “libobjectbox.so”**

This can have various reasons. In general **check your ABI filter setup** [or add one](https://google.github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.NdkOptions.html#com.android.build.gradle.internal.dsl.NdkOptions:abiFilters) in your Gradle build file.

If your app explicitly **ships code for "armeabi"**: For Android, ObjectBox comes with binaries for “armeabi-v7a” and “arm64-v8a” ABIs. We consider “armeabi” to be outdated and thus do not support it. Check if you have a Gradle config like abiFilters "armeabi", which is causing the problem (e.g. remove it or change it to “armeabi-v7a”).

If your app uses **split APKs or App Bundle**: some users might have sideloaded your APK that includes the library for a platform that is incompatible with the one of their device. See [App Bundle, split APKs and Multidex](/android/app-bundle-and-split-apk) for workarounds.

## **Version conflict with ‘com.google.code.findbugs:jsr305’**

If you are doing Android instrumentation (especially with Espresso), you may get a warning like this:\
`Error:Conflict with dependency ‘com.google.code.findbugs:jsr305’ in project ‘:app’. Resolved versions for app (3.0.2) and test app (2.0.1) differ. See` [`http://g.co/androidstudio/app-test-app-conflict`](http://g.co/androidstudio/app-test-app-conflict) `for details.`

You can easily resolve the version conflict by adding this Gradle dependency: `androidTestCompile 'com.google.code.findbugs:jsr305:3.0.2'`

[Background info](https://github.com/objectbox/objectbox-java/issues/73).

## **Incompatible property type**

Check the[ data model migration guide](https://docs.objectbox.io/advanced/data-model-updates) if you get an exception like:\
`io.objectbox.exception.DbException: Property […] is not compatible to its previous definition. Check its type.`

or

`Cannot change the following flags for Property`

## Flutter iOS builds for armv7 fail with "ObjectBox does not contain that architecture"

[Only 64-bit iOS devices are supported.](/faq#on-which-platforms-does-objectbox-run) To resolve the build error, configure Architectures in your Xcode project like described in [Getting Started](/getting-started) for Flutter.

## **Error Codes**

Sometimes you might get an error code along with a error message. Typically, you will find error codes in parenthesis. Example:

`Could not prepare directory: objectbox (30)`

This error code comes from the OS, and gives you additional information; e.g. 30 tells that you tried to init a database in a read-only location of the file system, which cannot work.

Here's the list of typical OS errors:

| Error code | Errno   | Description                         |
| :--------: | ------- | ----------------------------------- |
|      1     | EPERM   | Operation not permitted             |
|      2     | ENOENT  | No such file or directory           |
|      3     | ESRCH   | No such process                     |
|      4     | EINTR   | Interrupted system call             |
|      5     | EIO     | I/O error                           |
|      6     | ENXIO   | No such device or address           |
|      7     | E2BIG   | Argument list too long              |
|      8     | ENOEXEC | Exec format error                   |
|      9     | EBADF   | Bad file number                     |
|     10     | ECHILD  | No child processes                  |
|     11     | EAGAIN  | Try again                           |
|     12     | ENOMEM  | Out of memory                       |
|     13     | EACCES  | Permission denied                   |
|     14     | EFAULT  | Bad address                         |
|     15     | ENOTBLK | Block device required               |
|     16     | EBUSY   | Device or resource busy             |
|     17     | EEXIST  | File exists                         |
|     18     | EXDEV   | Cross-device link                   |
|     19     | ENODEV  | No such device                      |
|     20     | ENOTDIR | Not a directory                     |
|     21     | EISDIR  | Is a directory                      |
|     22     | EINVAL  | Invalid argument                    |
|     23     | ENFILE  | File table overflow                 |
|     24     | EMFILE  | Too many open files                 |
|     25     | ENOTTY  | Not a typewriter                    |
|     26     | ETXTBSY | Text file busy                      |
|     27     | EFBIG   | File too large                      |
|     28     | ENOSPC  | No space left on device             |
|     29     | ESPIPE  | Illegal seek                        |
|     30     | EROFS   | Read-only file system               |
|     31     | EMLINK  | Too many links                      |
|     32     | EPIPE   | Broken pipe                         |
|     33     | EDOM    | Math argument out of domain of func |
|     34     | ERANGE  | Math result not representable       |

## **Help with other issues**

{% content-ref url="/pages/-LSagCKqcUJYSop2Sfxf" %}
[FAQ](/faq)
{% endcontent-ref %}

If you believe to have found a bug or missing feature, please create an issue.

For Java/Kotlin: <https://github.com/objectbox/objectbox-java/issues>

For Flutter/Dart: <https://github.com/objectbox/objectbox-dart/issues>

If you have a usage question regarding ObjectBox, please post on Stack Overflow. <https://stackoverflow.com/questions/tagged/objectbox>


# Java Release History (<= v1.5)

Release notes for ObjectBox 1.5.0 and older.

Although this is technically the changelog for Java, this is also a good reference of what changed in the ObjectBox C++ core.

### 4.0.2 - 2024-08-20

* Add convenience `oneOf` and `notOneOf` conditions that accept `Date` to avoid manual conversion using `getTime()`.
* When `BoxStore` is closing, briefly wait on active transactions to finish.
* Guard against crashes when `BoxStore` was closed, but database operations do still occur concurrently (transactions are still active).

### 4.0.1 - 2024-06-03

* Examples: added [Vector Search example](https://github.com/objectbox/objectbox-examples/tree/main/java-main-vector-search) that demonstrates how to perform on-device [approximate nearest neighbor (ANN) search](https://docs.objectbox.io/on-device-vector-search).
* Revert deprecation of `Box.query()`, it is still useful for queries without any condition.
* Add note on old query API methods of `QueryBuilder` that they are not recommended for new projects. Use [the new query APIs](https://docs.objectbox.io/queries) instead.
* Update and expand documentation on `ToOne` and `ToMany`.

### 4.0.0 - Vector Search - 2024-05-16

**ObjectBox now supports** [**Vector Search**](https://docs.objectbox.io/ann-vector-search) to enable efficient similarity searches.

This is particularly useful for AI/ML/RAG applications, e.g. image, audio, or text similarity. Other use cases include semantic search or recommendation engines.

Create a Vector (HNSW) index for a floating point vector property. For example, a `City` with a location vector:

```java
@Entity
public class City {

    @HnswIndex(dimensions = 2)
    float[] location;
    
}
```

Perform a nearest neighbor search using the new `nearestNeighbors(queryVector, maxResultCount)` query condition and the new "find with scores" query methods (the score is the distance to the query vector). For example, find the 2 closest cities:

```java
final float[] madrid = {40.416775F, -3.703790F};
final Query<City> query = box
        .query(City_.location.nearestNeighbors(madrid, 2))
        .build();
final City closest = query.findWithScores().get(0).get();
```

For an introduction to Vector Search, more details and other supported languages see the [Vector Search documentation](https://docs.objectbox.io/ann-vector-search).

* BoxStore: deprecated `BoxStore.sizeOnDisk()`. Instead use one of the new APIs to determine the size of a database:
  * `BoxStore.getDbSize()` which for a file-based database returns the file size and for an in-memory database returns the approximately used memory,
  * `BoxStore.getDbSizeOnDisk()` which only returns a non-zero size for a file-based database.
* Query: add properly named `setParameter(prop, value)` methods that only accept a single parameter value, deprecated the old `setParameters(prop, value)` variants.
* Sync: add `SyncCredentials.userAndPassword(user, password)`.
* Gradle plugin: the license of the [Gradle plugin](https://github.com/objectbox/objectbox-java-generator) has changed to the GNU Affero General Public License (AGPL).

### V3.8.0 - 2024-02-13

* Support creating file-less in-memory databases, e.g. for caching and testing. To create one use `inMemory()` when building a `BoxStore`:

  ```java
  store = MyObjectBox.builder()
          .androidContext(context)
          .inMemory("test-db")
          .build();
  ```

  See the `BoxStoreBuilder.inMemory()` documentation for details.
* Change `BoxStore.deleteAllFiles()` to support deleting an in-memory database.
* The `maxDataSizeInKByte()` option when building a store is ready for production use. This is different from the existing `maxSizeInKByte()` option in that it is possible to remove data after reaching the limit and continue to use the database. See its documentation for more details.
* Admin will now print a warning when it does not have permission to show the Admin notification. When testing your app on a device with Android 13 or newer, developers should manually turn on notifications to make use of the Admin notification.
* Added examples on how to use Kotlin's unsigned integer types to [Custom types](/advanced/custom-types).
* Restore compatibility with Kotlin 1.5. However, need to exclude `kotlin-stdlib` 1.8 from `objectbox-kotlin` as it includes classes previously in the -jdk7/-jdk8 libraries to avoid duplicate class file errors. So if not absolutely needed, we still recommend to use at least Kotlin 1.8.

### V3.7.1 - 2023/11/07

* Throw an exception instead of crashing when trying to create a query on a closed store. [#1154](https://github.com/objectbox/objectbox-java/issues/1154)
* The Gradle plugin now requires at least Gradle 7.0 and Android Gradle Plugin 4.1.
* The Android library now requires Android 4.4 (API 19) or newer.

### V3.7.0 - 2023/08/22

* A new key/value validation option `validateOnOpenKv()` is available on `MyObjectBox.builder()` to help diagnose `FileCorruptException: Corrupt DB, min key size violated` issues. If enabled, the `build()` call will throw a `FileCorruptException` if corruption is detected with details on which key/value is affected. [#1143](https://github.com/objectbox/objectbox-java/issues/1143)
* Admin: integer and floating point arrays introduced with the previous release are now nicely displayed and collapsed if long.
* Admin: the data table again displays all items of a page. [#1135](https://github.com/objectbox/objectbox-java/issues/1135)
* The `__cxa_pure_virtual` crash should not occur anymore; if you get related exceptions, they should contain additional information to better diagnose this issue. Let us know details in [#1131](https://github.com/objectbox/objectbox-java/issues/1131)
* Queries: all expected results are now returned when using a less-than or less-or-equal condition for a String property with index type `VALUE`. Reported via [objectbox-dart#318](https://github.com/objectbox/objectbox-dart/issues/318)
* Queries: when combining multiple conditions with OR and adding a condition on a related entity ("link condition") the combined conditions are now properly applied. Reported via [objectbox-dart#546](https://github.com/objectbox/objectbox-dart/issues/546)
* Some flags classes have moved to the new `config` package:
  * `io.objectbox.DebugFlags` is deprecated, use `io.objectbox.config.DebugFlags` instead.
  * `io.objectbox.model.ValidateOnOpenMode` is deprecated, use `io.objectbox.config.ValidateOnOpenModePages` instead.

### V3.6.0 - 2023/05/16

* **Support for integer and floating point arrays**: store
* `short[]`, `char[]`, `int[]`, `long[]` and
* `float[]` and `double[]`

  (or their Kotlin counterparts, e.g. `FloatArray`) without a converter.

  A simple example is a shape entity that stores a palette of RGB colors:

  ```java
  @Entity
  public class Shape {

      @Id public long id;

      // An array of RGB color values that are used by this shape.
      public int[] palette;

  }

  // Find all shapes that use red in their palette
  try (Query<Shape> query = store.boxFor(Shape.class)
          .query(Shape_.palette.equal(0xFF0000))
          .build()) {
          query.findIds();
  }
  ```

  This can also be useful to store vector embeddings produced by machine learning, e.g.:

  ```java
  @Entity
  public class ImageEmbedding {

      @Id public long id;

      // Link to the actual image, e.g. on Cloud storage
      public String url;

      // The coordinates computed for this image (vector embedding)
      public float[] coordinates;

  }
  ```
* Fix incorrect Cursor code getting generated when using `@Convert` to convert to a `String` array.
* The `io.objectbox.sync` plugin now also automatically adds a Sync-enabled JNI library on macOS and Windows (previously on Linux x64 only; still need to [add manually for Linux on ARM](https://sync.objectbox.io/sync-client#objectbox-sync-enabled-library)).

### V3.5.1 - 2023/01/31

* Fixes writes failing with "error code -30786", which may occur in some corner cases on some devices. [#1099](https://github.com/objectbox/objectbox-java/issues/1099)
* Add docs to `DbSchemaException` on how to resolve its typical causes.

### V3.5.0 - 2022/12/05

**This release includes breaking changes to generated code. If you encounter build errors, make sure to clean and build your project (e.g. Build > Rebuild project in Android Studio).**

* **Add `Query.copy()` and `QueryThreadLocal`** to obtain a `Query` instance to use in different threads. [Learn more about re-using queries](https://docs.objectbox.io/queries#reusing-queries-and-parameters). [#1071](https://github.com/objectbox/objectbox-java/issues/1071)
* **Add `relationCount` query condition** to match objects that have a certain number of related objects pointing to them. E.g. `Customer_.orders.relationCount(2)` will match all customers with two orders, `Customer_.orders.relationCount(0)` will match all customers with no associated order. This can be useful to find objects where the relation was dissolved, e.g. after the related object was removed.
* Allow using a relation target ID property with a [property query](https://docs.objectbox.io/queries#propertyquery). E.g. `query.property(Order_.customerId)` will map results to the ID of the customer of an order. [#1028](https://github.com/objectbox/objectbox-java/issues/1028)
* Add docs on `DbFullException` about [why it occurs and how to handle it](https://docs.objectbox.io/troubleshooting#dbfullexception-could-not-put).
* Do not fail to transform an entity class that contains a transient relation field when using Android Gradle Plugin 7.1 or lower.
* Restore compatibility for Android projects using Gradle 6.1. **The minimum supported version for Gradle is 6.1 and for the Android Gradle Plugin 3.4.** This should make it easier for older projects to update to the latest version of ObjectBox.

**Using Sync?** This release uses a new Sync protocol which improves efficiency. Reach out via your existing contact to check if any actions are required for your setup.

### V3.4.0 - 2022/10/18

* **Add `findFirstId()` and `findUniqueId()`** to `Query` which just return the ID of a matching object instead of the full object.
* Experimental support for setting a maximum data size via the `maxDataSizeInKByte` property when building a Store. This is different from the existing `maxSizeInKByte` property in that it is possible to remove data after reaching the limit and continue to use the database. See its documentation for more details.
* Fix a crash when querying a value-based index (e.g. `@Index(type = IndexType.VALUE)`) on Android 32-bit ARM devices. [#1105](https://github.com/objectbox/objectbox-java/issues/1105)
* Various small improvements to the native libraries.

**Using Sync?** There is no Sync version for this release, please continue using version 3.2.1.

### V3.3.1 - 2022/09/05

**Note: V3.3.0 contains a bug preventing correct transformation of some classes, please use V3.3.1 instead.**

* Gradle plugin: use new transform API with Android Plugin 7.2.0 and newer. Builds should be slightly faster as only entity and cursor classes and only incremental changes are transformed. [#1078](https://github.com/objectbox/objectbox-java/issues/1078)
* Gradle plugin: improve detection of applied Android plugins, improve registration of byte-code transform for non-Android Java projects, add check for minimum supported version of Gradle.

**Using Sync?** There is no Sync version for this release, please continue using version 3.2.1.

### V3.2.1 - 2022/07/05

* Resolve an issue that prevented resources from getting cleaned up after closing `BoxStore`, causing the reference table to overflow when running many instrumentation tests on Android. [#1080](https://github.com/objectbox/objectbox-java/issues/1080)
* Plugin: support Kotlin 1.7. [#1085](https://github.com/objectbox/objectbox-java/issues/1085)

### V3.2.0 - 2022/06/20

* Query: throw `IllegalStateException` when query is closed instead of crashing the virtual machine. [#1081](https://github.com/objectbox/objectbox-java/issues/1081)
* BoxStore and Query now throw `IllegalStateException` when trying to subscribe but the store or query is closed already.
* Various internal improvements including minor optimizations for binary size and performance.

### V3.1.3 - 2022/05/10

* **The Data Browser has been renamed to** [**ObjectBox Admin**](https://docs.objectbox.io/data-browser)**.** Deprecated `AndroidObjectBrowser`, use `Admin` instead. `AndroidObjectBrowser` will be removed in a future release.
* Windows: using a database directory path that contains unicode (UTF-8) characters does not longer create an additional, unused, directory with garbled characters.
* Query: when using a negative offset or limit display a helpful error message.
* Processor: do not crash, but error if ToOne/ToMany type arguments are not supplied (e.g. `ToOne` instead of `ToOne<Entity>`).

### V3.1.2 - 2022/02/21

This release only contains bug fixes for the Android library when used with ObjectBox for Dart/Flutter.

### V3.1.1 - 2022/01/26

* Fix incorrect unique constraint violation if an entity contains at least two unique properties with a certain combination of non-unique indexes.
* **Data Browser/Admin: improved support when running multiple on the same host**, but a different port (e.g. `localhost:8090` and `localhost:8091`).

### V3.1.0 - 2021/12/15

[Read the blog post with more details](https://objectbox.io/objectbox-java-database-flex-type/) and code examples for the new flex properties and query conditions.

* [**Support Flex properties**](/advanced/custom-types#flex-properties)**.** Expanding on the string and flexible map support in `3.0.0`, it is now possible to add a property using `Object` in Java or `Any?` in Kotlin. These "flex properties" allow to store values of various types like integers, floating point values, strings and byte arrays. Or lists and maps (using string keys) of those.
* **The `containsElement` query condition now matches keys of string map properties.** It also matches string or integer elements of a Flex list.
* **New `containsKeyValue` query condition to match key/value combinations** of string map and Flex map properties containing strings and integers. Also added matching `Query.setParameters` overload.
* Add ProGuard/R8 rule to not warn about `SuppressFBWarnings` annotation. #1011
* Add more detailed error message when loading the native library fails on Android. #1024
* **Data browser: byte arrays are now correctly displayed in Base64 encoding.** #1033

#### Kotlin

* **Add `BoxStore.awaitCallInTx` suspend function** which wraps `BoxStore.callInTx`.

#### Gradle plugin

* Do not crash trying to add dependencies to Java desktop projects that only apply the Gradle `application` plugin.

### V3.0.0 - 2021/10/19

{% hint style="info" %}
**2021/10/19: Released version 3.0.1**, which contains a fix for Android Java projects.
{% endhint %}

* [**A new Query API**](https://docs.objectbox.io/queries#new-query-api) is available that works similar to the [ObjectBox for Dart/Flutter](https://pub.dev/packages/objectbox) Query API and makes it **easier to create nested conditions**. #201

  ```kotlin
  // equal AND (less OR oneOf)
  val query = box.query(
        User_.firstName equal "Joe"
                and (User_.age less 12
                or (User_.stamp oneOf longArrayOf(1012))))
        .order(User_.age)
        .build()
  ```
* **For the existing Query API, String property conditions now require to explicitly specify case.** See the documentation of [`StringOrder`](https://objectbox.io/docfiles/java/current/io/objectbox/query/QueryBuilder.StringOrder.html) for which one to choose (typically `StringOrder.CASE_INSENSITIVE`).

  ```kotlin
  // Replace String conditions like
  query().equal(User_.firstName, "Joe")
  // With the one accepting a StringOrder
  query().equal(User_.firstName, "Joe", StringOrder.CASE_INSENSITIVE)
  ```
* **Subscriptions now publish results in serial instead of in parallel** (using a single thread vs. multiple threads per publisher). Publishing in parallel could previously lead to outdated results getting delivered after the latest results. As a side-effect transformers now run in serial instead of in parallel as well (on the same single thread per publisher). #793
* **Support annotating a single property with `@Unique(onConflict = ConflictStrategy.REPLACE)`** to replace an existing Object if a conflict occurs when doing a put. #509

  ```kotlin
  @Entity
  data class Example(
          @Id
          var id: Long = 0,
          @Unique(onConflict = ConflictStrategy.REPLACE)
          var uniqueKey: String? = null
  )
  ```
* Support [`@Unsigned`](https://objectbox.io/docfiles/java/current/io/objectbox/annotation/Unsigned.html) to indicate that values of an integer property (e.g. `Integer` and `Long` in Java) should be treated as unsigned when doing queries or creating indexes.
* Store time in nanoseconds using the new `@Type` annotation for compatibility with other ObjectBox language bindings:

  ```kotlin
  @Type(DatabaseType.DateNano)
  var timeInNanos: Long;
  ```
* **Package FlatBuffers version into library to avoid conflicts with apps or other libraries using FlatBuffers.** #894
* **Kotlin: add `Flow` extension functions for `BoxStore` and `Query`.** #900
* Data browser: display query results if a property has a `NaN` value. #984
* **Android 12: support using Data Browser if targeting Android 12 (SDK 31).** #1007

#### New supported property types

* **String arrays** (Java `String[]` and Kotlin `Array<String>`) **and lists** (Java `List<String>` and Kotlin `MutableList<String>`). Using the new `containsElement("item")` condition, it is also possible to query for entities where "item" is equal to one of the elements.

  ```kotlin
  @Entity
  data class Example(
          @Id
          var id: Long = 0,
          var stringArray: Array<String>? = null,
          var stringMap: MutableMap<String, String>? = null
  )

  // matches [“first”, “second”, “third”]
  box.query(Example_.stringArray.containsElement(“second”)).build()
  ```
* **String maps** (Java `Map<String, String>` or Kotlin `MutableMap<String, String>`). Stored internally as a byte array using FlexBuffers.
* **Flexible maps:**
  * map keys must all have the same type,
  * map keys or values must not be null,
  * map values must be one of the supported database type, or a list of them (e.g. String, Boolean, Integer, Double, byte array...).

#### Sync

* The generated JSON model file no longer contains Java-specific flags that would lead to errors if used with Sync server.
* Additional checks when calling client or server methods.

### V2.9.2-RC4 - 2021/08/19

**Note: this is a preview release. Future releases may add, change or remove APIs.**

* [A new experimental Query API](/queries#new-query-api) provides support for nested AND and OR conditions. [#201](https://github.com/objectbox/objectbox-java/issues/201)
* Subscriptions now publish results in serial instead of in parallel (using a single thread vs. multiple threads per publisher). Publishing in parallel could previously lead to outdated results getting delivered after the latest results. As a side-effect transformers now run in serial instead of in parallel as well (on the same single thread per publisher). [#793](https://github.com/objectbox/objectbox-java/issues/793)
* Add documentation that string property conditions ignore case by default. Point to using case-sensitive conditions for high-performance look-ups, e.g. [when using string UIDs](https://docs.objectbox.io/entity-annotations#object-ids-id).
* Support annotating a single property with `@Unique(onConflict = ConflictStrategy.REPLACE)` to replace an existing Object if a conflict occurs when doing a put. [#509](https://github.com/objectbox/objectbox-java/issues/509)
* Support `@Unsigned` to indicate that values of an integer property (e.g. `Integer` and `Long` in Java) should be treated as unsigned when doing queries or creating indexes. See the Javadoc of the annotation for more details.
* Store time in nanoseconds by annotating a `Long` property with `@Type(DatabaseType.DateNano)`.
* Package FlatBuffers version into library to avoid conflicts if your code uses FlatBuffers as well. [#894](https://github.com/objectbox/objectbox-java/issues/894)
* Kotlin: add Flow extension functions for BoxStore and Query. [#900](https://github.com/objectbox/objectbox-java/pull/990)
* Data browser: display query results if a property has a NaN value. [#984](https://github.com/objectbox/objectbox-java/issues/984)

**New supported property types**

When adding new properties, a converter is no longer necessary to store these types:

* String arrays (Java `String[]` and Kotlin `Array<String>`). Using the new `containsElement("item")` condition, it is also possible to query for entities where "item" is equal to one of the array items.
* String maps (Java `Map<String, String>` or Kotlin `MutableMap<String, String>`). Stored internally as a byte array using FlexBuffers.

**Sync**

* The generated JSON model file no longer contains Java-specific flags that would lead to errors if used with Sync server.
* Additional checks when calling client or server methods.

### V2.9.1 - 2021/03/15

This is the first release **available on the Central repository** (Sonatype OSSRH). Make sure to adjust your `build.gradle` files accordingly:

```
repositories {
    mavenCentral()
}
```

Changes:

* Javadoc for `find(offset, limit)` of `Query` is more concrete on how offset and limit work.
* Javadoc for between conditions explicitly mentions it is inclusive of the two given values.
* Sync: Instead of the same name and a Maven classifier, Sync artifacts now use a different name. E.g. `objectbox-android:2.9.0:sync` is replaced with `objectbox-sync-android:2.9.1`.

### V2.9.0 - 2021/02/16

* Query: Add `lessOrEqual` and `greaterOrEqual` conditions for long, String, double and byte\[] properties.
* Support Java applications on ARMv7 and AArch64 devices. [#657](https://github.com/objectbox/objectbox-java/issues/657)

  To use, add `implementation "io.objectbox:objectbox-linux-armv7:$objectboxVersion` or `implementation "io.objectbox:objectbox-linux-arm64:$objectboxVersion` to your dependencies. Otherwise the setup is identical with [Java Desktop Apps](https://docs.objectbox.io/java-desktop-apps#add-libraries-for-distribution).
* Resolve rare `ClassNotFoundException: kotlin.text.Charsets` when running processor. [#946](https://github.com/objectbox/objectbox-java/issues/946)
* Ensure Query setParameters works if running the x86 library on x64 devices (which could happen if ABI filters were set up incorrectly). [#927](https://github.com/objectbox/objectbox-java/issues/927)

### V2.8.1 - 2020/11/10

* Minor improvements to [Sync](https://objectbox.io/sync/) tooling.

See the 2.8.0 release notes below for the latest changes.

### **V2.8.0 - 2020/11/05**

* Added [Sync](https://objectbox.io/sync/) API.
* Fixed "illegal reflective access" warning in the plugin.
* The data browser notification is now silent by default, for quieter testing. [#903](https://github.com/objectbox/objectbox-java/issues/903)
* Updated and improved API documentation in various places (e.g. on how `Query.findLazy()` and `Query.findLazyCached()` work with `LazyList` [#906](https://github.com/objectbox/objectbox-java/issues/906)).
* Print full name and link to element for `@Index` and `@Id` errors. [#902](https://github.com/objectbox/objectbox-java/issues/902)
* Explicitly allow to remove a `DbExceptionListener` by accepting null values for `BoxStore.setDbExceptionListener(listener)`.

### **V2.7.1 - 2020/08/19**

* Fix exception handling during `BoxStoreBuilder.build()` to allow retries. For example, after a `FileCorruptException` you could try to open the database again using the recently added `usePreviousCommit()` option.
* Add `PagesCorruptException` as a special case of `FileCorruptException`.
* `DbExceptionListener` is called more robustly.

### **V2.7.0 - 2020/07/30**

* Several database store improvements for`BoxStore` and `BoxStoreBuilder`
  * New configuration options to open the database, e.g. a new read-only mode and using the previous data snapshot (second last commit) to potentially recover data.
  * Database validation. We got a GitHub report indicating that some specific devices ship with a broken file system. While this is not a general concern (file systems should not be broken), we decided to detect some typical problems and provide some options to deal with these.
  * Get the size on disk
* Add an efficient check if an object exist in a `Box` via `contains(id)`.
* Android improvements
  * Resolve Android Studio Build Analyzer warning about a prepare tasks not specifying outputs.
  * Data Browser drawables are no longer packaged in the regular Android library. [GitHub #857](https://github.com/objectbox/objectbox-java/issues/857)
* Fixes for one-to-many relations, e.g. allow removing both entity classes of a one-to-many relation. [GitHub #859](https://github.com/objectbox/objectbox-java/issues/859)

### **V2.6.0 - 2020/06/09**

* `@DefaultValue("")` annotation for properties to return an empty string instead of null. This is useful if a not-null property is added to an entity, but there are existing entities in the database that will return null for the new property. [GH#157](https://github.com/objectbox/objectbox-java/issues/157)
* [**RxJava 3 support library**](https://github.com/objectbox/objectbox-java/tree/master/objectbox-rxjava3) `objectbox-rxjava3`. Also includes Kotlin extension functions to more easily obtain Rx types, e.g. use `query.observable()` to get an `Observable`. [GH#83](https://github.com/objectbox/objectbox-java/issues/839)
* The annotation processor is incremental by default. [GH#620](https://github.com/objectbox/objectbox-java/issues/620)
* Fix error handling if ObjectBox can't create a Java entity (the proper exception is now thrown).
* Support setting an alias after combining conditions using `and()` or `or()`. [GH#83](https://github.com/objectbox/objectbox-java/issues/834)
* Turn on incremental annotation processing by default. [GH#620](https://github.com/objectbox/objectbox-java/issues/620)
* Add documentation that string property conditions ignore case by default. Point to using case-sensitive conditions for high-performance look-ups, e.g. [when using string UIDs](https://docs.objectbox.io/entity-annotations#object-ids-id).
* Repository Artifacts are signed once again.

Changes since 2.6.0-RC (released on 2020/04/28):

* Performance improvements with query links (aka "joins").\
  Note: the order of results has changed unless you explicitly specified properties to order by. Remember: you should not depend on any internal order. If you did, this is a good time to fix it.
* `objectbox-java` no longer exposes the greenrobot-essentials and FlatBuffers dependencies to consuming projects.
* Minor code improvements.

### **V3.0.0-alpha2 - 2020/03/24**

**Note: this is a preview release. Future releases may add, change or remove APIs.**

* Add Kotlin infix extension functions for creating conditions using the[ new Query API](/queries#new-query-api). See [the documentation](/queries#new-query-api) for examples.
* The old Query API now also supports setting an alias after combining conditions using `and()` or `or()`. [GH#834](https://github.com/objectbox/objectbox-java/issues/834)
* Add documentation that string property conditions ignore case by default. Point to using case-sensitive conditions for high-performance look-ups, e.g. [when using string UIDs](https://docs.objectbox.io/entity-annotations#object-ids-id).
* Java's `String[]` and Kotlin's `Array<String>` are now a supported database type. A converter is no longer necessary to store these types. Using the `arrayProperty.equal("item")` condition, it is possible to query for entities where "item" is equal to one of the array items.
* Support `@Unsigned` to indicate that values of an integer property (e.g. `Integer` and `Long` in Java) should be treated as unsigned when doing queries or creating indexes. See the Javadoc of the annotation for more details.
* Add [new library to support RxJava 3](https://github.com/objectbox/objectbox-java/tree/release-3.0.0-alpha2/objectbox-rxjava3), `objectbox-rxjava3`. In addition `objectbox-kotlin` adds extension functions to more easily obtain Rx types, e.g. use `query.observable()` to get an `Observable`. [GH#839](https://github.com/objectbox/objectbox-java/issues/839)

To use this release change the version of `objectbox-gradle-plugin` to `3.0.0-alpha2`. The plugin now properly adds the preview version of `objectbox-java` to your dependencies.

```groovy
buildscript {
    dependencies {
        classpath "io.objectbox:objectbox-gradle-plugin:3.0.0-alpha2"
    }
}

dependencies {
    // Artifacts with native code remain at 2.5.1.
    implementation "io.objectbox:objectbox-android:2.5.1"
}
```

The `objectbox-android`, `objectbox-linux`, `objectbox-macos` and `objectbox-windows` artifacts shipping native code remain at version 2.5.1 as there have been no changes. If you explicitly include them, make sure to specify their version as `2.5.1`.

### **V3.0.0-alpha1 - 2020/03/09**

**Note: this is a preview release. Future releases may add, change or remove APIs.**

* [A new Query API](/queries#new-query-api) provides support for nested AND and OR conditions. See [the documentation](/queries#new-query-api) for examples and notable changes. [GH#201](https://github.com/objectbox/objectbox-java/issues/201)
* Subscriptions now publish results in serial instead of in parallel (using a single thread vs. multiple threads per publisher). Publishing in parallel could previously lead to outdated results getting delivered after the latest results. As a side-effect transformers now run in serial instead of in parallel as well (on the same single thread per publisher). [GH#793](https://github.com/objectbox/objectbox-java/issues/793)
* Turn on incremental annotation processing by default. [GH#620](https://github.com/objectbox/objectbox-java/issues/620)

To use this release change the version of `objectbox-gradle-plugin` to `3.0.0-alpha1` and add a dependency on `objectbox-java` version `3.0.0-alpha1`.

```groovy
buildscript {
    dependencies {
        classpath "io.objectbox:objectbox-gradle-plugin:3.0.0-alpha1"
    }
}

dependencies {
    implementation "io.objectbox:objectbox-java:3.0.0-alpha1"
    // Artifacts with native code remain at 2.5.1.
    implementation "io.objectbox:objectbox-android:2.5.1"
}
```

The `objectbox-android`, `objectbox-linux`, `objectbox-macos` and `objectbox-windows` artifacts shipping native code remain at version 2.5.1 as there have been no changes. However, if your project explicitly depends on them they will pull in version 2.5.1 of `objectbox-java`. Make sure to add an explicit dependency on of `objectbox-java` version `3.0.0-alpha1` as mentioned above.

### **V2.5.1 - 2020/02/10**

* Support Android Gradle Plugin 3.6.0. [GH#817](https://github.com/objectbox/objectbox-java/issues/817)
* Support for incremental annotation processing. [GH#620](https://github.com/objectbox/objectbox-java/issues/620) It is off by default. To turn it on set `objectbox.incremental` to `true` in `build.gradle` :

```groovy
android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [ "objectbox.incremental" : "true" ]
            }
        }
    }
}
```

### **V2.5.0 - 2019/12/12**

#### Important bug fix - please update asap if you are using N:M relations!

* Fixed corner case for N:M ToMany (not the backlinks for ToOne) returning wrong results

#### Improvements and New Features

* Property queries compute sums and averages more precisely (improved algorithms and wider internal types)
* Query adds "describe" methods to obtain useful debugging information
* New method removeAllObjects() in BoxStore to clear the database of all data

### **V2.4.1 - 2019/10/29**

* More helpful error messages if annotations can not be combined.
* Improved documentation on various annotations.

### **V2.4.0 - 2019/10/15**

#### **Upgrade Notes**

* Android: the AAR libraries ship Java 8 bytecode. Your app will not build unless you upgrade com.android.tools.build:gradle to 3.2.1 or later.
* Android: the [ObjectBox LiveData](/android/livedata-architecture-components) and [Paging integration](/android/paging-architecture-components) migrated from Android Support Libraries to Jetpack (AndroidX) Libraries. If you are using them the library will not work unless you make the following changes in your app:
  * Upgrade com.android.tools.build:gradle to 3.2.1 or later.
  * Upgrade compileSdkVersion to 28 or later.
  * Update your app to use Jetpack (AndroidX); follow the instructions in [Migrating to AndroidX](https://developer.android.com/jetpack/androidx/migrate).
* Note: this version requires backwards-incompatible changes to the generated MyObjectBox file. Make sure to rebuild your project before running your app so the MyObjectBox file is re-generated.

#### Improvements & Fixes

V2.4.0 - 2019/10/15

* Class transformation works correctly if absolute path contains special characters. [GH#135](https://github.com/objectbox/objectbox-java/issues/135)

V2.4.0-RC - Release Candidate 2019/10/03

* Box: add `getRelationEntities`, `getRelationBacklinkEntities`,`getRelationIds` and `getRelationBacklinkIds` to directly access relations without going through ToMany.
* Box: add `putBatched` to put entities using a separate transaction for each batch.
* `Box.removeByKeys()` is now deprecated; use `removeByIds()` instead.
* Query: fixed performance regressions introduced in version 2.3 on 32 bit devices in combination with ordered results
* Fixed removing a relation and the related entity class. [GH#490](https://github.com/objectbox/objectbox-java/issues/490)
* Resolved issue to enable query conditions on the target ID property of a ToOne relation. [GH#537](https://github.com/objectbox/objectbox-java/issues/537)
* Box.getAll always returns a mutable list. [GH#685](https://github.com/objectbox/objectbox-java/pull/685)
* Do not overwrite existing objectbox-java or objectbox-kotlin dependency. [GH#693](https://github.com/objectbox/objectbox-java/issues/693)
* Resolved a corner case build time crash when parsing package elements. [GH#698](https://github.com/objectbox/objectbox-java/issues/698)
* When trying to find an appropriate get-method for a property, also check if the return type matches the property type. [GH#720](https://github.com/objectbox/objectbox-java/issues/720)
* Explicitly display an error if two entities with the same name are detected. [GH#744](https://github.com/objectbox/objectbox-java/issues/744)
* The code in MyObjectBox is split up by entity to make it less likely to run into the Java method size limit when using many @Entity classes. [GH#750](https://github.com/objectbox/objectbox-java/issues/750)
* Query: improved performance for ordered results with a limit. [GH#769](https://github.com/objectbox/objectbox-java/issues/769)
* Query: throw if a filter is used incorrectly with count or remove. [GH#771](https://github.com/objectbox/objectbox-java/issues/771)
* Documentation and internal improvements.

### **V2.3.4 - 2019/03/19** <a href="#v-2-2-2018-09-27" id="v-2-2-2018-09-27"></a>

* Avoid UnsatisfiedLinkError on Android devices that are not identifying as Android correctly
* Fix displaying large objects in Object Browser 32 bit
* Kotlin properties starting with "is" of any type are detected
* Add `objectbox-kotlin` to dependencies if `kotlin-android` plugin is applied (previously only for `kotlin` plugin)
* @BaseEntity classes can be generic

### **V2.3.3 - 2019/02/14** <a href="#v-2-2-2018-09-27" id="v-2-2-2018-09-27"></a>

* Fixed a bug introduced by V2.3.2 affecting older Android versions 4.3 and below

### **V2.3.2 - 2019/02/04** <a href="#v-2-2-2018-09-27" id="v-2-2-2018-09-27"></a>

* Potential work around for UnsatisfiedLinkError probably caused by installation errors mostly in alternative app markets
* Support for Android Gradle Plugin 3.3.0: resolves deprecated API usage warnings.

### **V2.3.1 - 2019/01/08** <a href="#v-2-2-2018-09-27" id="v-2-2-2018-09-27"></a>

* Fixed a corner case for Box.getAll() after removeAll() to return a stale object if no objects are stored

### **V2.3 - 2018/12/30** <a href="#v-2-2-2018-09-27" id="v-2-2-2018-09-27"></a>

#### Improvements & Fixes

* Query improvements: findIds and LazyList also consider the order; offset and limit for findIds
* Improved 32 bit support: Windows 32 version officially deployed, fixed a corner case crash
* Property queries for a boolean property now allow sum()
* Added Box.isEmpty()
* Supporting older Linux distributions (now starting at e.g. Ubuntu 16.04 instead of 18.04)
* Fix for a corner case with Box.count() when using a maximum
* Minor improvements to the ObjectBox code generator
* Android: set extractNativeLibs to false to avoid issues with extracting the native library

### **V2.2 - 2018/09/27**

**Improvements & Fixes**

* *Fix:* the unique check for string properties had false positives resulting in UniqueViolationException. This occurs only in combination with IndexType.HASH (the default) when hashes actually collide. We advise to update immediately to the newest version if you are using hashed indexes.
* The release of new [ObjectBox C API](https://github.com/objectbox/objectbox-c) made us change name of the JNI library

  for better distinction. This should not affect you unless you depended on that (internal) name.
* Improved compatibility with class transformers like Jacoco
* Fixed query links for M:N backlinks
* Improved error messages for the build tools
* The Object Browser AAR now includes the required Android permissions

### **V2.1 - 2018/08/16**

#### Minor Improvements & Fixes

* Entity counts are now cached for better performance
* Deprecated aggregate function were removed (deprecation in 1.4 with introduction of PropertyQuery)
* Object browser hot fix: the hashed indexes introduced in 2.0 broke the object browser
* Object browser fixes: filters with long ints, improved performance in the schema view
* NPE fix in ToOne
* Added a specific NonUniqueResultException if a query did not return an expected unique result

### **V2.0 - 2018/07/25**

#### New Features/Improvements

ObjectBox 2.0 introduces [index types](/entity-annotations#index-types-string) for String. Before, every index used the property **value** for all look-ups. Now, ObjectBox can also use a **hash** to build an index. Because `String` properties are typically taking more space than scalar values, ObjectBox **switched the default index type to hash for strings.**

**When migrating data from pre-2.0 ObjectBox versions**, for String properties with a plain @Index annotation this will update the indexes automatically: the old value-based indexes will be deleted and the new hash-bashed indexes will be built.

A side effect of this is that the database file might grow in the process. If you want to prevent this, you can instruct ObjectBox to keep using a value-based index for a `String` property by specifying the index `type` using `@Index(type = IndexType.VALUE)`.

Other changes:

* Links and relation completeness and other features already announced in the 2.0 beta
* Unique constraint for properties via [@Unique annotation](/entity-annotations#unique-constraints)
* Support for char type (16 bit)
* [RX lib](/data-observers-and-rx#objectbox-rxjava-extension-library) deployed in JCenter
* Rework of Query APIs: type safe properties (property now knows its owning entity)
* Allow query conditions of links using properties (without parameter alias)
* Query performance improvements when using order
* [Property based count](/queries#aggregating-values): query for non-null or unique occurrences of entity properties (non-null and unique)
* Additional query conditions for strings: "greater than", "less than", "in"
* Added query conditions for byte arrays
* Set query parameters for "in" condition (int\[] and long\[])

### **V2.0 beta** – 2018/06/26

#### New Features/Improvements

* Query across relation bounds [using links](/queries#add-query-conditions-for-related-entities-links) (aka "join"): queries just got much more powerful. For example, query for orders that have a customer with an address on "Sesame Street". Or all persons, who have a grand parent called "Alice".
* [Backlinks for to-many relations](/relations#access-many-to-many-in-the-reverse-direction): now ObjectBox is "relation complete" with a bi-directional many-to-many relation.
* Query performance improvements: getting min/max values of indexed properties in constant time
* Android: added [Paging library support](/android/paging-architecture-components) (architecture components)
* [Kotlin extensions](/kotlin-support#using-the-provided-extension-functions): more Kotlin fun with ObjectBox KTX
* [Query parameters aliases](/queries#reusing-queries-and-parameters): helps setting query parameters in complex scenarios (e.g. for properties of linked entities)
* Improved query parameter verification
* Many internal improvements to keep us going fast in the future

### **V1.5.0** – 2018/04/17

#### New Features/Improvements

* Full support for [Android local tests](/android/android-local-unit-tests): use full ObjectBox features in local tests
* New count method optimized for a given maximum count
* Gradle option to [define the package for MyObjectBox](/advanced/advanced-setup#change-the-myobjectbox-package) explicitly
* Query condition startsWith now uses index if available for better performance

#### Fixes

* Fixed some static methods in BoxStore to ensure that the native lib is loaded
* Internal optimizations for 64 bit devices
* Some fixes for entities in the default package
* Entity can be named `Property`, no longer conflicts with ObjectBox Property class
* Property queries for strings crashed on some Android devices if there were more than 512 results
* Object Browser uses less threads
* Object Browser now displays negative int/long values correctly
* Changes to relations object in constructors were overwritten when constructors delegated to other constructors

### **V1.4.4** – 2018/03/08

#### New Features/Improvements

* Supply an initial database file using BoxStoreBuilder
* Gradle plugin detects [plain Java projects](/java-desktop-apps#objectbox-embedded-database-for-java-desktop-apps) and configures dependencies
* Improved Box.removeAll() performance for entities that have indexes or relations

#### Fixes

* Fixed converting from arrays in entities
* Fixed @NameInDb
* Fixed Gradle “androidTestCompile is obsolete” warning

### **V1.4.3** – 2018/03/01

#### New Features

* macOS support: with Linux, Windows, and macOS, ObjectBox now supports all major desktop/server platforms. Use it for local unit tests or standalone Java applications.

#### Fixes

* Fixed BoxStore.close being stuck in rare scenarios
* Fixed an issue with char properties in entities

### **V1.4.2** – 2018/02/25

**Note: This release requires the** [**Android Gradle Plugin 3.0.0**](https://developer.android.com/studio/releases/gradle-plugin.html#3-0-0) **or higher.**

#### Improvements

* JCenter: we’ve moved the ObjectBox artifacts to the JCenter repository. This simplifies set up and improves accessibility (e.g. JCenter is not blocked from China).
* Instant App support (only with Android Gradle Plugin 3.0.0 or higher)

### **V1.4.1** – 2018/01/23

#### Improvements

* Added DbExceptionListener as a central place to listen for DB related exceptions
* Minor improvements for ToMany and generated Cursor classes

#### Fixes

* ToMany: fixed handling of duplicate entries (e.g. fixes swap and reverse operations)
* ToMany: fixed removal of non-persisted element for standalone relations

### **V1.4.0** – 2018/01/11

#### New Features

* Property queries that return individual properties only (including distinct values, unique, null values, primitive result arrays or scalars)
* Entity inheritance (non-polymorphic)
* 50% size reduction of native libraries

### **V1.3.4** – 2017/12/07

#### Improvements

* ToOne now implements equals() and hashCode() based on the targetId property
* Android ABI x86\_64 was added to the aar

#### Fixes

* ID verification does not complain about “resurrected” objects that were loaded, removed, and put again
* Fixed setting Query parameters for Date type
* Fixes for ObjectBox browser

### **V1.3.3 (1.3.x)** – 2017/12/04

Please update to the latest version. We made important changes and fixes under the hood to make ObjectBox perform better, generally, and especially in concurrent scenarios. In addition, 1.3.x comes with several improvements for developers.

#### Improvements

* [Flag for query parameter logging](http://objectbox.io/files/objectbox-java/current/io/objectbox/DebugFlags.html)
* Object browser lets you download all entities as JSON
* Object browser efficiency improvements: introduced streamed processing to reduce memory consumption and increase performance for large data sets
* Improved transaction logging, e.g. numbered transactions and waiting times for write transactions
* Closing the store (e.g. for tests, an app should just leave it open) will wait for any ongoing write transaction to finish
* Two additional [overloads for static BoxStore.deleteAllFiles()](http://objectbox.io/files/objectbox-java/current/io/objectbox/BoxStore.html#deleteAllFiles-java.io.File-)
* Added automatic retries for read transactions; also configurable for queries

#### Fixes

* Fixes for concurrent setups (multi threaded, in live apps with up to 100 threads); internally we improved our testing automation and CI infrastructure significantly
* Fix for sumDouble throwing an exception
* Fixed ProGuard rule for ToOne

### **V1.2.1** – 2017/11/10

#### Improvements

* Improved debug logging for transactions and queries: enable this using BoxStoreBuilder.debugFlags(…) with values from the [DebugFlags](http://objectbox.io/files/objectbox-java/current/io/objectbox/DebugFlags.html) class
* Improved package selection for MyObjectBox if you use entities in multiple packages (please check if you need to adjust your imports after the update)
* ObjectBox Browser’s UI is more compact and thus better usable on mobile devices

#### Fixes

* Fix for ObjectBoxLiveData firing twice

### **V1.2.0** – 2017/10/31

Compatibility note: We removed some Box.find methods, which were all tagged as @Temporary. Only the Property based ones remain (for now, also @Temporary).

#### New Features

* [ObjectBoxLiveData](/android/livedata-architecture-components#objectbox-livedata-with-android-architecture-components): Implements LiveData from Android Architecture Components
* Object ID based methods for [ToMany](http://objectbox.io/files/objectbox-java/current/io/objectbox/relation/ToMany.html): getById, indexOfId, removeById
* More robust Android app directory detection that works around Android bugs
* Using the new official FlatBuffers Maven dependency (FlatBuffer is not anymore embedded in the artifact)
* UI improvements for ObjectBox browser
* Other minor improvements

#### Fixes

* Fixed query order by float and double
* Fixed an missing import if to-many relations referenced a entity in another package
* Other minor fixes

### **V1.1.0** – 2017/10/03

#### New Features

* Object Browser to view DB contents (Android)
* Plain Java support to run ObjectBox on Windows and Linux
* Added ToMany.hasA()/hasAll() to simplify query filters in Java
* Sort query result via Comparator
* Improved error messages on build errors
* Internal clean up, dropping legacy plugin

#### Fixes

* Annotation processor detects boolean getters starting with “is”
* Fixed a NPE with eager and findFirst() when there is no result

### **V1.0.1** – 2017/09/10

First bug fix release for [ObjectBox 1.0](http://objectbox.io/objectbox-1-0/).

#### New Features

* ToMany allows setting a Comparator to order the List (experimental)

#### Fixes

* Fix UID assignment process: use @Uid without value to see options (pin UID, reset/change)
* Fix relation code generation for entities in different packages
* Fix Kotlin extension functions in transformed (library) project
* Fix ToOne access if field is inaccessible (e.g. in Kotlin data classes if they are part of constructor – lateinit were OK)

### **V1.0.0** – 2017/09/04

ObjectBox is out of beta! See our [announcement blog post](http://objectbox.io/objectbox-1-0/) for details.

#### New Features

* Eager loading of relations via query builder
* Java filters as query post-processing
* Minor improvements like a new callInReadTx method and making Query.forEach breakable

#### Fixes

* Fixed two corner cases with queries

### V0.9.15 (beta) 2017/08/21 Hotfixes

#### Fixes

* Fixed: Android flavors in caused the model file (default.json) to be written into the wrong folder (inside the build folder) causing the build to fail
* Fixed: failed builds if entity constructor parameters are of specific types

### V0.9.14 (beta) 2017/08/14 Standalone relations, new build tools

For upgrade notes, please check the [announcement post](http://objectbox.io/objectbox-db-0-9-14/).

#### New Features

* No more in-place code generation: Java source code is all yours now. This is based on the new build tool chain introduced in 0.9.13. Thus Kotlin and Java share the same build system. The old Java-based plugin is still available (plugin ID “io.objectbox.legacy”) in this version.
* “Standalone” to-many relations (without backing to-one properties/relations)
* Gradle plugin tries to automatically add runtime dependencies (also (k)apt, but this does not always work!?)
* Improved error reporting

#### Fixes

* Fixed the issue causing a “Illegal state: Tx destroyed/inactive, writeable cursor still available” error log

### V0.9.13 (beta) 2017/07/12 Kotlin Support

#### New Features

* Kotlin support (based on a new annotation processor)
* Started “object-kotlin”, a sub-project for Kotlin extensions (tiny yet, let us know your ideas!)
* BoxStoreBuilder: added maxReaders configuration
* Get multiple entities by their IDs via Box methods (see get/getMap(Iterable))
* ToOne and ToMany are now serializable (which does not imply serializing is a good idea)
* ObjectBox may now opt to construct entities using the no-args constructor if the all-args constructor is absent
* Prevents opening two BoxStores for the same DB file, which may have side effects that are hard to debug
* Various minor and internal improvements and fixes

#### Fixes

* Fixed ToOne without an explicit target ID property
* Fixed type check of properties to allow ToMany (instead of List)
* Fixed @Convert in combination with List
* Fixed a race condition with cursor deletion when Java’s finalizer kicked in potentially resulting in a SIGSEGV
* Fixed a leak with potentially occurring with indexes

### V0.9.12 (beta) 2017/05/08 ToMany class

* **Update 2017/05/19:** We just released 0.9.12.1 for the Gradle plugin (only), which fixes two problems with parsing of to-many relations.
* Added the new list type ToMany which represents a to-many relation. A ToMany object will be automatically assigned to List types in the entity, eliminating a lot of generated code in the entity.
* ToMany comes with change tracking: all changes (add/remove) are automatically applied to the DB when its hosting entity is persisted via put(). Thus, the list content is synced to the DB, e.g. their relationship status is updated and new entities are put.
* Streamlined annotations (breaking API changes):\
  @Generated(hash = 123) becomes @Generated(123),\
  @Property was removed,\
  @NameInDb replaces attributes in @Entity and the former @Property,\
  Backlinking to-many relations require @Backlink (only),\
  @Relation is now only used for to-one relations (and is subject to change in the next version)

### V0.9.11 (beta) 2017/04/25: Various improvements

* Smarter to-one relations: if you put a new object that also has a new to-one relation object, the latter will also be put automatically.
* Getters and setters for properties are now only generated if no direct field access is possible
* JSR-305 annotations (@Nullable and others) to help the IDE find problems in your code
* @Uid(-1) will reassign IDs to simplify some migrations (docs will follow soon)
* No more getter for ToOne objects in favor of direct field access
* Quite a few internal improvements (evolved EntityInfo meta info object, etc.)

### V0.9.10 (beta) 2017/04/10: Bug Fixes and minor improvements

#### New features and improvements

* Breaking API: Replaced “uid” attribute of @Entity and @Property with @Uid annotation
* An empty @Uid will retrieve the current UID automatically
* Some minor efficency improvements for read transactions
* Better DB resources clean up for internal thread pool

#### Bug fixes

* Better compatibility with Android Gradle plugin
* Fixes for multithreaded reads of relation and index data
* Fixed compilation error in generated sources for Entities without non-ID properties

### V0.9.9 (beta) 2017/03/07: Bug Fixes

#### New features

* Query.forEach() to iterate efficiently over query result objects

#### Bug fixes

* Various bug fixes

### V0.9.8 (beta) 2017/02/22: Going Reactive

#### New features

* Data observers with reactive extensions for transformations, thread scheduling, etc.
* Optional RxJava 2 library
* OR conditions for QueryBuilder allow more powerful queries

#### Bug fixes

* Fixed: Changing the order of an entity’s properties could cause errors in some cases
* Fixed: Querying using relation IDs

### V0.9.7 (beta) 2017/02/10

#### New features

* LazyList returned by Query: another query option to defer getting actual objects until actually accessing them. This enables memory efficient iterations over large results. Also minimizes the time for a query to return. Note: LazyList cannot be combined with order specifications just yet.
* QueryBuilder and Query now support Date and boolean types directly
* QueryBuilder supports now a notIn opperator
* put() now uses entity fields directly unless they are private (can be more efficient than calling getters)

#### Breaking internal changes

At this early point in the beta we decided to break backward compatibility. This allowed us to make important improvements without worrying about rather complex migrations of previous versions. We believe this was a special situation and future versions will likely be backward compatible although we cannot make promises. If you intend to publish an app with ObjectBox it’s a good idea to contact us before.

* The internal data format was optimized to store data more compact. Previous database files are not compatible and should be deleted.
* We improved some details how IDs are used in the meta model. This affects the model file, which is stored in your project directory (objectbox-models/default.json). Files created by previous versions should be deleted.

### V0.9.6 (first public beta release) 2017/01/24

See [ObjectBox Announcement](http://greenrobot.org/announcement/introducing-objectbox-beta/)


