Custom Types
How to store types in ObjectBox that are not supported by default, recommendations for storing enums.

ObjectBox - Supported Types

With ObjectBox you can store pretty much any type (class), given that it can be converted to any of the built-in types.
ObjectBox can store the following built-in types without a converter:
Java
Kotlin
Dart
1
boolean, Boolean
2
int, Integer
3
short, Short
4
long, Long
5
float, Float
6
double, Double
7
byte, Byte
8
char, Character
9
byte[]
10
String
11
Date // Time with millisecond precision.
12
13
// As of 2.9.2-RC4 the following work out of the box:
14
String[]
15
@Type(DatabaseType.DateNano) long, Long // Time with nanosecond precision.
Copied!
1
Boolean, Boolean?
2
Int, Int?
3
Short, Short?
4
Long, Long?
5
Float, Float?
6
Double, Double?
7
Byte, Byte?
8
Char, Char?
9
ByteArray
10
String, String?
11
Date, Date? // Time with millisecond precision.
12
13
// As of 2.9.2-RC4 the following work out of the box:
14
Array<String>
15
@Type(DatabaseType.DateNano) Long, Long? // Time with nanosecond precision.
Copied!
1
// all fields are supported as both nullable and non-nullable
2
3
bool
4
int
5
double
6
String
7
List<String>
8
9
// Time with millisecond precision.
10
// Note: always restored in default time zone.
11
@Property(type: PropertyType.date)
12
DateTime date;
13
14
// Time with millisecond precision restored in UTC time zone.
15
@Transient()
16
DateTime utcDate;
17
18
int get dbUtcDate => utcDate.millisecondsSinceEpoch;
19
20
set dbUtcDate(int value) {
21
utcDate = DateTime.fromMillisecondsSinceEpoch(value, isUtc: true);
22
}
23
24
// Time with nanosecond precision.
25
@Property(type: PropertyType.dateNano)
26
DateTime nanoDate;
27
28
@Property(type: PropertyType.byte)
29
int byte; // 1 byte
30
31
@Property(type: PropertyType.short)
32
int short; // 2 bytes
33
34
@Property(type: PropertyType.char)
35
int char; // 1 bytes
36
37
@Property(type: PropertyType.int)
38
int int32; // 4 bytes
39
40
@Property(type: PropertyType.float)
41
double float; // 4 bytes
42
43
// And three supported types for a byte vector (binary data):
44
@Property(type: PropertyType.byteVector)
45
List<int> byteList;
46
47
Int8List int8List;
48
49
Uint8List uint8List;
Copied!
To store any other type, configure a 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:
Java
Kotlin
Dart
1
@Entity
2
public class User {
3
@Id
4
public long id;
5
6
@Convert(converter = RoleConverter.class, dbType = Integer.class)
7
public Role role;
8
9
public enum Role {
10
DEFAULT(0), AUTHOR(1), ADMIN(2);
11
12
final int id;
13
14
Role(int id) {
15
this.id = id;
16
}
17
}
18
19
public static class RoleConverter implements PropertyConverter<Role, Integer> {
20
@Override
21
public Role convertToEntityProperty(Integer databaseValue) {
22
if (databaseValue == null) {
23
return null;
24
}
25
for (Role role : Role.values()) {
26
if (role.id == databaseValue) {
27
return role;
28
}
29
}
30
return Role.DEFAULT;
31
}
32
33
@Override
34
public Integer convertToDatabaseValue(Role entityProperty) {
35
return entityProperty == null ? null : entityProperty.id;
36
}
37
}
38
}
Copied!
1
@Entity
2
data class User(
3
@Id
4
var id: Long = 0,
5
@Convert(converter = RoleConverter::class, dbType = Int::class)
6
var role: Role? = null
7
)
8
9
enum class Role(val id: Int) {
10
DEFAULT(0), AUTHOR(1), ADMIN(2);
11
}
12
13
class RoleConverter : PropertyConverter<Role?, Int?> {
14
override fun convertToEntityProperty(databaseValue: Int?): Role? {
15
if (databaseValue == null) {
16
return null
17
}
18
for (role in Role.values()) {
19
if (role.id == databaseValue) {
20
return role
21
}
22
}
23
return Role.DEFAULT
24
}
25
26
override fun convertToDatabaseValue(entityProperty: Role?): Int? {
27
return entityProperty?.id
28
}
29
}
Copied!
1
enum Role {
2
unknown,
3
author,
4
admin
5
}
6
7
@Entity()
8
class User {
9
int id;
10
11
// The Role type is not supported by ObjectBox.
12
Role? role;
13
14
// So define a field with a supported type,
15
// that is backed by the role field.
16
int? get dbRole {
17
_ensureStableEnumValues();
18
return role?.index;
19
}
20
21
set dbRole(int? value) {
22
_ensureStableEnumValues();
23
if (value == null) {
24
role = null
25
} else {
26
role = Role.values[value]; // throws a RangeError if not found
27
28
// or if you want to handle unknown values gracefully:
29
role = value >= 0 && value < Role.values.length
30
? Role.values[value]
31
: Role.unknown;
32
}
33
}
34
35
void _ensureStableEnumValues() {
36
assert(Role.none.index == 0);
37
assert(Role.author.index == 1);
38
assert(Role.admin.index == 2);
39
}
40
}
Copied!

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

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).
ObjectBox for Dart has built-in support for String lists.

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).
Last modified 2mo ago