Complete saving?

This commit is contained in:
Braydon 2023-12-13 20:57:25 -05:00
parent 5e7f42b76e
commit 94173eb473
7 changed files with 113 additions and 24 deletions

@ -0,0 +1,11 @@
package me.braydon.feather.annotation;
import java.lang.annotation.*;
/**
* @author Braydon
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented @Inherited
public @interface Serializable { }

@ -0,0 +1,25 @@
package me.braydon.feather.common;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Represents an object that
* holds a pair of two values.
*
* @author Braydon
*/
@NoArgsConstructor @AllArgsConstructor @Setter @Getter
public class Tuple<L, R> {
/**
* The left value of this tuple.
*/
private L left;
/**
* The right value of this tuple.
*/
private R right;
}

@ -8,11 +8,10 @@ import me.braydon.feather.IDatabase;
import me.braydon.feather.annotation.Collection; import me.braydon.feather.annotation.Collection;
import me.braydon.feather.annotation.Field; import me.braydon.feather.annotation.Field;
import me.braydon.feather.annotation.Id; import me.braydon.feather.annotation.Id;
import me.braydon.feather.annotation.Serializable;
import me.braydon.feather.common.Tuple;
import java.util.Collections; import java.util.*;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
/** /**
* A document is a key-value pair that is stored * A document is a key-value pair that is stored
@ -38,12 +37,18 @@ public class Document<V> {
/** /**
* The mapped data of this document. * The mapped data of this document.
* <p>
* The key of this key-value pair is the identifier
* for the field within this document. The value is
* a tuple that contains the Java field, as well as
* the field value.
* </p>
* *
* @see V for value type * @see V for value type
*/ */
private final Map<String, V> mappedData = Collections.synchronizedMap(new LinkedHashMap<>()); private final Map<String, Tuple<java.lang.reflect.Field, V>> mappedData = Collections.synchronizedMap(new LinkedHashMap<>());
public Document(@NonNull Object element, boolean rawObject) { public Document(@NonNull Object element) {
Class<?> clazz = element.getClass(); // Get the element class Class<?> clazz = element.getClass(); // Get the element class
String idKey = null; // The key for the id field String idKey = null; // The key for the id field
for (java.lang.reflect.Field field : clazz.getDeclaredFields()) { for (java.lang.reflect.Field field : clazz.getDeclaredFields()) {
@ -64,20 +69,15 @@ public class Document<V> {
} }
Class<?> fieldType = field.getType(); // The type of the field Class<?> fieldType = field.getType(); // The type of the field
try { try {
Object value; // The value of the field Object value = field.get(element); // The value of the field
if (fieldType == UUID.class) { // Convert UUIDs into strings if (field.isAnnotationPresent(Serializable.class)) { // Serialize the field if @Serializable is present
value = ((UUID) field.get(element)).toString(); value = FeatherSettings.getGson().toJson(field.get(element));
} else if (rawObject) { // Use the raw object from the field } else if (fieldType == UUID.class) { // Convert UUIDs into strings
value = field.get(element); value = ((UUID) value).toString();
} else { // Otherwise, turn the value into a string
if (fieldType == String.class) { // Already a string, cast it
value = field.get(element);
} else { // Convert the field into json using Gson
value = FeatherSettings.getGson().toJson(field.get(element));
}
} }
mappedData.put(key, (V) value); // Store in our map
mappedData.put(key, new Tuple<>(field, (V) value)); // Store in our map
} catch (IllegalAccessException ex) { } catch (IllegalAccessException ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
@ -85,10 +85,25 @@ public class Document<V> {
assert idKey != null; // We need an id key assert idKey != null; // We need an id key
this.idKey = idKey; // We have our id key this.idKey = idKey; // We have our id key
V key = mappedData.get(idKey); // Get the id from the data map Tuple<java.lang.reflect.Field, V> key = mappedData.get(idKey); // Get the id from the data map
if (key == null) { // The element is missing an id field if (key == null) { // The element is missing an id field
throw new IllegalArgumentException("No @Id annotated field found in " + clazz.getSimpleName()); throw new IllegalArgumentException("No @Id annotated field found in " + clazz.getSimpleName());
} }
this.key = key; this.key = key.getRight();
}
/**
* Turn this document into a map.
*
* @return the mapped data
* @see #mappedData for stored data
*/
@NonNull
public Map<String, V> toMappedData() {
Map<String, V> mappedData = new LinkedHashMap<>(); // The mapped data
for (Map.Entry<String, Tuple<java.lang.reflect.Field, V>> entry : this.mappedData.entrySet()) {
mappedData.put(entry.getKey(), entry.getValue().getRight());
}
return mappedData;
} }
} }

@ -6,13 +6,18 @@ import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase; import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters; import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Indexes;
import com.mongodb.client.model.UpdateOptions; import com.mongodb.client.model.UpdateOptions;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import me.braydon.feather.IDatabase; import me.braydon.feather.IDatabase;
import me.braydon.feather.annotation.Collection; import me.braydon.feather.annotation.Collection;
import me.braydon.feather.annotation.Field; import me.braydon.feather.annotation.Field;
import me.braydon.feather.common.Tuple;
import me.braydon.feather.data.Document; import me.braydon.feather.data.Document;
import me.braydon.feather.databases.mongodb.annotation.Index;
import java.util.Map;
/** /**
* The {@link IDatabase} implementation for MongoDB. * The {@link IDatabase} implementation for MongoDB.
@ -137,14 +142,22 @@ public class MongoDB implements IDatabase<MongoClient, ConnectionString, MongoSy
throw new IllegalStateException("Missing collection name in @Collection for " + clazz.getSimpleName()); throw new IllegalStateException("Missing collection name in @Collection for " + clazz.getSimpleName());
} }
MongoCollection<org.bson.Document> collection = database.getCollection(collectionName); // Get the collection MongoCollection<org.bson.Document> collection = database.getCollection(collectionName); // Get the collection
Document<Object> document = new Document<>(element, true); // Construct the document from the element Document<Object> document = new Document<>(element); // Construct the document from the element
// Set the map in the database // Set the map in the database
collection.updateOne( collection.updateOne(
Filters.eq(document.getIdKey(), document.getKey()), Filters.eq(document.getIdKey(), document.getKey()),
new org.bson.Document("$set", new org.bson.Document(document.getMappedData())), new org.bson.Document("$set", new org.bson.Document(document.toMappedData())),
new UpdateOptions().upsert(true) new UpdateOptions().upsert(true)
); );
// Create indexes for @Index fields
for (Map.Entry<String, Tuple<java.lang.reflect.Field, Object>> entry : document.getMappedData().entrySet()) {
java.lang.reflect.Field field = entry.getValue().getLeft();
if (field.isAnnotationPresent(Index.class)) {
collection.createIndex(Indexes.text(entry.getKey()));
}
}
} }
/** /**

@ -0,0 +1,16 @@
package me.braydon.feather.databases.mongodb.annotation;
import me.braydon.feather.databases.mongodb.MongoDB;
import java.lang.annotation.*;
/**
* Fields flagged with this annotation will be
* treated as an indexed field within {@link MongoDB}.
*
* @author Braydon
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented @Inherited
public @interface Index { }

@ -126,8 +126,8 @@ public class Redis implements IDatabase<StatefulRedisConnection<String, String>,
if (!element.getClass().isAnnotationPresent(Collection.class)) { // Missing annotation if (!element.getClass().isAnnotationPresent(Collection.class)) { // Missing annotation
throw new IllegalStateException("Element is missing @Collection annotation"); throw new IllegalStateException("Element is missing @Collection annotation");
} }
Document<String> document = new Document<>(element, false); // Construct the document from the element Document<String> document = new Document<>(element); // Construct the document from the element
sync().hmset(String.valueOf(document.getKey()), document.getMappedData()); // Set the map in the database sync().hmset(String.valueOf(document.getKey()), document.toMappedData()); // Set the map in the database
} }
/** /**

@ -20,4 +20,13 @@ public abstract class Repository<D extends IDatabase<?, ?, ?, ?>> {
* @see D for database * @see D for database
*/ */
@NonNull private final D database; @NonNull private final D database;
/**
* Save the given element to the database.
*
* @param element the element to save
*/
public final void save(@NonNull Object element) {
database.write(element);
}
} }