Accessing Domain Objects Without Domain Classes
Usually, to access any field in a domain object, you would need to have the class of that object on the member’s
classpath. However, you may not want to add classes on the member. In this case, Hazelcast can return a GenericRecord
object to your Java application. This object gives you access to your domain object’s fields without having to
add a factory class to the classpath of your members or register a serializer for them.
Hazelcast is able to represent Portable and
Compact serialized objects as GenericRecord
.
For example, to access the fields of a domain object in an entry processor, you could do the following:
map.executeOnKey(key, (EntryProcessor<Object, Object, Object>) entry -> {
Object value = entry.getValue();
GenericRecord genericRecord = (GenericRecord) value;
int id = genericRecord.getInt32("id");
return null;
});
An alternative approach introduced in the previous Hazelcast releases is the User Code Deployment
feature to deploy the classes from the client to the cluster.
However, it has a restriction: you cannot upload
a new version of your class to the cluster if you use the portable versioning support.
Loading two different versions of the same class on the JVM is not a problem that we want to solve: using With the introduction of |
Creating New Portable Objects
To create a GenericRecord
object in portable format, use the GenericRecordBuilder.portable()
method:
ClassDefinition classDefinition = new ClassDefinitionBuilder(PORTABLE_FACTORY_ID, EMPLOYEE_CLASS_ID)
.addStringField("name")
.addIntField("id")
.build();
GenericRecord namedRecord = GenericRecordBuilder.portable(classDefinition)
.setString("name", "foo")
.setInt32("id", 123)
.build();
Note that the class definitions are better to be created once and
used when creating different instances of the same GenericRecord
object.
Creating New Compact Objects
To create a GenericRecord
object in compact serialization format, use the GenericRecordBuilder.compact()
method:
GenericRecord namedRecord = GenericRecordBuilder.compact("employee")
.setString("name", "foo")
.setInt32("id", 123)
.build();
Note that there is no need to create a class definition, or a schema in this case. A schema will be created from the fields of the builder automatically.
Adding and Changing Values in Domain Objects
We have also added two convenience methods in GenericRecord
for you to
avoid passing a class definition or re-calculating schema. For example, if you want to modify a value and
put it back using an entry processor, you don’t need to create a class definition or pay the cost of re-calculating
a schema. Instead, you can create a builder from the GenericRecord
object which carries the same class definition
or schema as follows:
map.executeOnKey("key", (EntryProcessor<Object, Object, Object>) entry -> {
GenericRecord genericRecord = (GenericRecord) entry.getValue();
GenericRecord modifiedGenericRecord = genericRecord.newBuilder()
.setString("name", "Kermit")
.setInt64("id", 4)
.setInt32("age", 20)
.setString("surname", "The Frog")
.build();
entry.setValue(modifiedGenericRecord);
return null;
});
Another convenience method is cloneWithBuilder()
. This is useful if you want to update only
a couple of fields from the original genericRecord
. In that case, the new builder carries
both classDefinition
or schema
and values from the original
genericRecord
. Here is the same example where we just update the age:
map.executeOnKey("key", (EntryProcessor<Object, Object, Object>) entry -> {
GenericRecord genericRecord = (GenericRecord) entry.getValue();
GenericRecord modifiedGenericRecord = genericRecord.cloneWithBuilder()
.setInt32("age", 22)
.build();
entry.setValue(modifiedGenericRecord);
return null;
});