Links

Entity Annotations

ObjectBox database persistence for Java is based on objects for object-oriented programming instead of SQL. Learn how to persist entities with entity annotations in this tutorial section.

ObjectBox - Database Persistence with Entity Annotations

ObjectBox is a database that persists objects. For a clear distinction, we sometimes call those persistable objects 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:
Java
Kotlin
Dart
@Entity
public class User {
@Id
private long id;
private String name;
// Not persisted:
@Transient
private int tempUsageCount;
// TODO: getters and setters.
}
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.
@Entity
data class User(
@Id var id: Long = 0,
var name: String? = null,
// Not persisted:
@Transient var tempUsageCount: Int = 0
)
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.)
@Entity()
class User {
// Annotate with @Id() if name isn't "id" (case insensitive).
int id = 0;
String? name;
// Not persisted:
@Transient
int tempUsageCount = 0;
)
It’s often good practice to model entities as “dumb” data classes with just properties.
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:
User({this.id, this.name});

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.
Java
Kotlin
Dart
@Entity
public class User {
@Id public long id;
// Note: You can use the nullable java.lang.Long, but we do not recommend it.
...
}
@Entity
data class User(
@Id var id: Long = 0,
...
)
@Entity()
class User {
// Annotate with @Id() if name isn't "id" (case insensitive).
int id;
...
}
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 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 value for this secondary ID.
Java
Kotlin
Dart
@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()
@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()
@Entity()
class StringIdEntity(
int id;
@Index() // or alternatively use @Unique()
String uid;
)
final objects = box.query(StringIdEntity_.uid.equals('...')).build().find();
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 for details.
If you need to assign IDs by yourself have a look at how to switch to self-assigned IDs and what side effects apply.

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. 1.
    Make sure properties do not have private visibility.
  2. 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.
Java
Kotlin
Dart
@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;
}
}
// 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
)
@Entity()
class User {
int id;
String? _name;
String get name {...}
set name(String value) {...}
User(this.id);
}

Supported property types

ObjectBox can store almost any type (class) of property as long as it can be converted to one of the built-in types. See the dedicated page for details:

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.
Java
Kotlin
Dart
@Transient
private int notPersisted;
@Transient
var notPersisted: Int = 0
@Transient()
int? notPersisted;

NameInDb

Only available for Java/Kotlin at the moment
@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.
  • We recommend using @Uid annotations to rename properties and even entities instead.
  • @NameInDb only works with inline constants to specify a column name.
Java
Kotlin
@NameInDb("username")
private String name;
@NameInDb("username")
var name: String? = null

Property Indexes with @Index

Annotate a property with @Index to create a database index for the corresponding database column. This can improve performance when querying for that property.
Java
Kotlin
Dart
@Index
private String name;
@Index is currently not supported for String[], byte[], float and double
@Index
var name: String? = null
@Index is currently not supported for Array<String>, ByteArray , Float and Double
@Index()
String name;
@Index is currently not supported for double and listsList<String>, List<int>, Uint8List, Int8List
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:
Java
Kotlin
Dart
@Index(type = IndexType.VALUE)
private String name;
@Index(type = IndexType.VALUE)
var name: String? = null
@Index(type: IndexType.value)
String name;
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.
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.

Unique constraints

Annotate a property with @Unique to enforce that values are unique before an entity is put:
Java
Kotlin
Dart
@Unique
private String name;
@Unique
var name: String? = null
@Unique()
String? name;
A put() operation will abort and throw a UniqueViolationException if the unique constraint is violated:
Java
Kotlin
Dart
try {
box.put(new User("Sam Flynn"));
} catch (UniqueViolationException e) {
// A User with that name already exists.
}
try {
box.put(User("Sam Flynn"))
} catch (e: UniqueViolationException) {
// A User with that name already exists.
}
try {
box.put(User('Sam Flynn'))
} on UniqueViolationException catch (e) {
// A User with that name already exists.
}
For a single property it is possible to specify that a conflicting object should be replaced instead of an exception being thrown:
Java
Kotlin
Dart
@Unique(onConflict = ConflictStrategy.REPLACE)
private String name;
@Unique(onConflict = ConflictStrategy.REPLACE)
var name: String? = null
@Unique(onConflict: ConflictStrategy.replace)
String? name;
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.
Unique constraints are based on an index, so it is possible to further configure the index with an @Index annotation.

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.
Java
Kotlin
Dart
// Store 64-bit integer as time in nanoseconds.
@Type(DatabaseType.DateNano)
private long timeInNanos;
// Store 64-bit integer as time in nanoseconds.
@Type(DatabaseType.DateNano)
var timeInNanos: Long = 0
// 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;

Relations

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

Triggering code generation

Once your entity schema is in place, you can trigger the code generation process.