Entity Annotations

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, for example:

Java
Kotlin
@Entity
public class User {
@Id
private long id;
private String name;
// Not persisted:
@Transient
private int tempUsageCount;
// Getters and setters...
}
@Entity
data class User(
@Id var id: Long = 0,
var name: String? = null,
// Not persisted:
@Transient var tempUsageCount: Int = 0
)

The @Entity annotation identifies the class User as a persistable entity. This will trigger ObjectBox to generate persistence code tailored for this class.

Note:

  • It’s often good practice to model entities as “dumb” data classes (POJOs) with just properties.

  • For Kotlin: When using a data class, add default values for all properties. This will ensure your data class will have a constructor that can be called by ObjectBox. (Technically this is only required if using custom or transient properties or relations, but it's a good idea to do it always.)

Object IDs: @Id

In ObjectBox, entities must have one @Id property of type long (or Long in Kotlin) to efficiently get or reference objects. You can use the nullable type java.lang.Long, but we do not recommend it.

Java
Kotlin
@Entity
public class User {
@Id public long id;
...
}
@Entity
data class User(
@Id var id: Long = 0,
...
)

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:

Java
Kotlin
@Entity
class StringIdEntity {
@Id public long id;
public String uid;
}
StringIdEntity entity = box.query()
.equal(StringIdEntity_.uid, uid)
.build().findUnique()
@Entity
data class StringIdEntity(
@Id var id: Long = 0,
var uid: String? = null
)
val entity = box.query()
.equal(StringIdEntity_.uid, uid)
.build().findUnique()

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 generated Cursor classes). You have two options:

  1. Give your property fields at least “package private” (not “private”) visibility. In Kotlin, you can use @JvmField.

  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 (for Kotlin data classes, make sure all properties have a default value instead). For example:

Java
Kotlin
@Entity
public class User {
@Id private long id;
private String name;
// Not persisted:
@Transient private int tempUsageCount;
public User() { /* Default constructor */ }
public User(id, name) {
this.id = id;
this.name = name;
}
// Getters and setters for properties...
}
// Ensure all properties have default values:
@Entity
data class User(
@Id var id: Long = 0,
var name: String? = null,
// Not persisted:
@Transient var tempUsageCount: Int = 0
)

Basic annotations for entity properties

Java
Kotlin
...
@NameInDb("username")
private String name;
@Transient
private int tempUsageCount;
...
...
@NameInDb("username")
var name: String? = null,
@Transient
var tempUsageCount: Int = 0,
...

@NameInDb lets you define a name on the database level for a property. This allows you to rename the field (or property in Kotlin) without affecting the property name on the database level.

Note:

  • To rename properties and even entities you should use @Uid annotations instead.

  • @NameInDb only works with inline constants to specify a column name.

@Transient (or alternatively the transient modifier) marks properties that should not be persisted, like the temporary counter above. static properties will also not be persisted.

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
...
@Index
private String name;
...
...
@Index
var name: String? = null,
...

@Index is currently not supported for byte[], float and double in Java or the equivalent ByteArray , Float and Double in Kotlin.

An index stores additional information in the database to make look-ups 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 does don'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)

Since 2.0.0

ObjectBox 2.0 introduced index types. 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.

You can instruct ObjectBox to use a value-based index for a String property by specifying the index type:

Java
Kotlin
...
@Index(type = IndexType.VALUE)
private String name;
...
...
@Index(type = IndexType.VALUE)
var name: String? = null,
...

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 best index based on property type (HASH for String, VALUE for others).

  • VALUE Uses property values to build index. For String, this may require more storage than a hash-based index.

  • HASH Uses 32-bit hash of property values to build 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 long hash of property values to build the index. Requires more storage than HASH and thus should not be the first choice in most cases.

When migrating data from pre-2.0 ObjectBox versions, the default index type for strings has changed from value to hash. 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 build. A side effect of this is that the database file might grow in the process. If you want to prevent this, you stick to the "old" index type using @Index(type = IndexType.VALUE).

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

Since 2.0.0

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

Java
Kotlin
...
@Unique
private String name;
...
...
@Unique
var name: String? = null,
...

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

Java
Kotlin
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.
}

Unique constraints are based on an index. You can further configure the index by adding an @Index annotation.

Relations

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

Triggering generation

Once your entity schema is in place, you can trigger the code generation process by compiling your project. For example using Build > Make project in Android Studio.

If you encounter errors after changing your entity classes, try to rebuild (clean, then build) your project to make sure old generated classes are cleaned.