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 add annotations to them. Then ObjectBox can do its magic with your entities.

Here is an example:

@Entity
public class User {
@Id
private long id;
private String name;
@Transient
private int tempUsageCount; // not persisted
// getters and setters for id and user ...
}

The @Entity annotation identifies the Java 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.

  • If you prefer to use Kotlin to define your entity classes, have a look at the ObjectBox and Kotlin documentation.

Object IDs: @Id

In ObjectBox, every object has an ID of type long to efficiently get or reference objects. Use the @Id annotation on a long property in your entity:

@Entity
public class User {
@Id
private long id;
...
}

To assign IDs by yourself you can change the ID annotation to @Id(assignable = true). This will allow putting an entity with any valid ID, including zero to let ObjectBox auto assign a new ID.

When using self-assigned IDs additional constraints apply.

See the Object ID docs for general information.

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 to look up entities by your application specific ID. An example:

@Entity
public class User {
@Id
private long id;
@Index
private String serverUid;
...
}

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

@Entity
public class User {
@Id private long id;
private String name;
@Transient private int tempUsageCount; // not persisted
public User() { /* default constructor */ }
public User(id, name) {
this.id = id;
this.name = name;
}
// getters and setters for properties...
}

Basic annotations for entity properties

@Entity
public class User {
@NameInDb("USERNAME")
private String name;
@Transient
private int tempUsageCount;
...
}

@NameInDb lets you define a name on the database level for a property. This allows you to rename the Java field 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.

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.

@Index
private String name;

@Index is currently not supported for byte[], float and double

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 also can also use 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:

@Index(type = IndexType.VALUE)
private 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 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:

@Unique
private String name;

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

try {
box.put(new User("Sam Flynn"));
} catch (UniqueViolationException e) {
// 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.