Compare commits

...

5 Commits

12 changed files with 1204 additions and 47 deletions

View File

@ -0,0 +1,27 @@
package org.atriasoft.archidata.annotation;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* In NoSql entity the relation is stored in the 2 part of the entity,
* then it is needed to define the field that store the relation data value in the remote elements.
*/
@Retention(RUNTIME)
@Target({ FIELD, METHOD })
public @interface ManyToManyLocal {
/**
* The entity class that is the target of the
* association.
*/
Class<?> targetEntity();
/**
* The field that owns the revert value. empty if the relationship is unidirectional.
*/
String remoteField() default "";
}

View File

@ -29,9 +29,11 @@ import org.atriasoft.archidata.annotation.CreationTimestamp;
import org.atriasoft.archidata.annotation.UpdateTimestamp; import org.atriasoft.archidata.annotation.UpdateTimestamp;
import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnDataJson; import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnDataJson;
import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnManyToMany; import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnManyToMany;
import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnManyToManyLocal;
import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnManyToOne; import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnManyToOne;
import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnOneToMany; import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnOneToMany;
import org.atriasoft.archidata.dataAccess.addOnSQL.DataAccessAddOn; import org.atriasoft.archidata.dataAccess.addOnSQL.DataAccessAddOn;
import org.atriasoft.archidata.dataAccess.options.AccessDeletedItems;
import org.atriasoft.archidata.dataAccess.options.CheckFunction; import org.atriasoft.archidata.dataAccess.options.CheckFunction;
import org.atriasoft.archidata.dataAccess.options.Condition; import org.atriasoft.archidata.dataAccess.options.Condition;
import org.atriasoft.archidata.dataAccess.options.DBInterfaceRoot; import org.atriasoft.archidata.dataAccess.options.DBInterfaceRoot;
@ -41,6 +43,7 @@ import org.atriasoft.archidata.dataAccess.options.Limit;
import org.atriasoft.archidata.dataAccess.options.OptionSpecifyType; import org.atriasoft.archidata.dataAccess.options.OptionSpecifyType;
import org.atriasoft.archidata.dataAccess.options.OrderBy; import org.atriasoft.archidata.dataAccess.options.OrderBy;
import org.atriasoft.archidata.dataAccess.options.QueryOption; import org.atriasoft.archidata.dataAccess.options.QueryOption;
import org.atriasoft.archidata.dataAccess.options.ReadAllColumn;
import org.atriasoft.archidata.dataAccess.options.TransmitKey; import org.atriasoft.archidata.dataAccess.options.TransmitKey;
import org.atriasoft.archidata.db.DbIoSql; import org.atriasoft.archidata.db.DbIoSql;
import org.atriasoft.archidata.exception.DataAccessException; import org.atriasoft.archidata.exception.DataAccessException;
@ -64,8 +67,12 @@ import jakarta.ws.rs.InternalServerErrorException;
public class DBAccessSQL extends DBAccess { public class DBAccessSQL extends DBAccess {
final static Logger LOGGER = LoggerFactory.getLogger(DBAccessSQL.class); final static Logger LOGGER = LoggerFactory.getLogger(DBAccessSQL.class);
// by default we manage some add-on that permit to manage non-native model (like json serialization, List of external key as String list...) // by default we manage some add-on that permit to manage non-native model (like json serialization, List of external key as String list...)
final static List<DataAccessAddOn> addOn = List.of(new AddOnManyToMany(), new AddOnManyToOne(), final static List<DataAccessAddOn> addOn = List.of(//
new AddOnOneToMany(), new AddOnDataJson()); new AddOnManyToMany(), //
new AddOnManyToOne(), //
new AddOnOneToMany(), //
new AddOnDataJson(), //
new AddOnManyToManyLocal());
private final DbIoSql db; private final DbIoSql db;
@ -941,11 +948,13 @@ public class DBAccessSQL extends DBAccess {
continue; continue;
} }
final DataAccessAddOn addOn = findAddOnforField(field); final DataAccessAddOn addOn = findAddOnforField(field);
if (addOn != null && !addOn.canInsert(field)) { if (addOn != null) {
if (addOn.isInsertAsync(field)) { if (addOn.isInsertAsync(field)) {
asyncFieldUpdate.add(field); asyncFieldUpdate.add(field);
} }
continue; if (!addOn.canInsert(field)) {
continue;
}
} }
final boolean createTime = field.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0; final boolean createTime = field.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0;
if (createTime) { if (createTime) {
@ -1157,6 +1166,31 @@ public class DBAccessSQL extends DBAccess {
query.append("UPDATE `"); query.append("UPDATE `");
query.append(tableName); query.append(tableName);
query.append("` SET "); query.append("` SET ");
//Some mode need to get the previous data to perform a correct update...
boolean needPreviousValues = false;
for (final Field field : clazz.getFields()) {
// field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
continue;
}
final FieldName name = AnnotationTools.getFieldName(field, options);
if (!filter.getValues().contains(name.inStruct())) {
continue;
} else if (AnnotationTools.isGenericField(field)) {
continue;
}
final DataAccessAddOn addOn = findAddOnforField(field);
if (addOn != null && addOn.isPreviousDataNeeded(field)) {
needPreviousValues = true;
break;
}
}
Object previousData = null;
if (needPreviousValues) {
final List<TransmitKey> transmitKey = options.get(TransmitKey.class);
previousData = this.get(data.getClass(), transmitKey.get(0).getKey(), new AccessDeletedItems(),
new ReadAllColumn());
}
boolean firstField = true; boolean firstField = true;
for (final Field field : clazz.getFields()) { for (final Field field : clazz.getFields()) {
@ -1171,17 +1205,19 @@ public class DBAccessSQL extends DBAccess {
continue; continue;
} }
final DataAccessAddOn addOn = findAddOnforField(field); final DataAccessAddOn addOn = findAddOnforField(field);
if (addOn != null && !addOn.canInsert(field)) { if (addOn != null) {
if (addOn.isUpdateAsync(field)) { if (addOn.isUpdateAsync(field)) {
final List<TransmitKey> transmitKey = options.get(TransmitKey.class); final List<TransmitKey> transmitKey = options.get(TransmitKey.class);
if (transmitKey.size() != 1) { if (transmitKey.size() != 1) {
throw new DataAccessException( throw new DataAccessException(
"Fail to transmit Key to update the async update... (must have only 1)"); "Fail to transmit Key to update the async update... (must have only 1)");
} }
addOn.asyncUpdate(this, tableName, transmitKey.get(0).getKey(), field, field.get(data), addOn.asyncUpdate(this, previousData, tableName, transmitKey.get(0).getKey(), field,
asyncActions, options); field.get(data), asyncActions, options);
}
if (!addOn.canInsert(field)) {
continue;
} }
continue;
} }
if (!field.getClass().isPrimitive()) { if (!field.getClass().isPrimitive()) {
final Object tmp = field.get(data); final Object tmp = field.get(data);
@ -1206,7 +1242,6 @@ public class DBAccessSQL extends DBAccess {
query.append(" "); query.append(" ");
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz); final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
condition.whereAppendQuery(query, tableName, null, deletedFieldName); condition.whereAppendQuery(query, tableName, null, deletedFieldName);
// If the first field is not set, then nothing to update n the main base: // If the first field is not set, then nothing to update n the main base:
if (!firstField) { if (!firstField) {
LOGGER.debug("generate update query: '{}'", query.toString()); LOGGER.debug("generate update query: '{}'", query.toString());
@ -1423,7 +1458,7 @@ public class DBAccessSQL extends DBAccess {
final T out = (T) data; final T out = (T) data;
outs.add(out); outs.add(out);
} }
LOGGER.info("Async calls: {}", lazyCall.size()); LOGGER.trace("Async calls: {}", lazyCall.size());
for (final LazyGetter elem : lazyCall) { for (final LazyGetter elem : lazyCall) {
elem.doRequest(); elem.doRequest();
} }

View File

@ -219,62 +219,63 @@ public class AddOnDataJson implements DataAccessAddOn {
public static void addLink( public static void addLink(
final DBAccess ioDb, final DBAccess ioDb,
final Class<?> clazz, final Class<?> clazz,
final String clazzPrimaryKeyName, String clazzPrimaryKeyName,
final Object clazzPrimaryKeyValue, final Object clazzPrimaryKeyValue,
final String fieldNameToUpdate, final String fieldNameToUpdate,
final Object valueToAdd) throws Exception { final Object valueToAdd) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz); final String tableName = AnnotationTools.getTableName(clazz);
final QueryOptions options = new QueryOptions(new OverrideTableName(tableName), final QueryOptions options = new QueryOptions(new OverrideTableName(tableName),
new OptionSpecifyType("id", clazzPrimaryKeyValue.getClass()), new OptionSpecifyType("idOfTheObject", clazzPrimaryKeyValue.getClass()),
new OptionSpecifyType("covers", valueToAdd.getClass(), true)); new OptionSpecifyType("filedNameOfTheObject", valueToAdd.getClass(), true));
if (clazzPrimaryKeyName != null && !clazzPrimaryKeyName.equals("id")) { if (clazzPrimaryKeyName == null) {
options.add(new OptionRenameColumn("id", clazzPrimaryKeyName)); clazzPrimaryKeyName = "id";
}
if (fieldNameToUpdate != null && !fieldNameToUpdate.equals("covers")) {
options.add(new OptionRenameColumn("covers", fieldNameToUpdate));
} }
options.add(new OptionRenameColumn("idOfTheObject", clazzPrimaryKeyName));
options.add(new OptionRenameColumn("filedNameOfTheObject", fieldNameToUpdate));
final TableCoversGeneric data = ioDb.get(TableCoversGeneric.class, clazzPrimaryKeyValue, options.getAllArray()); final TableCoversGeneric data = ioDb.get(TableCoversGeneric.class, clazzPrimaryKeyValue, options.getAllArray());
if (data.covers == null) { if (data.filedNameOfTheObject == null) {
data.covers = new ArrayList<>(); data.filedNameOfTheObject = new ArrayList<>();
} }
for (final Object elem : data.covers) { for (final Object elem : data.filedNameOfTheObject) {
if (elem.equals(valueToAdd)) { if (elem.equals(valueToAdd)) {
return; return;
} }
} }
data.covers.add(valueToAdd); data.filedNameOfTheObject.add(valueToAdd);
ioDb.update(data, data.id, List.of("covers"), options.getAllArray()); ioDb.update(data, data.idOfTheObject, List.of("filedNameOfTheObject"), options.getAllArray());
} }
public static void removeLink( public static void removeLink(
final DBAccess ioDb, final DBAccess ioDb,
final Class<?> clazz, final Class<?> clazz,
final String clazzPrimaryKeyName, String clazzPrimaryKeyName,
final Object clazzPrimaryKeyValue, final Object clazzPrimaryKeyValue,
final String fieldNameToUpdate, final String fieldNameToUpdate,
final Object valueToRemove) throws Exception { final Object valueToRemove) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz); final String tableName = AnnotationTools.getTableName(clazz);
final QueryOptions options = new QueryOptions(new OverrideTableName(tableName), final QueryOptions options = new QueryOptions(new OverrideTableName(tableName),
new OptionSpecifyType("id", clazzPrimaryKeyValue.getClass()), new OptionSpecifyType("idOfTheObject", clazzPrimaryKeyValue.getClass()),
new OptionSpecifyType("covers", valueToRemove.getClass(), true)); new OptionSpecifyType("filedNameOfTheObject", valueToRemove.getClass(), true));
if (clazzPrimaryKeyName != null && !clazzPrimaryKeyName.equals("id")) { if (clazzPrimaryKeyName == null) {
options.add(new OptionRenameColumn("id", clazzPrimaryKeyName)); clazzPrimaryKeyName = "id";
}
if (fieldNameToUpdate != null && !fieldNameToUpdate.equals("covers")) {
options.add(new OptionRenameColumn("covers", fieldNameToUpdate));
} }
options.add(new OptionRenameColumn("idOfTheObject", clazzPrimaryKeyName));
options.add(new OptionRenameColumn("filedNameOfTheObject", fieldNameToUpdate));
final TableCoversGeneric data = ioDb.get(TableCoversGeneric.class, clazzPrimaryKeyValue, options.getAllArray()); final TableCoversGeneric data = ioDb.get(TableCoversGeneric.class, clazzPrimaryKeyValue, options.getAllArray());
if (data.covers == null) { if (data.filedNameOfTheObject == null) {
return; return;
} }
final List<Object> newList = new ArrayList<>(); final List<Object> newList = new ArrayList<>();
for (final Object elem : data.covers) { for (final Object elem : data.filedNameOfTheObject) {
if (elem.equals(valueToRemove)) { if (elem.equals(valueToRemove)) {
continue; continue;
} }
newList.add(elem); newList.add(elem);
} }
data.covers = newList; data.filedNameOfTheObject = newList;
ioDb.update(data, data.id, List.of("covers"), options.getAllArray()); if (data.filedNameOfTheObject.isEmpty()) {
data.filedNameOfTheObject = null;
}
ioDb.update(data, data.idOfTheObject, List.of("filedNameOfTheObject"), options.getAllArray());
} }
} }

View File

@ -304,8 +304,7 @@ public class AddOnManyToMany implements DataAccessAddOn {
final QueryOptions options, final QueryOptions options,
final List<LazyGetter> lazyCall) throws Exception { final List<LazyGetter> lazyCall) throws Exception {
if (field.getType() != List.class) { if (field.getType() != List.class) {
LOGGER.error("Can not ManyToMany with other than List Model: {}", field.getType().getCanonicalName()); throw new SystemException("@ManyToMany must contain a List");
return;
} }
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()) final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0]; .getActualTypeArguments()[0];
@ -314,12 +313,14 @@ public class AddOnManyToMany implements DataAccessAddOn {
field.set(data, idList); field.set(data, idList);
count.inc(); count.inc();
return; return;
} else if (objectClass == UUID.class) { }
if (objectClass == UUID.class) {
final List<UUID> idList = ioDb.getListOfRawUUIDs(rs, count.value); final List<UUID> idList = ioDb.getListOfRawUUIDs(rs, count.value);
field.set(data, idList); field.set(data, idList);
count.inc(); count.inc();
return; return;
} else if (objectClass == ObjectId.class) { }
if (objectClass == ObjectId.class) {
final List<ObjectId> idList = ioDb.getListOfRawOIDs(rs, count.value); final List<ObjectId> idList = ioDb.getListOfRawOIDs(rs, count.value);
field.set(data, idList); field.set(data, idList);
count.inc(); count.inc();
@ -408,6 +409,7 @@ public class AddOnManyToMany implements DataAccessAddOn {
@Override @Override
public void asyncUpdate( public void asyncUpdate(
final DBAccessSQL ioDb, final DBAccessSQL ioDb,
final Object previousData,
final String tableName, final String tableName,
final Object localKey, final Object localKey,
final Field field, final Field field,

View File

@ -0,0 +1,464 @@
package org.atriasoft.archidata.dataAccess.addOnSQL;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import org.atriasoft.archidata.annotation.AnnotationTools;
import org.atriasoft.archidata.annotation.AnnotationTools.FieldName;
import org.atriasoft.archidata.annotation.ManyToManyLocal;
import org.atriasoft.archidata.dataAccess.CountInOut;
import org.atriasoft.archidata.dataAccess.DBAccess;
import org.atriasoft.archidata.dataAccess.DBAccessSQL;
import org.atriasoft.archidata.dataAccess.DataFactory;
import org.atriasoft.archidata.dataAccess.LazyGetter;
import org.atriasoft.archidata.dataAccess.QueryInList;
import org.atriasoft.archidata.dataAccess.QueryOptions;
import org.atriasoft.archidata.dataAccess.addOnSQL.model.TableCoversGeneric;
import org.atriasoft.archidata.dataAccess.options.Condition;
import org.atriasoft.archidata.dataAccess.options.OptionRenameColumn;
import org.atriasoft.archidata.dataAccess.options.OptionSpecifyType;
import org.atriasoft.archidata.dataAccess.options.OverrideTableName;
import org.atriasoft.archidata.exception.SystemException;
import org.atriasoft.archidata.tools.ContextGenericTools;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.validation.constraints.NotNull;
public class AddOnManyToManyLocal implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToManyLocal.class);
static final String SEPARATOR_LONG = "-";
static final String SEPARATOR_UUID = "_";
@Override
public Class<?> getAnnotationClass() {
return ManyToManyLocal.class;
}
@Override
public boolean isCompatibleField(final Field field) {
if (field.getType() != List.class) {
return false;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass == Long.class || objectClass == UUID.class || objectClass == ObjectId.class) {
return true;
}
final ManyToManyLocal decorators = field.getDeclaredAnnotation(ManyToManyLocal.class);
if (decorators == null) {
return false;
}
if (decorators.targetEntity() == objectClass) {
return true;
}
return false;
}
@Override
public void insertData(
final DBAccessSQL ioDb,
final PreparedStatement ps,
final Field field,
final Object rootObject,
final CountInOut iii)
throws SQLException, IllegalArgumentException, IllegalAccessException, JsonProcessingException {
final Object data = field.get(rootObject);
if (data == null) {
ps.setNull(iii.value, Types.VARCHAR);
}
final ObjectMapper objectMapper = ContextGenericTools.createObjectMapper();
final String dataString = objectMapper.writeValueAsString(data);
ps.setString(iii.value, dataString);
iii.inc();
}
@Override
public boolean isUpdateAsync(final Field field) {
return true;
}
@Override
public void asyncUpdate(
final DBAccessSQL ioDb,
final Object previousData,
final String tableName,
final Object primaryKeyValue,
final Field field,
final Object insertedData,
final List<LazyGetter> actions,
final QueryOptions options) throws Exception {
final Object previousDataValue = field.get(previousData);
Collection<?> previousDataCollection = new ArrayList<>();
if (previousDataValue instanceof final Collection<?> tmpCollection) {
previousDataCollection = tmpCollection;
}
final Object insertedDataValue = insertedData;
Collection<?> insertedDataCollection = new ArrayList<>();
if (insertedDataValue instanceof final Collection<?> tmpCollection) {
insertedDataCollection = tmpCollection;
}
// add new Values
for (final Object value : insertedDataCollection) {
if (previousDataCollection.contains(value)) {
continue;
}
actions.add(() -> {
addLinkRemote(ioDb, field, primaryKeyValue, value);
});
}
// remove old values:
for (final Object value : previousDataCollection) {
if (insertedDataCollection.contains(value)) {
continue;
}
actions.add(() -> {
removeLinkRemote(ioDb, field, primaryKeyValue, value);
});
}
}
/** Some action must be done asynchronously for update or remove element
* @param field
* @return */
@Override
public boolean isInsertAsync(final Field field) throws Exception {
return true;
}
/** When insert is mark async, this function permit to create or update the data
* @param tableName Name of the Table.
* @param localId Local ID of the current table
* @param field Field that is updated.
* @param data Data that might be inserted.
* @param actions Asynchronous action to do after main request. */
@Override
public void asyncInsert(
final DBAccessSQL ioDb,
final String tableName,
final Object primaryKeyValue,
final Field field,
final Object data,
final List<LazyGetter> actions,
final QueryOptions options) throws Exception {
final Object insertedData = data;
if (insertedData == null) {
return;
}
if (insertedData instanceof final Collection<?> insertedDataCollection) {
for (final Object value : insertedDataCollection) {
actions.add(() -> {
addLinkRemote(ioDb, field, primaryKeyValue, value);
});
}
}
}
@Override
public boolean isPreviousDataNeeded(final Field field) {
return true;
}
@Override
public boolean canInsert(final Field field) {
return isCompatibleField(field);
}
@Override
public boolean canRetrieve(final Field field) {
return isCompatibleField(field);
}
@Override
public void generateQuery(
@NotNull final String tableName,
@NotNull final String primaryKey,
@NotNull final Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull final String name,
@NotNull final CountInOut count,
final QueryOptions options) throws Exception {
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(name);
count.inc();
}
@Override
public void fillFromQuery(
final DBAccessSQL ioDb,
final ResultSet rs,
final Field field,
final Object data,
final CountInOut count,
final QueryOptions options,
final List<LazyGetter> lazyCall) throws Exception {
if (field.getType() != List.class) {
throw new SystemException("@ManyToManyLocal must contain a List");
}
final String jsonData = rs.getString(count.value);
count.inc();
if (rs.wasNull()) {
return;
}
final ObjectMapper objectMapper = ContextGenericTools.createObjectMapper();
final ParameterizedType listType = (ParameterizedType) field.getGenericType();
final Class<?> objectClass = (Class<?>) listType.getActualTypeArguments()[0];
if (objectClass == Long.class) {
final List<Long> dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Long>>() {});
field.set(data, dataParsed);
return;
}
if (objectClass == String.class) {
final List<String> dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<String>>() {});
field.set(data, dataParsed);
return;
}
if (objectClass == UUID.class)
{
final List<UUID> dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<UUID>>() {});
field.set(data, dataParsed);
return;
}
if (objectClass == ObjectId.class) {
final List<ObjectId> dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<ObjectId>>() {});
field.set(data, dataParsed);
return;
}
final ManyToManyLocal decorators = field.getDeclaredAnnotation(ManyToManyLocal.class);
if (decorators == null) {
return;
}
if (objectClass == decorators.targetEntity()) {
final Class<?> foreignKeyType = AnnotationTools.getPrimaryKeyField(objectClass).getType();
if (foreignKeyType == Long.class) {
final List<Long> idList = objectMapper.readValue(jsonData, new TypeReference<List<Long>>() {});
if (idList != null && idList.size() > 0) {
final FieldName idField = AnnotationTools.getFieldName(AnnotationTools.getIdField(objectClass),
options);
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
// TODO: update to have get with abstract types ....
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryInList<>(idField.inTable(), idList)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
} else if (foreignKeyType == UUID.class) {
final List<UUID> idList = objectMapper.readValue(jsonData, new TypeReference<List<UUID>>() {});
if (idList != null && idList.size() > 0) {
final FieldName idField = AnnotationTools.getFieldName(AnnotationTools.getIdField(objectClass),
options);
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
final List<UUID> childs = new ArrayList<>(idList);
// TODO: update to have get with abstract types ....
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryInList<>(idField.inTable(), childs)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
} else if (foreignKeyType == ObjectId.class) {
final List<ObjectId> idList = objectMapper.readValue(jsonData, new TypeReference<List<ObjectId>>() {});
if (idList != null && idList.size() > 0) {
final FieldName idField = AnnotationTools.getFieldName(AnnotationTools.getIdField(objectClass),
options);
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
final List<ObjectId> childs = new ArrayList<>(idList);
// TODO: update to have get with abstract types ....
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryInList<>(idField.inTable(), childs)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
}
}
}
@Override
public void createTables(
final String tableName,
final Field primaryField,
final Field field,
final StringBuilder mainTableBuilder,
final List<String> preActionList,
final List<String> postActionList,
final boolean createIfNotExist,
final boolean createDrop,
final int fieldId,
final QueryOptions options) throws Exception {
// store data as json to response like a no-sql
DataFactory.createTablesSpecificType(tableName, primaryField, field, mainTableBuilder, preActionList,
postActionList, createIfNotExist, createDrop, fieldId, JsonValue.class, options);
}
private static void addLinkLocal(
final DBAccess ioDb,
final Class<?> clazz,
final String clazzPrimaryKeyName,
final Object clazzPrimaryKeyValue,
final String fieldNameToUpdate,
final Object valueToAdd) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final QueryOptions options = new QueryOptions(new OverrideTableName(tableName),
new OptionSpecifyType("idOfTheObject", clazzPrimaryKeyValue.getClass()),
new OptionSpecifyType("filedNameOfTheObject", valueToAdd.getClass(), true));
options.add(new OptionRenameColumn("idOfTheObject", clazzPrimaryKeyName));
options.add(new OptionRenameColumn("filedNameOfTheObject", fieldNameToUpdate));
final TableCoversGeneric data = ioDb.get(TableCoversGeneric.class, clazzPrimaryKeyValue, options.getAllArray());
if (data.filedNameOfTheObject == null) {
data.filedNameOfTheObject = new ArrayList<>();
}
for (final Object elem : data.filedNameOfTheObject) {
if (elem.equals(valueToAdd)) {
return;
}
}
data.filedNameOfTheObject.add(valueToAdd);
ioDb.update(data, data.idOfTheObject, List.of("filedNameOfTheObject"), options.getAllArray());
}
public static void addLink(
final DBAccess ioDb,
final Class<?> clazz,
final Object clazzPrimaryKeyValue,
final String fieldNameToUpdate,
final Object valueToAdd) throws Exception {
final Field localField = AnnotationTools.getFieldNamed(clazz, fieldNameToUpdate);
{
//get local field to find the remote field name:
final Field primaryKeyField = AnnotationTools.getPrimaryKeyField(clazz);
final FieldName primaryKeyColomnName = AnnotationTools.getFieldName(primaryKeyField, null);
final FieldName localFieldName = AnnotationTools.getFieldName(localField, null);
addLinkLocal(ioDb, clazz, primaryKeyColomnName.inTable(), clazzPrimaryKeyValue, localFieldName.inTable(),
valueToAdd);
}
addLinkRemote(ioDb, localField, clazzPrimaryKeyValue, valueToAdd);
}
private static void addLinkRemote(
final DBAccess ioDb,
final Field localField,
final Object localPrimaryKeyValue,
final Object remotePrimaryKeyValue) throws Exception {
final ManyToManyLocal manyLocal = AnnotationTools.get(localField, ManyToManyLocal.class);
// Update the remote elements:
if (manyLocal == null || manyLocal.targetEntity() == null || manyLocal.remoteField() == null
|| manyLocal.remoteField().isEmpty()) {
return;
}
{
//get local field to find the remote field name:
final Field primaryKeyField = AnnotationTools.getPrimaryKeyField(manyLocal.targetEntity());
final FieldName primaryKeyColomnName = AnnotationTools.getFieldName(primaryKeyField, null);
final Field remoteField = AnnotationTools.getFieldNamed(manyLocal.targetEntity(), manyLocal.remoteField());
final FieldName localFieldName = AnnotationTools.getFieldName(remoteField, null);
addLinkLocal(ioDb, manyLocal.targetEntity(), primaryKeyColomnName.inTable(), remotePrimaryKeyValue,
localFieldName.inTable(), localPrimaryKeyValue);
}
}
private static void removeLinkLocal(
final DBAccess ioDb,
final Class<?> clazz,
final String clazzPrimaryKeyName,
final Object clazzPrimaryKeyValue,
final String fieldNameToUpdate,
final Object valueToRemove) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final QueryOptions options = new QueryOptions(new OverrideTableName(tableName),
new OptionSpecifyType("idOfTheObject", clazzPrimaryKeyValue.getClass()),
new OptionSpecifyType("filedNameOfTheObject", valueToRemove.getClass(), true));
options.add(new OptionRenameColumn("idOfTheObject", clazzPrimaryKeyName));
options.add(new OptionRenameColumn("filedNameOfTheObject", fieldNameToUpdate));
final TableCoversGeneric data = ioDb.get(TableCoversGeneric.class, clazzPrimaryKeyValue, options.getAllArray());
if (data.filedNameOfTheObject == null) {
return;
}
final List<Object> newList = new ArrayList<>();
for (final Object elem : data.filedNameOfTheObject) {
if (elem.equals(valueToRemove)) {
continue;
}
newList.add(elem);
}
data.filedNameOfTheObject = newList;
if (data.filedNameOfTheObject.isEmpty()) {
data.filedNameOfTheObject = null;
}
ioDb.update(data, data.idOfTheObject, List.of("filedNameOfTheObject"), options.getAllArray());
}
public static void removeLink(
final DBAccess ioDb,
final Class<?> clazz,
final Object clazzPrimaryKeyValue,
final String fieldNameToUpdate,
final Object valueToRemove) throws Exception {
final Field localField = AnnotationTools.getFieldNamed(clazz, fieldNameToUpdate);
{
//get local field to find the remote field name:
final Field primaryKeyField = AnnotationTools.getPrimaryKeyField(clazz);
final FieldName primaryKeyColomnName = AnnotationTools.getFieldName(primaryKeyField, null);
final FieldName localFieldName = AnnotationTools.getFieldName(localField, null);
removeLinkLocal(ioDb, clazz, primaryKeyColomnName.inTable(), clazzPrimaryKeyValue, localFieldName.inTable(),
valueToRemove);
}
removeLinkRemote(ioDb, localField, clazzPrimaryKeyValue, valueToRemove);
}
private static void removeLinkRemote(
final DBAccess ioDb,
final Field localField,
final Object localPrimaryKeyValue,
final Object remotePrimaryKeyValue) throws Exception {
final ManyToManyLocal manyLocal = AnnotationTools.get(localField, ManyToManyLocal.class);
// Update the remote elements:
if (manyLocal == null || manyLocal.targetEntity() == null || manyLocal.remoteField() == null
|| manyLocal.remoteField().isEmpty()) {
return;
}
{
//get local field to find the remote field name:
final Field primaryKeyField = AnnotationTools.getPrimaryKeyField(manyLocal.targetEntity());
final FieldName primaryKeyColomnName = AnnotationTools.getFieldName(primaryKeyField, null);
final Field remoteField = AnnotationTools.getFieldNamed(manyLocal.targetEntity(), manyLocal.remoteField());
final FieldName localFieldName = AnnotationTools.getFieldName(remoteField, null);
removeLinkLocal(ioDb, manyLocal.targetEntity(), primaryKeyColomnName.inTable(), remotePrimaryKeyValue,
localFieldName.inTable(), localPrimaryKeyValue);
}
}
}

View File

@ -137,6 +137,7 @@ public interface DataAccessAddOn {
* @param actions Asynchronous action to do after main request. */ * @param actions Asynchronous action to do after main request. */
default void asyncUpdate( default void asyncUpdate(
final DBAccessSQL ioDb, final DBAccessSQL ioDb,
final Object previousData,
final String tableName, final String tableName,
final Object localId, final Object localId,
final Field field, final Field field,
@ -146,6 +147,13 @@ public interface DataAccessAddOn {
} }
/** Some annotation need to collect data before updating the current values
* @param field
* @return */
default boolean isPreviousDataNeeded(final Field field) {
return false;
}
default void drop(final DBAccessSQL ioDb, final String tableName, final Field field, final QueryOptions options) default void drop(final DBAccessSQL ioDb, final String tableName, final Field field, final QueryOptions options)
throws Exception { throws Exception {

View File

@ -11,15 +11,15 @@ public class TableCoversGeneric {
// nothing to do... // nothing to do...
} }
public TableCoversGeneric(final Object id, final List<Object> covers) { public TableCoversGeneric(final Object idOfTheObject, final List<Object> filedNameOfTheObject) {
this.id = id; this.idOfTheObject = idOfTheObject;
this.covers = covers; this.filedNameOfTheObject = filedNameOfTheObject;
} }
@Id @Id
public Object id; public Object idOfTheObject;
@DataJson() @DataJson()
public List<Object> covers; public List<Object> filedNameOfTheObject;
} }

View File

@ -0,0 +1,563 @@
package test.atriasoft.archidata.dataAccess;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.atriasoft.archidata.dataAccess.DBAccessSQL;
import org.atriasoft.archidata.dataAccess.DataFactory;
import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnManyToManyLocal;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import test.atriasoft.archidata.ConfigureDb;
import test.atriasoft.archidata.StepwiseExtension;
import test.atriasoft.archidata.dataAccess.model.TypeManyToManyLocalOIDRemote;
import test.atriasoft.archidata.dataAccess.model.TypeManyToManyLocalOIDRoot;
import test.atriasoft.archidata.dataAccess.model.TypeManyToManyLocalOIDRootExpand;
@ExtendWith(StepwiseExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TestManyToManyLocalOID {
final static private Logger LOGGER = LoggerFactory.getLogger(TestManyToManyLocalOID.class);
@BeforeAll
public static void configureWebServer() throws Exception {
ConfigureDb.configure();
}
@AfterAll
public static void removeDataBase() throws IOException {
ConfigureDb.clear();
}
@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class SimpleTestInsertionAndRetrieve {
@BeforeAll
public void testCreateTable() throws Exception {
final List<String> sqlCommand2 = DataFactory.createTable(TypeManyToManyLocalOIDRoot.class);
final List<String> sqlCommand = DataFactory.createTable(TypeManyToManyLocalOIDRemote.class);
sqlCommand.addAll(sqlCommand2);
if (ConfigureDb.da instanceof final DBAccessSQL daSQL) {
for (final String elem : sqlCommand) {
LOGGER.debug("request: '{}'", elem);
daSQL.executeSimpleQuery(elem);
}
}
}
@AfterAll
public void dropTables() throws Exception {
if (ConfigureDb.da instanceof final DBAccessSQL daSQL) {
daSQL.drop(TypeManyToManyLocalOIDRoot.class);
daSQL.drop(TypeManyToManyLocalOIDRemote.class);
}
}
@Order(2)
@Test
public void testSimpleInsertAndRetieve() throws Exception {
final TypeManyToManyLocalOIDRoot test = new TypeManyToManyLocalOIDRoot();
test.otherData = "root insert";
final TypeManyToManyLocalOIDRoot insertedData = ConfigureDb.da.insert(test);
Assertions.assertNotNull(insertedData);
Assertions.assertNotNull(insertedData.oid);
Assertions.assertNull(insertedData.remote);
// Try to retrieve all the data:
final TypeManyToManyLocalOIDRoot retrieve = ConfigureDb.da.get(TypeManyToManyLocalOIDRoot.class,
insertedData.oid);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.oid);
Assertions.assertEquals(insertedData.oid, retrieve.oid);
Assertions.assertNotNull(retrieve.otherData);
Assertions.assertEquals(insertedData.otherData, retrieve.otherData);
Assertions.assertNull(retrieve.remote);
ConfigureDb.da.delete(TypeManyToManyLocalOIDRoot.class, insertedData.oid);
}
}
// TODO: add and remove link from remote class
@Order(3)
@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class AddLinkInsertInRoot {
TypeManyToManyLocalOIDRemote insertedRemote1;
TypeManyToManyLocalOIDRemote insertedRemote2;
TypeManyToManyLocalOIDRoot insertedData;
@BeforeAll
public void testCreateTable() throws Exception {
final List<String> sqlCommand2 = DataFactory.createTable(TypeManyToManyLocalOIDRoot.class);
final List<String> sqlCommand = DataFactory.createTable(TypeManyToManyLocalOIDRemote.class);
sqlCommand.addAll(sqlCommand2);
if (ConfigureDb.da instanceof final DBAccessSQL daSQL) {
for (final String elem : sqlCommand) {
LOGGER.debug("request: '{}'", elem);
daSQL.executeSimpleQuery(elem);
}
}
}
@AfterAll
public void dropTables() throws Exception {
if (ConfigureDb.da instanceof final DBAccessSQL daSQL) {
daSQL.drop(TypeManyToManyLocalOIDRoot.class);
daSQL.drop(TypeManyToManyLocalOIDRemote.class);
}
}
// ---------------------------------------------------------------
// -- Add remote:
// ---------------------------------------------------------------
@Order(1)
@Test
public void addRemotes() throws Exception {
TypeManyToManyLocalOIDRemote remote = new TypeManyToManyLocalOIDRemote();
for (int iii = 0; iii < 100; iii++) {
remote.data = "tmp" + iii;
this.insertedRemote1 = ConfigureDb.da.insert(remote);
ConfigureDb.da.delete(TypeManyToManyLocalOIDRemote.class, this.insertedRemote1.oid);
}
remote = new TypeManyToManyLocalOIDRemote();
remote.data = "remote1";
this.insertedRemote1 = ConfigureDb.da.insert(remote);
Assertions.assertEquals(this.insertedRemote1.data, remote.data);
remote = new TypeManyToManyLocalOIDRemote();
remote.data = "remote2";
this.insertedRemote2 = ConfigureDb.da.insert(remote);
Assertions.assertEquals(this.insertedRemote2.data, remote.data);
}
@Order(2)
@Test
public void insertDataWithoutRemote() throws Exception {
final TypeManyToManyLocalOIDRoot test = new TypeManyToManyLocalOIDRoot();
test.otherData = "root insert 55";
this.insertedData = ConfigureDb.da.insert(test);
Assertions.assertNotNull(this.insertedData);
Assertions.assertNotNull(this.insertedData.oid);
Assertions.assertNull(this.insertedData.remote);
// Try to retrieve all the data:
final TypeManyToManyLocalOIDRoot retrieve = ConfigureDb.da.get(TypeManyToManyLocalOIDRoot.class,
this.insertedData.oid);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.oid);
Assertions.assertEquals(this.insertedData.oid, retrieve.oid);
Assertions.assertNotNull(retrieve.otherData);
Assertions.assertEquals(this.insertedData.otherData, retrieve.otherData);
Assertions.assertNull(retrieve.remote);
}
@Order(3)
@Test
public void addLinksRemotes() throws Exception {
// Add remote elements
AddOnManyToManyLocal.addLink(ConfigureDb.da, //
TypeManyToManyLocalOIDRoot.class, //
this.insertedData.oid, //
"remote", this.insertedRemote1.oid);
AddOnManyToManyLocal.addLink(ConfigureDb.da, //
TypeManyToManyLocalOIDRoot.class, //
this.insertedData.oid, //
"remote", this.insertedRemote2.oid);
final TypeManyToManyLocalOIDRoot retrieve = ConfigureDb.da.get(TypeManyToManyLocalOIDRoot.class,
this.insertedData.oid);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.oid);
Assertions.assertEquals(this.insertedData.oid, retrieve.oid);
Assertions.assertNotNull(retrieve.otherData);
Assertions.assertEquals(this.insertedData.otherData, retrieve.otherData);
Assertions.assertNotNull(retrieve.remote);
Assertions.assertEquals(2, retrieve.remote.size());
Assertions.assertEquals(retrieve.remote.get(0), this.insertedRemote1.oid);
Assertions.assertEquals(retrieve.remote.get(1), this.insertedRemote2.oid);
// -- Verify remote is linked:
final TypeManyToManyLocalOIDRemote retrieveRemote = ConfigureDb.da.get(TypeManyToManyLocalOIDRemote.class,
this.insertedRemote1.oid);
Assertions.assertNotNull(retrieveRemote);
Assertions.assertNotNull(retrieveRemote.oid);
Assertions.assertEquals(this.insertedRemote1.oid, retrieveRemote.oid);
Assertions.assertNotNull(retrieveRemote.data);
Assertions.assertEquals(this.insertedRemote1.data, retrieveRemote.data);
Assertions.assertNotNull(retrieveRemote.remoteToParent);
Assertions.assertEquals(1, retrieveRemote.remoteToParent.size());
Assertions.assertEquals(this.insertedData.oid, retrieveRemote.remoteToParent.get(0));
}
@Order(3)
@Test
public void testExpand() throws Exception {
final TypeManyToManyLocalOIDRootExpand retrieveExpand = ConfigureDb.da
.get(TypeManyToManyLocalOIDRootExpand.class, this.insertedData.oid);
Assertions.assertNotNull(retrieveExpand);
Assertions.assertNotNull(retrieveExpand.oid);
Assertions.assertEquals(this.insertedData.oid, retrieveExpand.oid);
Assertions.assertNotNull(retrieveExpand.otherData);
Assertions.assertEquals(this.insertedData.otherData, retrieveExpand.otherData);
Assertions.assertNotNull(retrieveExpand.remote);
Assertions.assertEquals(2, retrieveExpand.remote.size());
Assertions.assertEquals(retrieveExpand.remote.get(0).oid, this.insertedRemote1.oid);
Assertions.assertEquals(retrieveExpand.remote.get(0).data, this.insertedRemote1.data);
Assertions.assertEquals(retrieveExpand.remote.get(1).oid, this.insertedRemote2.oid);
Assertions.assertEquals(retrieveExpand.remote.get(1).data, this.insertedRemote2.data);
}
@Order(4)
@Test
public void removeLinksRemotes() throws Exception {
// Remove an element
AddOnManyToManyLocal.removeLink(ConfigureDb.da, TypeManyToManyLocalOIDRoot.class, //
this.insertedData.oid, //
"remote", this.insertedRemote1.oid);
TypeManyToManyLocalOIDRoot retrieve = ConfigureDb.da.get(TypeManyToManyLocalOIDRoot.class,
this.insertedData.oid);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.oid);
Assertions.assertEquals(this.insertedData.oid, retrieve.oid);
Assertions.assertNotNull(retrieve.otherData);
Assertions.assertEquals(this.insertedData.otherData, retrieve.otherData);
Assertions.assertNotNull(retrieve.remote);
Assertions.assertEquals(retrieve.remote.size(), 1);
Assertions.assertEquals(retrieve.remote.get(0), this.insertedRemote2.oid);
// Remove the second element
AddOnManyToManyLocal.removeLink(ConfigureDb.da, TypeManyToManyLocalOIDRoot.class, //
retrieve.oid, //
"remote", this.insertedRemote2.oid);
retrieve = ConfigureDb.da.get(TypeManyToManyLocalOIDRoot.class, this.insertedData.oid);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.oid);
Assertions.assertEquals(this.insertedData.oid, retrieve.oid);
Assertions.assertNotNull(retrieve.otherData);
Assertions.assertEquals(this.insertedData.otherData, retrieve.otherData);
Assertions.assertNull(retrieve.remote);
ConfigureDb.da.delete(TypeManyToManyLocalOIDRoot.class, this.insertedData.oid);
}
}
@Order(4)
@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class directInsertAndRemoveInRoot {
TypeManyToManyLocalOIDRemote insertedRemote1;
TypeManyToManyLocalOIDRemote insertedRemote2;
TypeManyToManyLocalOIDRoot insertedRoot1;
TypeManyToManyLocalOIDRoot insertedRoot2;
@BeforeAll
public void testCreateTable() throws Exception {
final List<String> sqlCommand2 = DataFactory.createTable(TypeManyToManyLocalOIDRoot.class);
final List<String> sqlCommand = DataFactory.createTable(TypeManyToManyLocalOIDRemote.class);
sqlCommand.addAll(sqlCommand2);
if (ConfigureDb.da instanceof final DBAccessSQL daSQL) {
for (final String elem : sqlCommand) {
LOGGER.debug("request: '{}'", elem);
daSQL.executeSimpleQuery(elem);
}
}
}
@AfterAll
public void dropTables() throws Exception {
if (ConfigureDb.da instanceof final DBAccessSQL daSQL) {
daSQL.drop(TypeManyToManyLocalOIDRoot.class);
daSQL.drop(TypeManyToManyLocalOIDRemote.class);
}
}
// ---------------------------------------------------------------
// -- Add remote:
// ---------------------------------------------------------------
@Order(1)
@Test
public void addRemotes() throws Exception {
TypeManyToManyLocalOIDRemote remote = new TypeManyToManyLocalOIDRemote();
for (int iii = 0; iii < 100; iii++) {
remote.data = "tmp" + iii;
this.insertedRemote1 = ConfigureDb.da.insert(remote);
ConfigureDb.da.delete(TypeManyToManyLocalOIDRemote.class, this.insertedRemote1.oid);
}
remote = new TypeManyToManyLocalOIDRemote();
remote.data = "remote 1";
this.insertedRemote1 = ConfigureDb.da.insert(remote);
Assertions.assertEquals(this.insertedRemote1.data, remote.data);
remote = new TypeManyToManyLocalOIDRemote();
remote.data = "remote 2";
this.insertedRemote2 = ConfigureDb.da.insert(remote);
Assertions.assertEquals(this.insertedRemote2.data, remote.data);
TypeManyToManyLocalOIDRoot root = new TypeManyToManyLocalOIDRoot();
root.otherData = "root 1";
this.insertedRoot1 = ConfigureDb.da.insert(root);
root = new TypeManyToManyLocalOIDRoot();
root.otherData = "root 2";
this.insertedRoot2 = ConfigureDb.da.insert(root);
}
@Order(3)
@Test
public void addLinksRemotes() throws Exception {
// Add remote elements
AddOnManyToManyLocal.addLink(ConfigureDb.da, TypeManyToManyLocalOIDRemote.class, //
this.insertedRemote2.oid, //
"remoteToParent", this.insertedRoot1.oid);
AddOnManyToManyLocal.addLink(ConfigureDb.da, TypeManyToManyLocalOIDRemote.class, //
this.insertedRemote2.oid, //
"remoteToParent", this.insertedRoot2.oid);
final TypeManyToManyLocalOIDRemote retrieve = ConfigureDb.da.get(TypeManyToManyLocalOIDRemote.class,
this.insertedRemote2.oid);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.oid);
Assertions.assertEquals(this.insertedRemote2.oid, retrieve.oid);
Assertions.assertNotNull(retrieve.data);
Assertions.assertEquals(this.insertedRemote2.data, retrieve.data);
Assertions.assertNotNull(retrieve.remoteToParent);
Assertions.assertEquals(2, retrieve.remoteToParent.size());
Assertions.assertEquals(this.insertedRoot1.oid, retrieve.remoteToParent.get(0));
Assertions.assertEquals(this.insertedRoot2.oid, retrieve.remoteToParent.get(1));
final TypeManyToManyLocalOIDRoot retrieveExpand = ConfigureDb.da.get(TypeManyToManyLocalOIDRoot.class,
this.insertedRoot1.oid);
Assertions.assertNotNull(retrieveExpand);
Assertions.assertNotNull(retrieveExpand.oid);
Assertions.assertEquals(this.insertedRoot1.oid, retrieveExpand.oid);
Assertions.assertNotNull(retrieveExpand.otherData);
Assertions.assertEquals(this.insertedRoot1.otherData, retrieveExpand.otherData);
Assertions.assertNotNull(retrieveExpand.remote);
Assertions.assertEquals(1, retrieveExpand.remote.size());
Assertions.assertEquals(this.insertedRemote2.oid, retrieveExpand.remote.get(0));
// -- Verify remote is linked:
final TypeManyToManyLocalOIDRoot retrieveRemote = ConfigureDb.da.get(TypeManyToManyLocalOIDRoot.class,
this.insertedRoot2.oid);
Assertions.assertNotNull(retrieveRemote);
Assertions.assertNotNull(retrieveRemote.oid);
Assertions.assertEquals(this.insertedRoot2.oid, retrieveRemote.oid);
Assertions.assertNotNull(retrieveRemote.otherData);
Assertions.assertEquals(this.insertedRoot2.otherData, retrieveRemote.otherData);
Assertions.assertNotNull(retrieveRemote.remote);
Assertions.assertEquals(1, retrieveRemote.remote.size());
Assertions.assertEquals(this.insertedRemote2.oid, retrieveRemote.remote.get(0));
}
@Order(4)
@Test
public void removeLinksRemotes() throws Exception {
// Remove root elements
AddOnManyToManyLocal.removeLink(ConfigureDb.da, TypeManyToManyLocalOIDRemote.class, //
this.insertedRemote2.oid, //
"remoteToParent", this.insertedRoot2.oid);
final TypeManyToManyLocalOIDRemote retrieve = ConfigureDb.da.get(TypeManyToManyLocalOIDRemote.class,
this.insertedRemote2.oid);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.oid);
Assertions.assertEquals(this.insertedRemote2.oid, retrieve.oid);
Assertions.assertNotNull(retrieve.data);
Assertions.assertEquals(this.insertedRemote2.data, retrieve.data);
Assertions.assertNotNull(retrieve.remoteToParent);
Assertions.assertEquals(1, retrieve.remoteToParent.size());
Assertions.assertEquals(this.insertedRoot1.oid, retrieve.remoteToParent.get(0));
// -- Verify remote is linked:
final TypeManyToManyLocalOIDRoot retrieveExpand = ConfigureDb.da.get(TypeManyToManyLocalOIDRoot.class,
this.insertedRoot1.oid);
Assertions.assertNotNull(retrieveExpand);
Assertions.assertNotNull(retrieveExpand.oid);
Assertions.assertEquals(this.insertedRoot1.oid, retrieveExpand.oid);
Assertions.assertNotNull(retrieveExpand.otherData);
Assertions.assertEquals(this.insertedRoot1.otherData, retrieveExpand.otherData);
Assertions.assertNotNull(retrieveExpand.remote);
Assertions.assertEquals(1, retrieveExpand.remote.size());
Assertions.assertEquals(this.insertedRemote2.oid, retrieveExpand.remote.get(0));
// -- Verify remote is un-linked:
final TypeManyToManyLocalOIDRoot retrieveRemote = ConfigureDb.da.get(TypeManyToManyLocalOIDRoot.class,
this.insertedRoot2.oid);
Assertions.assertNotNull(retrieveRemote);
Assertions.assertNotNull(retrieveRemote.oid);
Assertions.assertEquals(this.insertedRoot2.oid, retrieveRemote.oid);
Assertions.assertNotNull(retrieveRemote.otherData);
Assertions.assertEquals(this.insertedRoot2.otherData, retrieveRemote.otherData);
Assertions.assertNull(retrieveRemote.remote);
}
@Order(5)
@Test
public void removeSecondLinksRemotes() throws Exception {
// Remove root elements
AddOnManyToManyLocal.removeLink(ConfigureDb.da, TypeManyToManyLocalOIDRemote.class, //
this.insertedRemote2.oid, //
"remoteToParent", this.insertedRoot1.oid);
final TypeManyToManyLocalOIDRemote retrieve = ConfigureDb.da.get(TypeManyToManyLocalOIDRemote.class,
this.insertedRemote2.oid);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.oid);
Assertions.assertEquals(this.insertedRemote2.oid, retrieve.oid);
Assertions.assertNotNull(retrieve.data);
Assertions.assertEquals(this.insertedRemote2.data, retrieve.data);
Assertions.assertNull(retrieve.remoteToParent);
// -- Verify remote is linked:
final TypeManyToManyLocalOIDRootExpand retrieveExpand = ConfigureDb.da
.get(TypeManyToManyLocalOIDRootExpand.class, this.insertedRoot1.oid);
Assertions.assertNotNull(retrieveExpand);
Assertions.assertNotNull(retrieveExpand.oid);
Assertions.assertEquals(this.insertedRoot1.oid, retrieveExpand.oid);
Assertions.assertNotNull(retrieveExpand.otherData);
Assertions.assertEquals(this.insertedRoot1.otherData, retrieveExpand.otherData);
Assertions.assertNull(retrieveExpand.remote);
// -- Verify remote is un-linked:
final TypeManyToManyLocalOIDRootExpand retrieveRemote = ConfigureDb.da
.get(TypeManyToManyLocalOIDRootExpand.class, this.insertedRoot2.oid);
Assertions.assertNotNull(retrieveRemote);
Assertions.assertNotNull(retrieveRemote.oid);
Assertions.assertEquals(this.insertedRoot2.oid, retrieveRemote.oid);
Assertions.assertNotNull(retrieveRemote.otherData);
Assertions.assertEquals(this.insertedRoot2.otherData, retrieveRemote.otherData);
Assertions.assertNull(retrieveRemote.remote);
}
TypeManyToManyLocalOIDRemote insertedParameters;
// ---------------------------------------------------------------
// -- Add parent with manyToMany in parameters:
// ---------------------------------------------------------------
@Order(6)
@Test
public void AddParentWithManyToManyInParameters() throws Exception {
final TypeManyToManyLocalOIDRemote test = new TypeManyToManyLocalOIDRemote();
test.data = "insert with remote";
test.remoteToParent = new ArrayList<>();
test.remoteToParent.add(this.insertedRoot1.oid);
test.remoteToParent.add(this.insertedRoot2.oid);
this.insertedParameters = ConfigureDb.da.insert(test);
Assertions.assertNotNull(this.insertedParameters);
Assertions.assertNotNull(this.insertedParameters.oid);
Assertions.assertNotNull(this.insertedParameters.remoteToParent);
Assertions.assertEquals(2, this.insertedParameters.remoteToParent.size());
Assertions.assertEquals(this.insertedRoot1.oid, this.insertedParameters.remoteToParent.get(0));
Assertions.assertEquals(this.insertedRoot2.oid, this.insertedParameters.remoteToParent.get(1));
// -- Verify remote is linked:
TypeManyToManyLocalOIDRoot retrieveRoot = ConfigureDb.da.get(TypeManyToManyLocalOIDRoot.class,
this.insertedRoot1.oid);
Assertions.assertNotNull(retrieveRoot);
Assertions.assertNotNull(retrieveRoot.oid);
Assertions.assertEquals(this.insertedRoot1.oid, retrieveRoot.oid);
Assertions.assertNotNull(retrieveRoot.otherData);
Assertions.assertEquals(this.insertedRoot1.otherData, retrieveRoot.otherData);
Assertions.assertNotNull(retrieveRoot.remote);
Assertions.assertEquals(1, retrieveRoot.remote.size());
Assertions.assertEquals(this.insertedParameters.oid, retrieveRoot.remote.get(0));
retrieveRoot = ConfigureDb.da.get(TypeManyToManyLocalOIDRoot.class, this.insertedRoot2.oid);
Assertions.assertNotNull(retrieveRoot);
Assertions.assertNotNull(retrieveRoot.oid);
Assertions.assertEquals(this.insertedRoot2.oid, retrieveRoot.oid);
Assertions.assertNotNull(retrieveRoot.otherData);
Assertions.assertEquals(this.insertedRoot2.otherData, retrieveRoot.otherData);
Assertions.assertNotNull(retrieveRoot.remote);
Assertions.assertEquals(1, retrieveRoot.remote.size());
Assertions.assertEquals(this.insertedParameters.oid, retrieveRoot.remote.get(0));
}
// ---------------------------------------------------------------
// -- Update Parent Data:
// ---------------------------------------------------------------
@Order(7)
@Test
public void updateRequest() throws Exception {
final TypeManyToManyLocalOIDRemote testUpdate = new TypeManyToManyLocalOIDRemote();
testUpdate.remoteToParent = new ArrayList<>();
testUpdate.remoteToParent.add(this.insertedRoot2.oid);
final long numberUpdate = ConfigureDb.da.update(testUpdate, this.insertedParameters.oid);
Assertions.assertEquals(1, numberUpdate);
final TypeManyToManyLocalOIDRemote insertedDataUpdate = ConfigureDb.da
.get(TypeManyToManyLocalOIDRemote.class, this.insertedParameters.oid);
Assertions.assertNotNull(insertedDataUpdate);
Assertions.assertNotNull(insertedDataUpdate.oid);
Assertions.assertNotNull(insertedDataUpdate.remoteToParent);
Assertions.assertEquals(1, insertedDataUpdate.remoteToParent.size());
Assertions.assertEquals(this.insertedRoot2.oid, insertedDataUpdate.remoteToParent.get(0));
// -- Verify remote is linked (removed):
TypeManyToManyLocalOIDRoot retrieveRoot = ConfigureDb.da.get(TypeManyToManyLocalOIDRoot.class,
this.insertedRoot1.oid);
Assertions.assertNotNull(retrieveRoot);
Assertions.assertNotNull(retrieveRoot.oid);
Assertions.assertEquals(this.insertedRoot1.oid, retrieveRoot.oid);
Assertions.assertNotNull(retrieveRoot.otherData);
Assertions.assertEquals(this.insertedRoot1.otherData, retrieveRoot.otherData);
Assertions.assertNull(retrieveRoot.remote);
// -- Verify remote is linked (keep):
retrieveRoot = ConfigureDb.da.get(TypeManyToManyLocalOIDRoot.class, this.insertedRoot2.oid);
Assertions.assertNotNull(retrieveRoot);
Assertions.assertNotNull(retrieveRoot.oid);
Assertions.assertEquals(this.insertedRoot2.oid, retrieveRoot.oid);
Assertions.assertNotNull(retrieveRoot.otherData);
Assertions.assertEquals(this.insertedRoot2.otherData, retrieveRoot.otherData);
Assertions.assertNotNull(retrieveRoot.remote);
Assertions.assertEquals(1, retrieveRoot.remote.size());
Assertions.assertEquals(this.insertedParameters.oid, retrieveRoot.remote.get(0));
}
}
}

View File

@ -2,6 +2,7 @@ package test.atriasoft.archidata.dataAccess;
import java.io.IOException; import java.io.IOException;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalTime; import java.time.LocalTime;
@ -257,9 +258,10 @@ public class TestTypes {
LOGGER.debug("Retreive Timestamp = {}", retrieve.timeStampData); LOGGER.debug("Retreive Timestamp = {}", retrieve.timeStampData);
Assertions.assertNotNull(retrieve.timeStampData); Assertions.assertNotNull(retrieve.timeStampData);
// Can not compare the exact timestamp due to aproximation and model of storing data : // Can not compare the exact timestamp due to aproximation and model of storing data :
// Assertions.assertEquals(insertedData.timeStampData, retrieve.timeStampData); final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Assertions.assertEquals(insertedData.timeStampData.toInstant().toEpochMilli(), final String insertedFormatted = sdf.format(insertedData.timeStampData);
retrieve.timeStampData.toInstant().toEpochMilli()); final String retrieveFormatted = sdf.format(retrieve.timeStampData);
Assertions.assertEquals(insertedFormatted, retrieveFormatted);
ConfigureDb.da.delete(TypesTable.class, insertedData.id); ConfigureDb.da.delete(TypesTable.class, insertedData.id);
} }

View File

@ -0,0 +1,17 @@
package test.atriasoft.archidata.dataAccess.model;
import java.util.List;
import org.atriasoft.archidata.annotation.ManyToManyLocal;
import org.atriasoft.archidata.model.OIDGenericData;
import org.bson.types.ObjectId;
import dev.morphia.annotations.Entity;
@Entity
public class TypeManyToManyLocalOIDRemote extends OIDGenericData {
@ManyToManyLocal(targetEntity = TypeManyToManyLocalOIDRoot.class, remoteField = "remote")
public List<ObjectId> remoteToParent;
public String data;
}

View File

@ -0,0 +1,18 @@
package test.atriasoft.archidata.dataAccess.model;
import java.util.List;
import org.atriasoft.archidata.annotation.ManyToManyLocal;
import org.atriasoft.archidata.model.OIDGenericData;
import org.bson.types.ObjectId;
import dev.morphia.annotations.Entity;
@Entity
public class TypeManyToManyLocalOIDRoot extends OIDGenericData {
public String otherData;
@ManyToManyLocal(targetEntity = TypeManyToManyLocalOIDRemote.class, remoteField = "remoteToParent")
public List<ObjectId> remote;
}

View File

@ -0,0 +1,20 @@
package test.atriasoft.archidata.dataAccess.model;
import java.util.List;
import org.atriasoft.archidata.annotation.ManyToManyLocal;
import org.atriasoft.archidata.model.OIDGenericData;
import dev.morphia.annotations.Entity;
import jakarta.persistence.Table;
@Table(name = "TypeManyToManyLocalOIDRoot")
// for Mongo
@Entity(value = "TypeManyToManyLocalOIDRoot")
public class TypeManyToManyLocalOIDRootExpand extends OIDGenericData {
public String otherData;
@ManyToManyLocal(targetEntity = TypeManyToManyLocalOIDRemote.class, remoteField = "remoteToParent")
public List<TypeManyToManyLocalOIDRemote> remote;
}