-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Layered entities example of storing/loading/updating? #315
Comments
Here is a failing junit test (java 22 with @Test public void test() {
FileUtils.deleteRecursively(STORE_FOLDER);
var dataRoot = new EclipseStoreDataRoot(Lazy.Reference(new Data()) , Lazy.Reference(new UsersStorage()));
var storageManager = EmbeddedStorage.start(dataRoot, STORE_FOLDER);
var userStorage = dataRoot.userStorage().get();
userStorage.initialize(storageManager); //set the storage manager which is a transient filed
var createdAuditEvent = new AuditEvent.Created(Instant.now(), null);
//Prepare an electronic address
var emailFlags = new BitSet();
ElectronicAddress.setForSecurity(emailFlags, true);
var events = new ConcurrentLinkedQueue<AuditEvent>();
events.add(createdAuditEvent);
var electronicAddress = ElectronicAddressCreator.New()
.id("[email protected]")
.flags(emailFlags)
.type(ElectronicAddress.Type.EMAIL)
.created(createdAuditEvent)
.auditEvents(Lazy.Reference(events))
.create();
//Prepare a pass key
var passkeyId = new PassKey.ID(new byte[]{1,2,3,4});
var passKeyEvents = new ConcurrentLinkedQueue<AuditEvent>();
passKeyEvents.add(createdAuditEvent);
var passKey = PassKeyCreator.New()
.id(passkeyId)
.created(createdAuditEvent)
.auditEvents(Lazy.Reference(passKeyEvents))
.credentialRecord(null)
.create();
//Register a new user with new pass key and electronic address
var userId = Tsid.from(100);
var user = UserCreator.New()//User.creator()
.id(userId)
.firstName("John")
.lastName("Smith")
.created(createdAuditEvent)
.userAttributes(new ConcurrentHashMap<>())
.create();
user.putAttribute(electronicAddress);
user.putAttribute(passKey);
userStorage.addNewUser(user);
return;
} In the User entity interface, I use (generated) Updaters (ElectronicAddress and PassKey) for public interface User extends WithHumanName, WithId<Tsid>, WithStatus, Entity {
...
// Shared map for all UserAttributes
ConcurrentHashMap<Object, Object> userAttributes();
@SuppressWarnings("unchecked")
private static <K, A extends UserAttribute<K>> ConcurrentHashMap<K, A> castMap(ConcurrentHashMap<Object, Object> map) {
return (ConcurrentHashMap<K, A>) (ConcurrentHashMap<?, ?>) map;
}
private static void updateUserReference(UserAttribute<?> attribute, User user) {
if (attribute instanceof ElectronicAddress electronicAddress) {
ElectronicAddressUpdater.New(electronicAddress).user(user).update();
} else if (attribute instanceof PassKey passKey) {
PassKeyUpdater.New(passKey).user(user).update();
}
// Add more conditions here for other types of UserAttributes if needed
else throw new AssertionError(STR."Unhandled UserAttribute type: \{attribute.getClass()}");
}
default <K, A extends UserAttribute<K>> ConcurrentHashMap<K, A> typeSafeUserAttributes() {
return castMap(userAttributes());
}
// Generic put method
@SuppressWarnings("unchecked")
default <K, A extends UserAttribute<K>> A putAttribute(A userAttribute) {
A oldAttribute = (A) userAttributes().put(userAttribute.id(), userAttribute);
if (oldAttribute == null) {
updateUserReference(userAttribute, this);
}
return oldAttribute;
}
} and here is a sample for UserStorage, ElectronicAddress and PassKey: public interface UserAttribute<I> extends WithId<I>, WithStatus, Entity {
User user();
}
public interface PassKey extends UserAttribute<PassKey.ID> {}
public interface ElectronicAddress extends UserAttribute<String>{}
public class UserStorage {
private final Map<Tsid, User> usersById = new ConcurrentHashMap<>();
private final Map<Object, UserAttribute<?>> userAttributesById = new ConcurrentHashMap<>();
private transient EmbeddedStorageManager persister;
public boolean addNewUser(User newUser) {
if (usersById.putIfAbsent(newUser.id(), newUser) == null) {
addUserAttributes(newUser);
storeAll(newUser, usersById, userAttributesById);
return true;
}
return false;
}
private void addUserAttributes(User user) {
user.typeSafeUserAttributes().values().forEach(attr -> userAttributesById.put(attr.id(), attr));
}
} |
Spent some time debugging the issue today, and it seems that exception happens because I use Passkey.ID record as part of my PassKey entity interface definition. Why is this not allowed??? public interface WithId<I> {
I id();
}
...
public interface UserAttribute<I> extends WithId<I>, WithStatus, Entity {
User user();
}
...
public interface PassKey extends UserAttribute<PassKey.ID> {
Object credentialRecord();
AtomicLong signInCount();
record ID(byte[] idBytes) implements Comparable<ID>, Entity {
public ID {
Objects.requireNonNull(idBytes, "Passkey ID bytes required");
}
@Override
public int hashCode() {
return Arrays.hashCode(idBytes);
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (ID) obj;
return Arrays.equals(this.idBytes, that.idBytes);
}
@Override
public int compareTo(ID o) {
return Arrays.compare(this.idBytes, o.idBytes);
}
@Override
public String toString() {
return formatHex(idBytes);
}
}
} |
Ok .. very strange, but the issue seem to be that I accidentally had this: record ID(byte[] idBytes) implements Comparable<ID>, Entity and I only need this - and it works now! record ID(byte[] idBytes) implements Comparable<ID> well at least may I suggest a better worded exception ,pointing out clearly what the issue is? |
Hello,
I am getting this exception when trying to persist a layered entity. Looking at the "layered-entities" example, it is unclear how entities are to be stored - please show some code
The text was updated successfully, but these errors were encountered: