mojtab23 mojtab23 - 1 year ago 54
Java Question

How to register custom property type to xodus `PersistentEntityStore`

I'm working with xodus database and I want to store

java.time.Instant
as property of my model object (
Diary
) so I implemented
InstantBinding
.

Next in repository object that I created to work with my
Diary
objects I registered
InstantBindig
in save and load methods of repository because
PersistentEntityStore.registerCustomPropertyType(@NotNull final StoreTransaction txn, @NotNull final Class<? extends Comparable> clazz,@NotNull final ComparableBinding binding);
needs
StoreTransaction txn
input.
and then when I'm using the repository I get this exception:

Exception in thread "JavaFX Application Thread" jetbrains.exodus.entitystore.EntityStoreException: Already registered property type id 9
at jetbrains.exodus.entitystore.tables.PropertyTypes.registerCustomPropertyType(PropertyTypes.java:77)
at jetbrains.exodus.entitystore.PersistentEntityStoreImpl.registerCustomPropertyType(PersistentEntityStoreImpl.java:520)
at io.github.mojtab23.diaries.DiaryRepository.lambda$saveDiary$0(DiaryRepository.java:35)
at jetbrains.exodus.entitystore.PersistentEntityStoreImpl.executeInTransaction(PersistentEntityStoreImpl.java:529)
at io.github.mojtab23.diaries.DiaryRepository.saveDiary(DiaryRepository.java:34)
at io.github.mojtab23.diaries.DiariesView.saveChanges(DiariesView.java:222)
at io.github.mojtab23.diaries.DiariesView.lambda$buildDiaryEditor$6(DiariesView.java:203)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Node.fireEvent(Node.java:8413)
at javafx.scene.control.Button.fire(Button.java:185)
at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:182)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:96)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:89)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:381)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$354(GlassViewEventHandler.java:417)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:416)
at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
at com.sun.glass.ui.View.notifyMouse(View.java:937)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)


So what is the right way of registering custom property type to xodus?

here is my classes:

Diary:

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import jetbrains.exodus.entitystore.EntityId;

public class Diary implements Cloneable {

private final ObjectProperty<Instant> timestamp;
private final StringProperty text;
private EntityId id;

public Diary() {
this.text = new SimpleStringProperty();
timestamp = new SimpleObjectProperty<>(Instant.now());
}

public Diary(String text, Instant time) {
this.text = new SimpleStringProperty(text);
timestamp = new SimpleObjectProperty<>(time);

}

public Diary(SimpleObjectProperty<Instant> timestamp, StringProperty text, EntityId id) {
this.timestamp = timestamp;
this.text = text;
this.id = id;
}

public EntityId getId() {
return id;
}

public void setId(EntityId id) {
this.id = id;
}

public Instant getTimestamp() {
return timestamp.get();
}

public void setTimestamp(Instant timestamp) {
this.timestamp.set(timestamp);
}

public ObjectProperty<Instant> timestampProperty() {
return timestamp;
}

public String getText() {
return text.get();
}

public void setText(String text) {
this.text.set(text);
}

public StringProperty textProperty() {
return text;
}

@Override
public String toString() {
return "Diary{" +
"id=" + id +
", timestamp=" + timestamp +
", text=" + text +
'}';
}

@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}


DiaryRepository:

import io.github.mojtab23.diaries.model.diary.Diary;
import jetbrains.exodus.entitystore.*;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;

import javax.annotation.PreDestroy;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;



@Repository
public class DiaryRepository {
private static final Logger LOGGER = LoggerFactory.getLogger(DiaryRepository.class);

private final PersistentEntityStore entityStore;
private final String entityType = "Diary";

public DiaryRepository() {
entityStore = PersistentEntityStores.newInstance(".DiariesAppData");


}


public void saveDiary(final Diary diary) {
entityStore.executeInTransaction((StoreTransaction txn) -> {
entityStore.registerCustomPropertyType(txn, Instant.class, InstantBinding.BINDING);

diaryToEntity(diary, txn);
});

}

public Entity diaryToEntity(final Diary diary, final StoreTransaction txn) {

final Entity diaryEntity = txn.newEntity(entityType);
diaryEntity.setProperty("text", diary.getText());

diaryEntity.setProperty("timestamp", diary.getTimestamp());
return diaryEntity;
}


public Diary entityToDiary(final Entity entity, final StoreTransaction txn) {


final @Nullable String text = (String) entity.getProperty("text");
final @Nullable Instant timestamp = (Instant) entity.getProperty("timestamp");

if (text != null) {
if (timestamp != null) {
return new Diary(text, timestamp);
} else
return new Diary(text, Instant.MIN);
}
return null;
}

public List<Diary> readAllDiaries() {

return entityStore.computeInReadonlyTransaction(txn -> {
entityStore.registerCustomPropertyType(txn, Instant.class, InstantBinding.BINDING);

final EntityIterable allDiaries = txn.getAll(entityType);
final List<Diary> diaryList = new ArrayList<>();
allDiaries.forEach(entity -> {
final Diary e = entityToDiary(entity, txn);
if (e != null) {
diaryList.add(e);
}
});
LOGGER.warn("Number of Diaries: " + diaryList.size());
return diaryList;

});

}

public void deleteAll() {

entityStore.executeInTransaction(txn -> {


txn.getAll(entityType).forEach(entity ->
{
if (!entity.delete()) {
LOGGER.warn("cant delete {}", entityToDiary(entity, txn));
}
});
});

}


@PreDestroy
public void atEnd() {
entityStore.close();
}

}


InstantBinding:

import jetbrains.exodus.ArrayByteIterable;
import jetbrains.exodus.ByteIterable;
import jetbrains.exodus.bindings.BindingUtils;
import jetbrains.exodus.bindings.ComparableBinding;
import jetbrains.exodus.util.LightOutputStream;
import org.jetbrains.annotations.NotNull;

import java.io.ByteArrayInputStream;
import java.time.Instant;

public class InstantBinding extends ComparableBinding {

public static final InstantBinding BINDING = new InstantBinding();

public static Instant entryToInstant(@NotNull final ByteIterable entry) {
return (Instant) BINDING.entryToObject(entry);
}

public static ArrayByteIterable instantToEntry(final Instant object) {
return BINDING.objectToEntry(object);
}

@Override
public Comparable readObject(@NotNull ByteArrayInputStream stream) {
final long l = BindingUtils.readLong(stream);
final int i = BindingUtils.readInt(stream);
return Instant.ofEpochSecond(l, i);
}

@Override
public void writeObject(@NotNull LightOutputStream output, @NotNull Comparable object) {
final Instant instant = (Instant) object;
output.writeUnsignedLong(instant.getEpochSecond() ^ 0x8000000000000000L);
output.writeUnsignedInt(instant.getNano() ^ 0x80000000);

}

}

Answer Source

It seems you are experiencing this bug. It's already fixed, the fix will be available in the version 1.0.6. Currently there are two workarounds:

  1. Surround the PersistentEntityStore.registerCustomPropertyType() call with try-catch block, catch EntityStoreException and ignore it. When the next release (1.0.6) is available make your project depending on it, and remove the try-catch block.
  2. Set your project depending on the 1.0.6-SNAPSHOT version published to the Sonartype snapshots repository.
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download