0

Since Entity store is throwing out when storing null value, I managed to get a "hack" to save a null value into it. However I am not sure if my approach is futile.

Here's a snippet:

entityStore.executeInTransaction(new StoreTransactionalExecutable() {
                @Override
                public void execute(@NotNull final StoreTransaction txn) {

                    try {
                        entityStore.registerCustomPropertyType(txn, UndefinedIterable.class, UndefinedBinding.BINDING);
                    } catch (Exception e) {

                    }

                    final Entity entity = txn.newEntity(storeName);
                    Iterator<String> it = comparableMap.keySet().iterator();
                    while (it.hasNext()) {
                        String key = it.next();
                        Comparable value = comparableMap.get(key);
                        if(value == null) {
                            entity.setProperty(key, new UndefinedIterable());
                        } else {
                            entity.setProperty(key, value);
                        }
                    }

First question here is, is it safe to registerCustomPropertyType over and over again, since this method will be called each time the server gets a POST request.

Next is the UndefinedIterable even needed here?

Here's the complete code

UndefinedIterable.java

public class UndefinedIterable implements Serializable, ByteIterable {

    private byte[] bytes;

    public UndefinedIterable() {
        bytes = "null".getBytes();
    }

    @Override
    public ByteIterator iterator() {
        return new ArrayByteIterable(bytes).iterator();
    }

    @Override
    public byte[] getBytesUnsafe() {
        return bytes;
    }

    @Override
    public int getLength() {
        return bytes.length;
    }

    @NotNull
    @Override
    public ByteIterable subIterable(int offset, int length) {
        return null;
    }

    @Override
    public int compareTo(@NotNull ByteIterable o) {
        return 0;
    }
}

UndefinedBinding.java

public class UndefinedBinding extends ComparableBinding {

    public static final UndefinedBinding BINDING = new UndefinedBinding();

    @Override
    public Comparable readObject(@NotNull ByteArrayInputStream stream) {
        try {
            byte[] serialized = ByteStreams.toByteArray(stream);
            Comparable deserialized = deserialize(serialized, Comparable.class);
            return deserialized;
        } catch (Exception e) {

        }
        return null;
    }

    @Override
    public void writeObject(@NotNull LightOutputStream output, @NotNull Comparable object) {
        byte[] serialized = serialize(object);
        output.write(serialized);
    }

    public static byte[] serialize(Object obj) {
        try {
            try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
                 ObjectOutput out = new ObjectOutputStream(bos)) {
                out.writeObject(obj);
                return bos.toByteArray();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static <T> T deserialize(byte[] data, Class<T> clazz) {
        try {
            ByteArrayInputStream in = new ByteArrayInputStream(data);
            ObjectInputStream is = new ObjectInputStream(in);
            return (T) is.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

}

I am afraid that my approach might be a bit overkill for the simple job of saving a null value?

quarks
  • 33,478
  • 73
  • 290
  • 513

1 Answers1

1

It's safe to registerCustomPropertyType several times, though it is intended to be called usually once on an init stage.

If I really need to distinguish lack of property value and property having null value, then I'd try to define non-null values replacing null. For String, it can be hex representation of an UUID. For Integer, Integer.MIN_VALUE or Integer.MAX_VALUE, etc. Don't use values of mixed types for a single property, otherwise search by property value or range search won't work.

Vyacheslav Lukianov
  • 1,913
  • 8
  • 12
  • Aside from this, does is mean having mix types sort and find would not work? – quarks Aug 21 '18 at 16:05
  • Does it mean that it is impossible to store "null" value for a property? In this case I guess the best solution is to do `entity.deleteProperty(key);` – quarks Aug 21 '18 at 16:10
  • In relation to this also, what are the options if we want to used Xodus as "schemaless", I mean a given property we store in a Entity may have two different types? – quarks Aug 21 '18 at 16:26
  • 1
    @xybrek It's impossible to compare values of different types, that's why mixing types of values of a _single_ property breaks sort and find. And yes, it's impossible to store "null" value, use `deleteProperty` instead. – Vyacheslav Lukianov Aug 21 '18 at 16:30