Interface IgniteBinary


  • public interface IgniteBinary
    Defines binary objects functionality. With binary objects you are able to:
    • Seamlessly interoperate between Java, .NET, and C++.
    • Make any object binary with zero code change to your existing code.
    • Nest binary objects within each other.
    • Automatically handle circular or null references.
    • Automatically convert collections and maps between Java, .NET, and C++.
    • Optionally avoid deserialization of objects on the server side (objects are stored in BinaryObject format).
    • Avoid need to have concrete class definitions on the server side.
    • Dynamically change structure of the classes without having to restart the cluster.
    • Index into binary objects for querying purposes.

    Working With Binaries Directly

    Once an object is defined as binary, Ignite will always store it in memory in the binary (i.e. binary) format. User can choose to work either with the binary format or with the deserialized form (assuming that class definitions are present in the classpath).

    To work with the binary format directly, user should create a special cache projection using IgniteCache.withKeepBinary() method and then retrieve individual fields as needed:

     IgniteCache<BinaryObject, BinaryObject> prj = cache.withKeepBinary();
    
     // Convert instance of MyKey to binary format.
     // We could also use BinaryBuilder to create the key in binary format directly.
     BinaryObject key = grid.binary().toBinary(new MyKey());
    
     BinaryObject val = prj.get(key);
    
     String field = val.field("myFieldName");
     
    Alternatively, if we have class definitions in the classpath, we may choose to work with deserialized typed objects at all times.
     IgniteCache<MyKey.class, MyValue.class> cache = grid.cache(null);
    
     MyValue val = cache.get(new MyKey());
    
     // Normal java getter.
     String fieldVal = val.getMyFieldName();
     
    If we used, for example, one of the automatically handled binary types for a key, like integer, and still wanted to work with binary binary format for values, then we would declare cache projection as follows:
     IgniteCache<Integer.class, BinaryObject> prj = cache.withKeepBinary();
     

    Automatic Binary Types

    Note that only binary classes are converted to BinaryObject format. Following classes are never converted (e.g., toBinary(Object) method will return original object, and instances of these classes will be stored in cache without changes):
    • All primitives (byte, int, ...) and there boxed versions (Byte, Integer, ...)
    • Arrays of primitives (byte[], int[], ...)
    • String and array of Strings
    • UUID and array of UUIDs
    • Date and array of Dates
    • Timestamp and array of Timestamps
    • Enums and array of enums
    • Maps, collections and array of objects (but objects inside them will still be converted if they are binary)

    Working With Maps and Collections

    All maps and collections in the binary objects are serialized automatically. When working with different platforms, e.g. C++ or .NET, Ignite will automatically pick the most adequate collection or map in either language. For example, ArrayList in Java will become List in C#, LinkedList in Java is LinkedList in C#, HashMap in Java is Dictionary in C#, and TreeMap in Java becomes SortedDictionary in C#, etc.

    Building Binary Objects

    Ignite comes with BinaryObjectBuilder which allows to build binary objects dynamically:
     BinaryBuilder builder = Ignition.ignite().binary().builder();
    
     builder.typeId("MyObject");
    
     builder.stringField("fieldA", "A");
     build.intField("fieldB", "B");
    
     BinaryObject binaryObj = builder.build();
     
    For the cases when class definition is present in the class path, it is also possible to populate a standard POJO and then convert it to binary format, like so:
     MyObject obj = new MyObject();
    
     obj.setFieldA("A");
     obj.setFieldB(123);
    
     BinaryObject binaryObj = Ignition.ignite().binary().toBinary(obj);
     
    NOTE: you don't need to convert typed objects to binary format before storing them in cache, Ignite will do that automatically.

    Binary Metadata

    Even though Ignite binary protocol only works with hash codes for type and field names to achieve better performance, Ignite provides metadata for all binary types which can be queried ar runtime via any of the type(Class) methods. Having metadata also allows for proper formatting of BinaryObject#toString() method, even when binary objects are kept in binary format only, which may be necessary for audit reasons.

    Dynamic Structure Changes

    Since objects are always cached in the binary binary format, server does not need to be aware of the class definitions. Moreover, if class definitions are not present or not used on the server, then clients can continuously change the structure of the binary objects without having to restart the cluster. For example, if one client stores a certain class with fields A and B, and another client stores the same class with fields B and C, then the server-side binary object will have the fields A, B, and C. As the structure of a binary object changes, the new fields become available for SQL queries automatically.

    Configuration

    By default all your objects are considered as binary and no specific configuration is needed. The only requirement Ignite imposes is that your object has an empty constructor. Note, that since server side does not have to know the class definition, you only need to list binary objects in configuration on the client side. However, if you list them on the server side as well, then you get the ability to deserialize binary objects into concrete types on the server as well as on the client.

    Here is an example of binary configuration (note that star (*) notation is supported):

     ...
     <!-- Explicit binary objects configuration. -->
     <property name="marshaller">
         <bean class="org.apache.ignite.marshaller.binary.BinaryMarshaller">
             <property name="classNames">
                 <list>
                     <value>my.package.for.binary.objects.*</value>
                     <value>org.apache.ignite.examples.client.binary.Employee</value>
                 </list>
             </property>
         </bean>
     </property>
     ...
     
    or from code:
     IgniteConfiguration cfg = new IgniteConfiguration();
    
     BinaryMarshaller marsh = new BinaryMarshaller();
    
     marsh.setClassNames(Arrays.asList(
         Employee.class.getName(),
         Address.class.getName())
     );
    
     cfg.setMarshaller(marsh);
     
    You can also specify class name for a binary object via BinaryTypeConfiguration. Do it in case if you need to override other configuration properties on per-type level, like ID-mapper, or serializer.

    Custom Affinity Keys

    Often you need to specify an alternate key (not the cache key) for affinity routing whenever storing objects in cache. For example, if you are caching Employee object with Organization, and want to colocate employees with organization they work for, so you can process them together, you need to specify an alternate affinity key. With binary objects you would have to do it as following:
     <property name="marshaller">
         <bean class="org.gridgain.grid.marshaller.binary.BinaryMarshaller">
             ...
             <property name="typeConfigurations">
                 <list>
                     <bean class="org.apache.ignite.binary.BinaryTypeConfiguration">
                         <property name="className" value="org.apache.ignite.examples.client.binary.EmployeeKey"/>
                         <property name="affinityKeyFieldName" value="organizationId"/>
                     </bean>
                 </list>
             </property>
             ...
         </bean>
     </property>
     

    Serialization

    Serialization and deserialization works out-of-the-box in Ignite. However, you can provide your own custom serialization logic by optionally implementing Binarylizable interface, like so:
     public class Address implements BinaryMarshalAware {
         private String street;
         private int zip;
    
         // Empty constructor required for binary deserialization.
         public Address() {}
    
         @Override public void writeBinary(BinaryWriter writer) throws BinaryException {
             writer.writeString("street", street);
             writer.writeInt("zip", zip);
         }
    
         @Override public void readBinary(BinaryReader reader) throws BinaryException {
             street = reader.readString("street");
             zip = reader.readInt("zip");
         }
     }
     
    Alternatively, if you cannot change class definitions, you can provide custom serialization logic in BinarySerializer either globally in BinaryConfiguration or for a specific type via BinaryTypeConfiguration instance.

    Similar to java serialization you can use writeReplace() and readResolve() methods.

    • readResolve is defined as follows: ANY-ACCESS-MODIFIER Object readResolve(). It may be used to replace the de-serialized object by another one of your choice.
    • writeReplace is defined as follows: ANY-ACCESS-MODIFIER Object writeReplace(). This method allows the developer to provide a replacement object that will be serialized instead of the original one.

    Custom ID Mappers

    Ignite implementation uses name hash codes to generate IDs for class names or field names internally. However, in cases when you want to provide your own ID mapping schema, you can provide your own BinaryIdMapper implementation.

    ID-mapper may be provided either globally in BinaryConfiguration, or for a specific type via BinaryTypeConfiguration instance.

    Query Indexing

    Binary objects can be indexed for querying by specifying index fields in QueryEntity inside of specific CacheConfiguration instance, like so:
     ...
     <bean class="org.apache.ignite.cache.CacheConfiguration">
         ...
         <property name="queryEntities">
             <list>
                 <bean class="QueryEntity">
                     <property name="type" value="Employee"/>
    
                     <!-- Fields available from query. -->
                     <property name="fields">
                         <map>
                         <entry key="name" value="java.lang.String"/>
    
                         <!-- Nested binary objects can also be indexed. -->
                         <entry key="address.zip" value="java.lang.Integer" />
                         </map>
                     </property>
    
                     <!-- Aliases for full property name in dot notation. -->
                     <property name="fields">
                         <map>
                         <entry key="address.zip" value="zip" />
                         </map>
                     </property>
    
                     <!-- Indexes configuration. -->
                     <property name="indexes">
                     <list>
                     <bean class="org.apache.ignite.cache.QueryIndex">
                         <property name="fields">
                              <map>
                              <!-- The boolean value is the acceding flag. -->
                              <entry key="name" value="true"/>
                              </map>
                         </property>
                     </bean>
                     </list>
                     </property>
                 </bean>
             </list>
         </property>
     </bean>