[FEAT] configure back compatibility with mongo

This commit is contained in:
Edouard DUPIN 2025-03-29 20:21:07 +01:00
parent f0cf1acf8a
commit 969bf78576
8 changed files with 175 additions and 381 deletions

View File

@ -18,6 +18,7 @@ import org.bson.Document;
import org.bson.conversions.Bson; import org.bson.conversions.Bson;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.kar.archidata.annotation.AnnotationTools; import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.annotation.CreationTimestamp; import org.kar.archidata.annotation.CreationTimestamp;
import org.kar.archidata.annotation.UpdateTimestamp; import org.kar.archidata.annotation.UpdateTimestamp;
import org.kar.archidata.dataAccess.addOnMongo.AddOnManyToOne; import org.kar.archidata.dataAccess.addOnMongo.AddOnManyToOne;
@ -27,6 +28,7 @@ import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.dataAccess.options.Condition; import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.FilterValue; import org.kar.archidata.dataAccess.options.FilterValue;
import org.kar.archidata.dataAccess.options.Limit; import org.kar.archidata.dataAccess.options.Limit;
import org.kar.archidata.dataAccess.options.OptionSpecifyType;
import org.kar.archidata.dataAccess.options.OrderBy; import org.kar.archidata.dataAccess.options.OrderBy;
import org.kar.archidata.dataAccess.options.QueryOption; import org.kar.archidata.dataAccess.options.QueryOption;
import org.kar.archidata.db.DbIoMorphia; import org.kar.archidata.db.DbIoMorphia;
@ -128,7 +130,7 @@ public class DBAccessMorphia extends DBAccess {
return groups; return groups;
} }
protected <T> void setValuedb( protected <T> void setValueToDb(
final Class<?> type, final Class<?> type,
final T data, final T data,
final Field field, final Field field,
@ -136,7 +138,9 @@ public class DBAccessMorphia extends DBAccess {
final Document docSet, final Document docSet,
final Document docUnSet) throws Exception { final Document docUnSet) throws Exception {
if (field.get(data) == null) { if (field.get(data) == null) {
docUnSet.append(fieldName, ""); if (docUnSet != null) {
docUnSet.append(fieldName, "");
}
return; return;
} }
if (type == long.class) { if (type == long.class) {
@ -151,7 +155,7 @@ public class DBAccessMorphia extends DBAccess {
docSet.append(fieldName, field.getFloat(data)); docSet.append(fieldName, field.getFloat(data));
return; return;
} }
if (type == Double.class) { if (type == double.class) {
docSet.append(fieldName, field.getDouble(data)); docSet.append(fieldName, field.getDouble(data));
return; return;
} }
@ -196,56 +200,40 @@ public class DBAccessMorphia extends DBAccess {
docSet.append(fieldName, tmp); docSet.append(fieldName, tmp);
return; return;
} }
if (type == ObjectId.class) {
docSet.append(fieldName, tmp);
return;
}
if (type == UUID.class) { if (type == UUID.class) {
docSet.append(fieldName, tmp); docSet.append(fieldName, tmp);
return; return;
} }
if (type == Date.class) { if (type == Date.class) {
// TODO ...
/* /*
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
final Timestamp sqlDate = java.sql.Timestamp.from(((Date) tmp).toInstant()); final Timestamp sqlDate = java.sql.Timestamp.from(((Date) tmp).toInstant());
ps.setTimestamp(iii.value, sqlDate); ps.setTimestamp(iii.value, sqlDate);
}*/ */
} }
if (type == Instant.class) { if (type == Instant.class) {
/* /*
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
final String sqlDate = ((Instant) tmp).toString(); final String sqlDate = ((Instant) tmp).toString();
ps.setString(iii.value, sqlDate); ps.setString(iii.value, sqlDate);
}
*/ */
} }
if (type == LocalDate.class) { if (type == LocalDate.class) {
/* /*
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
final java.sql.Date sqlDate = java.sql.Date.valueOf((LocalDate) tmp); final java.sql.Date sqlDate = java.sql.Date.valueOf((LocalDate) tmp);
ps.setDate(iii.value, sqlDate); ps.setDate(iii.value, sqlDate);
}
*/ */
} }
if (type == LocalTime.class) { if (type == LocalTime.class) {
/* /*
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
final java.sql.Time sqlDate = java.sql.Time.valueOf((LocalTime) tmp); final java.sql.Time sqlDate = java.sql.Time.valueOf((LocalTime) tmp);
ps.setTime(iii.value, sqlDate); ps.setTime(iii.value, sqlDate);
}
*/ */
} }
throw new DataAccessException("Unknown Field Type"); docSet.append(fieldName, tmp);
//throw new DataAccessException("Unknown Field Type");
} }
public <T> void setValueFromDoc( public <T> void setValueFromDoc(
@ -265,6 +253,11 @@ public class DBAccessMorphia extends DBAccess {
field.set(data, value); field.set(data, value);
return; return;
} }
if (type == ObjectId.class) {
final ObjectId value = doc.get(fieldName, ObjectId.class);
field.set(data, value);
return;
}
if (type == Long.class || type == long.class) { if (type == Long.class || type == long.class) {
final Long value = doc.getLong(fieldName); final Long value = doc.getLong(fieldName);
field.set(data, value); field.set(data, value);
@ -349,8 +342,8 @@ public class DBAccessMorphia extends DBAccess {
final Object value = doc.get(fieldName, field.getType()); final Object value = doc.get(fieldName, field.getType());
field.set(data, value); field.set(data, value);
} else { } else {
final Object value = createObjectFromDocument(doc.get(fieldName, Document.class), field.getType(), null, final Object value = createObjectFromDocument(doc.get(fieldName, Document.class), field.getType(),
lazyCall); new QueryOptions(), lazyCall);
field.set(data, value); field.set(data, value);
} }
@ -459,32 +452,45 @@ public class DBAccessMorphia extends DBAccess {
Object uniqueId = null; Object uniqueId = null;
// real add in the BDD: // real add in the BDD:
ObjectId insertedId = null; ObjectId insertedId = null;
final List<OptionSpecifyType> specificTypes = options.get(OptionSpecifyType.class);
try { try {
final MongoCollection<Document> collection = this.db.getDatastore().getDatabase() final MongoCollection<Document> collection = this.db.getDatastore().getDatabase()
.getCollection(collectionName); .getCollection(collectionName);
final Document doc = new Document(); final Document docSet = new Document();
final Document docUnSet = new Document();
for (final Field field : clazz.getFields()) { for (final Field field : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it .. // static field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) { if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
continue; continue;
} }
final String tableFieldName = AnnotationTools.getFieldName(field, options).inTable(); final FieldName tableFieldName = AnnotationTools.getFieldName(field, options);
Object currentInsertValue = field.get(data); Object currentInsertValue = field.get(data);
if (AnnotationTools.isPrimaryKey(field)) { if (AnnotationTools.isPrimaryKey(field)) {
primaryKeyField = field; primaryKeyField = field;
if (primaryKeyField.getType() == UUID.class) { if (primaryKeyField.getType() == UUID.class) {
final UUID uuid = UuidUtils.nextUUID(); final UUID uuid = UuidUtils.nextUUID();
uniqueId = uuid; uniqueId = uuid;
doc.append(tableFieldName, uuid); docSet.append(tableFieldName.inTable(), uuid);
continue; continue;
} else if (primaryKeyField.getType() == Long.class || primaryKeyField.getType() == long.class) { } else if (primaryKeyField.getType() == Long.class || primaryKeyField.getType() == long.class) {
// By default the MongoDB does not manage the // By default the MongoDB does not manage the
final long id = getNextSequenceLongValue(collectionName, tableFieldName); final long id = getNextSequenceLongValue(collectionName, tableFieldName.inTable());
uniqueId = id; uniqueId = id;
doc.append(tableFieldName, id); docSet.append(tableFieldName.inTable(), id);
continue; continue;
} }
LOGGER.error("TODO: Manage the ID primary key for type: "); LOGGER.error("TODO: Manage the ID primary key for type: {}=>{}", clazz.getCanonicalName(),
primaryKeyField.getType());
continue;
}
final boolean createTime = field.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0;
if (createTime) {
docSet.append(tableFieldName.inTable(), Date.from(Instant.now()));
continue;
}
final boolean updateTime = field.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0;
if (updateTime) {
docSet.append(tableFieldName.inTable(), Date.from(Instant.now()));
continue; continue;
} }
final DataAccessAddOn addOn = findAddOnforField(field); final DataAccessAddOn addOn = findAddOnforField(field);
@ -495,19 +501,8 @@ public class DBAccessMorphia extends DBAccess {
} }
continue; continue;
} }
final boolean createTime = field.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0;
if (createTime) {
doc.append(tableFieldName, Date.from(Instant.now()));
continue;
}
final boolean updateTime = field.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0;
if (updateTime) {
doc.append(tableFieldName, Date.from(Instant.now()));
continue;
}
if (currentInsertValue == null && !field.getClass().isPrimitive()) { if (currentInsertValue == null && !field.getClass().isPrimitive()) {
final DefaultValue[] defaultValue = field.getDeclaredAnnotationsByType(DefaultValue.class); final DefaultValue[] defaultValue = field.getDeclaredAnnotationsByType(DefaultValue.class);
LOGGER.error("TODO: convert default value in the correct value for the DB...");
if (defaultValue.length == 0) { if (defaultValue.length == 0) {
continue; continue;
} else { } else {
@ -518,9 +513,24 @@ public class DBAccessMorphia extends DBAccess {
currentInsertValue = convertDefaultField(value, field); currentInsertValue = convertDefaultField(value, field);
} }
} }
doc.append(tableFieldName, currentInsertValue); // conversion table ...
//doc.append(tableFieldName, currentInsertValue);
if (addOn != null) {
addOn.insertData(this, field, data, options, docSet, docUnSet);
} else {
final Class<?> type = field.getType();
if (!type.isPrimitive()) {
if (field.get(data) == null) {
if (currentInsertValue != null) {
docSet.append(tableFieldName.inTable(), currentInsertValue);
}
continue;
}
}
setValueToDb(type, data, field, tableFieldName.inTable(), docSet, null);
}
} }
final InsertOneResult result = collection.insertOne(doc); final InsertOneResult result = collection.insertOne(docSet);
// Get the Object of inserted object: // Get the Object of inserted object:
insertedId = result.getInsertedId().asObjectId().getValue(); insertedId = result.getInsertedId().asObjectId().getValue();
LOGGER.info("Document inserted with ID: " + insertedId); LOGGER.info("Document inserted with ID: " + insertedId);
@ -587,14 +597,14 @@ public class DBAccessMorphia extends DBAccess {
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) { if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
continue; continue;
} }
final String fieldName = AnnotationTools.getFieldName(field, options).inTable(); final FieldName fieldName = AnnotationTools.getFieldName(field, options);
// update field is not conditioned by filter: // update field is not conditioned by filter:
final boolean updateTime = field.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0; final boolean updateTime = field.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0;
if (updateTime) { if (updateTime) {
docSet.append(fieldName, Date.from(Instant.now())); docSet.append(fieldName.inTable(), Date.from(Instant.now()));
continue; continue;
} }
if (!filterKey.getValues().contains(fieldName)) { if (!filterKey.getValues().contains(fieldName.inStruct())) {
continue; continue;
} else if (AnnotationTools.isGenericField(field)) { } else if (AnnotationTools.isGenericField(field)) {
continue; continue;
@ -624,12 +634,10 @@ public class DBAccessMorphia extends DBAccess {
continue; continue;
} }
} }
setValuedb(type, data, field, fieldName, docSet, docUnSet); setValueToDb(type, data, field, fieldName.inTable(), docSet, docUnSet);
} }
} }
// Do the query ... // Do the query ...
final MongoCollection<Document> collection = this.db.getDatastore().getDatabase() final MongoCollection<Document> collection = this.db.getDatastore().getDatabase()
.getCollection(collectionName); .getCollection(collectionName);
final Document actions = new Document(); final Document actions = new Document();
@ -652,7 +660,6 @@ public class DBAccessMorphia extends DBAccess {
} }
public List<String> generateSelectField(final Class<?> clazz, final QueryOptions options) throws Exception { public List<String> generateSelectField(final Class<?> clazz, final QueryOptions options) throws Exception {
// TODO: list of user select fields.
final boolean readAllfields = QueryOptions.readAllColomn(options); final boolean readAllfields = QueryOptions.readAllColomn(options);
final List<String> fieldsName = new ArrayList<>(); final List<String> fieldsName = new ArrayList<>();
@ -776,6 +783,8 @@ public class DBAccessMorphia extends DBAccess {
final Class<?> clazz, final Class<?> clazz,
final QueryOptions options, final QueryOptions options,
final List<LazyGetter> lazyCall) throws Exception { final List<LazyGetter> lazyCall) throws Exception {
final List<OptionSpecifyType> specificTypes = options.get(OptionSpecifyType.class);
LOGGER.info("createObjectFromDocument: {}", clazz.getCanonicalName());
final boolean readAllfields = QueryOptions.readAllColomn(options); final boolean readAllfields = QueryOptions.readAllColomn(options);
// TODO: manage class that is defined inside a class ==> Not manage for now... // TODO: manage class that is defined inside a class ==> Not manage for now...
Object data = null; Object data = null;
@ -790,23 +799,37 @@ public class DBAccessMorphia extends DBAccess {
"Can not find the default constructor for the class: " + clazz.getCanonicalName()); "Can not find the default constructor for the class: " + clazz.getCanonicalName());
} }
for (final Field elem : clazz.getFields()) { for (final Field elem : clazz.getFields()) {
LOGGER.info(" Inspect field: name='{}' type='{}'", elem.getName(), elem.getType().getCanonicalName());
// static field is only for internal global declaration ==> remove it .. // static field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(elem.getModifiers())) { if (java.lang.reflect.Modifier.isStatic(elem.getModifiers())) {
LOGGER.info(" ==> static");
continue; continue;
} }
final DataAccessAddOn addOn = findAddOnforField(elem); final DataAccessAddOn addOn = findAddOnforField(elem);
if (addOn != null && !addOn.canRetrieve(elem)) { if (addOn != null && !addOn.canRetrieve(elem)) {
LOGGER.info(" ==> Can not retreive this field");
continue; continue;
} }
final boolean notRead = AnnotationTools.isDefaultNotRead(elem); final boolean notRead = AnnotationTools.isDefaultNotRead(elem);
if (!readAllfields && notRead) { if (!readAllfields && notRead) {
LOGGER.info(" ==> Not read this element");
continue; continue;
} }
if (addOn != null) { if (addOn != null) {
LOGGER.error("TODO: Add on not managed .6. ");
addOn.fillFromDoc(this, doc, elem, data, options, lazyCall); addOn.fillFromDoc(this, doc, elem, data, options, lazyCall);
} else { } else {
setValueFromDoc(elem.getType(), data, elem, doc, lazyCall, options); Class<?> type = elem.getType();
if (type == Object.class) {
for (final OptionSpecifyType specify : specificTypes) {
if (specify.name.equals(elem.getName())) {
type = specify.clazz;
LOGGER.info("Detect overwrite of typing var={} ... '{}' => '{}'", elem.getName(),
elem.getType().getCanonicalName(), specify.clazz.getCanonicalName());
break;
}
}
}
setValueFromDoc(type, data, elem, doc, lazyCall, options);
} }
} }
return data; return data;

View File

@ -6,7 +6,6 @@ import java.util.List;
import org.bson.Document; import org.bson.Document;
import org.kar.archidata.annotation.AnnotationTools; import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.DataJson; import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DBAccessMorphia; import org.kar.archidata.dataAccess.DBAccessMorphia;
import org.kar.archidata.dataAccess.DataFactory; import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter; import org.kar.archidata.dataAccess.LazyGetter;
@ -17,8 +16,6 @@ import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.annotation.JsonValue;
import jakarta.validation.constraints.NotNull;
public class AddOnDataJson implements DataAccessAddOn { public class AddOnDataJson implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnDataJson.class); static final Logger LOGGER = LoggerFactory.getLogger(AddOnDataJson.class);
@ -74,24 +71,6 @@ public class AddOnDataJson implements DataAccessAddOn {
return true; return true;
} }
@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();
return;
}
@Override @Override
public void fillFromDoc( public void fillFromDoc(
final DBAccessMorphia ioDb, final DBAccessMorphia ioDb,

View File

@ -173,34 +173,6 @@ public class AddOnManyToMany implements DataAccessAddOn {
count.inc(); count.inc();
} }
@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 {
if (field.getType() != List.class) {
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass == Long.class || objectClass == UUID.class) {
generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options);
}
final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class);
if (decorators == null) {
return;
}
if (objectClass == decorators.targetEntity()) {
generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options);
}
}
@Override @Override
public void fillFromDoc( public void fillFromDoc(
final DBAccessMorphia ioDb, final DBAccessMorphia ioDb,

View File

@ -5,9 +5,9 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.bson.Document; import org.bson.Document;
import org.bson.types.ObjectId;
import org.kar.archidata.annotation.AnnotationTools; import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.AnnotationTools.FieldName; import org.kar.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DBAccessMorphia; import org.kar.archidata.dataAccess.DBAccessMorphia;
import org.kar.archidata.dataAccess.DataFactory; import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter; import org.kar.archidata.dataAccess.LazyGetter;
@ -16,7 +16,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import jakarta.validation.constraints.NotNull;
public class AddOnManyToOne implements DataAccessAddOn { public class AddOnManyToOne implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToMany.class); static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToMany.class);
@ -52,7 +51,7 @@ public class AddOnManyToOne implements DataAccessAddOn {
final Document docUnSet) throws Exception { final Document docUnSet) throws Exception {
final FieldName fieldName = AnnotationTools.getFieldName(field, options); final FieldName fieldName = AnnotationTools.getFieldName(field, options);
final Object data = field.get(rootObject); final Object data = field.get(rootObject);
if (field.get(data) == null) { if (data == null) {
docUnSet.append(fieldName.inTable(), ""); docUnSet.append(fieldName.inTable(), "");
return; return;
} else if (field.getType() == Long.class) { } else if (field.getType() == Long.class) {
@ -70,6 +69,9 @@ public class AddOnManyToOne implements DataAccessAddOn {
} else if (field.getType() == UUID.class) { } else if (field.getType() == UUID.class) {
final UUID dataTyped = (UUID) data; final UUID dataTyped = (UUID) data;
docSet.append(fieldName.inTable(), dataTyped); docSet.append(fieldName.inTable(), dataTyped);
} else if (field.getType() == ObjectId.class) {
final ObjectId dataTyped = (ObjectId) data;
docSet.append(fieldName.inTable(), dataTyped);
} else { } else {
final Field idField = AnnotationTools.getFieldOfId(field.getType()); final Field idField = AnnotationTools.getFieldOfId(field.getType());
final Object uid = idField.get(data); final Object uid = idField.get(data);
@ -84,7 +86,8 @@ public class AddOnManyToOne implements DataAccessAddOn {
@Override @Override
public boolean canInsert(final Field field) { public boolean canInsert(final Field field) {
if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class
|| field.getType() == String.class || field.getType() == UUID.class) { || field.getType() == String.class || field.getType() == UUID.class
|| field.getType() == ObjectId.class) {
return true; return true;
} }
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
@ -103,7 +106,7 @@ public class AddOnManyToOne implements DataAccessAddOn {
public boolean canRetrieve(final Field field) { public boolean canRetrieve(final Field field) {
final Class<?> classType = field.getType(); final Class<?> classType = field.getType();
if (classType == Long.class || classType == Integer.class || classType == Short.class if (classType == Long.class || classType == Integer.class || classType == Short.class
|| classType == String.class || classType == UUID.class) { || classType == String.class || classType == UUID.class || classType == ObjectId.class) {
return true; return true;
} }
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
@ -113,36 +116,6 @@ public class AddOnManyToOne implements DataAccessAddOn {
return false; return false;
} }
@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 {
if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class
|| field.getType() == String.class || field.getType() == UUID.class) {
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(name);
count.inc();
return;
}
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
if (field.getType() == decorators.targetEntity()) {
// no eager possible for no sql
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(name);
count.inc();
}
}
@Override @Override
public void fillFromDoc( public void fillFromDoc(
final DBAccessMorphia ioDb, final DBAccessMorphia ioDb,
@ -159,7 +132,8 @@ public class AddOnManyToOne implements DataAccessAddOn {
} }
// local field to manage no remote object to retrieve. // local field to manage no remote object to retrieve.
if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class
|| field.getType() == String.class || field.getType() == UUID.class) { || field.getType() == String.class || field.getType() == UUID.class
|| field.getType() == ObjectId.class) {
ioDb.setValueFromDoc(field.getType(), data, field, doc, lazyCall, options); ioDb.setValueFromDoc(field.getType(), data, field, doc, lazyCall, options);
return; return;
} }
@ -174,9 +148,8 @@ public class AddOnManyToOne implements DataAccessAddOn {
final Class<?> remotePrimaryKeyType = remotePrimaryKeyField.getType(); final Class<?> remotePrimaryKeyType = remotePrimaryKeyField.getType();
if (remotePrimaryKeyType == Long.class) { if (remotePrimaryKeyType == Long.class) {
// here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ... // here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ...
final Long foreignKey = doc.getLong(fieldName); final Long foreignKey = doc.getLong(fieldName.inTable());
if (foreignKey != null) { if (foreignKey != null) {
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> { final LazyGetter lambda = () -> {
// TODO: update to have get with abstract types .... // TODO: update to have get with abstract types ....
final Object foreignData = ioDb.get(decorators.targetEntity(), foreignKey); final Object foreignData = ioDb.get(decorators.targetEntity(), foreignKey);
@ -189,9 +162,22 @@ public class AddOnManyToOne implements DataAccessAddOn {
} }
} else if (remotePrimaryKeyType == UUID.class) { } else if (remotePrimaryKeyType == UUID.class) {
// here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ... // here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ...
final UUID foreignKey = doc.get(fieldName, UUID.class); final UUID foreignKey = doc.get(fieldName.inTable(), UUID.class);
if (foreignKey != null) {
final LazyGetter lambda = () -> {
// TODO: update to have get with abstract types ....
final Object foreignData = ioDb.get(decorators.targetEntity(), foreignKey);
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
} else if (remotePrimaryKeyType == ObjectId.class) {
// here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ...
final ObjectId foreignKey = doc.get(fieldName.inTable(), ObjectId.class);
if (foreignKey != null) { if (foreignKey != null) {
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> { final LazyGetter lambda = () -> {
// TODO: update to have get with abstract types .... // TODO: update to have get with abstract types ....
final Object foreignData = ioDb.get(decorators.targetEntity(), foreignKey); final Object foreignData = ioDb.get(decorators.targetEntity(), foreignKey);
@ -221,7 +207,7 @@ public class AddOnManyToOne implements DataAccessAddOn {
final QueryOptions options) throws Exception { final QueryOptions options) throws Exception {
final Class<?> classType = field.getType(); final Class<?> classType = field.getType();
if (classType == Long.class || classType == Integer.class || classType == Short.class if (classType == Long.class || classType == Integer.class || classType == Short.class
|| classType == String.class || classType == UUID.class) { || classType == String.class || classType == UUID.class || classType == ObjectId.class) {
DataFactory.createTablesSpecificType(tableName, primaryField, field, mainTableBuilder, preActionList, DataFactory.createTablesSpecificType(tableName, primaryField, field, mainTableBuilder, preActionList,
postActionList, createIfNotExist, createDrop, fieldId, classType, options); postActionList, createIfNotExist, createDrop, fieldId, classType, options);
} else { } else {

View File

@ -2,62 +2,28 @@ package org.kar.archidata.dataAccess.addOnMongo;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors;
import org.bson.Document; import org.bson.Document;
import org.bson.types.ObjectId;
import org.kar.archidata.annotation.AnnotationTools; import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.AnnotationTools.FieldName; import org.kar.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DBAccessMorphia; import org.kar.archidata.dataAccess.DBAccessMorphia;
import org.kar.archidata.dataAccess.LazyGetter; import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryCondition; import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.QueryOptions; import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.options.Condition; import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany; import jakarta.persistence.OneToMany;
import jakarta.validation.constraints.NotNull;
public class AddOnOneToMany implements DataAccessAddOn { public class AddOnOneToMany implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnOneToMany.class); static final Logger LOGGER = LoggerFactory.getLogger(AddOnOneToMany.class);
static final String SEPARATOR_LONG = "-"; static final String SEPARATOR_LONG = "-";
/** Convert the list if external id in a string '-' separated
* @param ids List of value (null are removed)
* @return '-' string separated */
protected static String getStringOfIds(final List<Long> ids) {
final List<Long> tmp = new ArrayList<>(ids);
return tmp.stream().map(String::valueOf).collect(Collectors.joining("-"));
}
/** extract a list of "-" separated element from a SQL input data.
* @param rs Result Set of the BDD
* @param iii Id in the result set
* @return The list of Long value
* @throws SQLException if an error is generated in the sql request. */
protected static List<Long> getListOfIds(final ResultSet rs, final int iii) throws SQLException {
final String trackString = rs.getString(iii);
if (rs.wasNull()) {
return null;
}
final List<Long> out = new ArrayList<>();
final String[] elements = trackString.split("-");
for (final String elem : elements) {
final Long tmp = Long.parseLong(elem);
out.add(tmp);
}
return out;
}
@Override @Override
public Class<?> getAnnotationClass() { public Class<?> getAnnotationClass() {
return OneToMany.class; return OneToMany.class;
@ -98,7 +64,7 @@ public class AddOnOneToMany implements DataAccessAddOn {
} }
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()) final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0]; .getActualTypeArguments()[0];
if (objectClass == Long.class || objectClass == UUID.class) { if (objectClass == Long.class || objectClass == UUID.class || objectClass == ObjectId.class) {
return true; return true;
} }
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class); final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
@ -111,104 +77,7 @@ public class AddOnOneToMany implements DataAccessAddOn {
return false; return false;
} }
public void generateConcatQuery( // in first implementation we did not keep the data in the 2 Objects, bun we will do it after to have a faster table interactions.
@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,
final Class<?> targetEntity,
final String mappedBy) throws Exception {
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
final String remoteTableName = AnnotationTools.getTableName(targetEntity);
final FieldName remoteTablePrimaryKeyName = AnnotationTools
.getFieldName(AnnotationTools.getPrimaryKeyField(targetEntity), options);
final String tmpRemoteVariable = "tmp_" + Integer.toString(count.value);
final String remoteDeletedFieldName = AnnotationTools.getDeletedFieldName(targetEntity);
querySelect.append(" (SELECT GROUP_CONCAT(");
querySelect.append(tmpRemoteVariable);
querySelect.append(".");
querySelect.append(remoteTablePrimaryKeyName.inTable());
querySelect.append(" ");
if ("sqlite".equals(ConfigBaseVariable.getDBType())) {
querySelect.append(", ");
} else {
querySelect.append("SEPARATOR ");
}
querySelect.append("'");
if (objectClass == Long.class) {
querySelect.append(SEPARATOR_LONG);
}
querySelect.append("') FROM ");
querySelect.append(remoteTableName);
querySelect.append(" ");
querySelect.append(tmpRemoteVariable);
querySelect.append(" WHERE ");
if (remoteDeletedFieldName != null) {
querySelect.append(tmpRemoteVariable);
querySelect.append(".");
querySelect.append(remoteDeletedFieldName);
querySelect.append(" = false");
querySelect.append(" AND ");
}
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(primaryKey);
querySelect.append(" = ");
querySelect.append(tmpRemoteVariable);
querySelect.append(".");
querySelect.append(mappedBy);
querySelect.append(" ");
querySelect.append(") AS ");
querySelect.append(name);
querySelect.append(" ");
}
@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 {
if (field.getType() != List.class) {
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
if (decorators == null) {
return;
}
// TODO: manage better the eager and lazy !!
if (objectClass == Long.class || objectClass == UUID.class) {
generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options,
decorators.targetEntity(), decorators.mappedBy());
return;
}
if (objectClass == decorators.targetEntity()) {
if (decorators.fetch() == FetchType.EAGER) {
throw new DataAccessException("EAGER is not supported for list of element...");
} else {
// Force a copy of the primaryKey to permit the async retrieve of the data
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(primaryKey);
querySelect.append(" AS tmp_");
querySelect.append(Integer.toString(count.value));
}
}
}
@Override @Override
public void fillFromDoc( public void fillFromDoc(
final DBAccessMorphia ioDb, final DBAccessMorphia ioDb,
@ -222,11 +91,12 @@ public class AddOnOneToMany implements DataAccessAddOn {
return; return;
} }
final String fieldName = AnnotationTools.getFieldName(field, options).inTable(); final FieldName fieldName = AnnotationTools.getFieldName(field, options);
if (!doc.containsKey(fieldName)) { // in step 1 the fields are not stored in the local element
field.set(data, null); // if (!doc.containsKey(fieldName.inTable())) {
return; // field.set(data, null);
} // return;
// }
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()) final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0]; .getActualTypeArguments()[0];
@ -234,62 +104,46 @@ public class AddOnOneToMany implements DataAccessAddOn {
if (decorators == null) { if (decorators == null) {
return; return;
} }
if (objectClass == Long.class || objectClass == UUID.class) { if (objectClass == Long.class || objectClass == UUID.class || objectClass == ObjectId.class) {
final Object value = doc.get(fieldName, field.getType()); if (true) {
field.set(data, value); // DEVELOPMENT step 1 we search all the element in the list:
// get the curentObject primary key
final Field primaryField = AnnotationTools.getPrimaryKeyField(data.getClass());
final String primaryKeyName = AnnotationTools.getFieldNameRaw(primaryField);
final Object primaryKey = doc.get(primaryKeyName, primaryField.getType());
// get the remotes objects
final List<?> returnValue = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(decorators.mappedBy(), "=", primaryKey)));
// extract the primary key of the remote objects
final Field remotePrimaryField = AnnotationTools.getPrimaryKeyField(decorators.targetEntity());
final String remotePrimaryKeyName = AnnotationTools.getFieldNameRaw(remotePrimaryField);
final List<Object> listOfRemoteKeys = new ArrayList<>();
for (final var item : returnValue) {
listOfRemoteKeys.add(remotePrimaryField.get(item));
}
// inject in the current data field
if (listOfRemoteKeys.size() != 0) {
field.set(data, listOfRemoteKeys);
}
} else {
// DEVELOPMENT In step 2 this will work well:
final Object value = doc.get(fieldName.inTable(), field.getType());
field.set(data, value);
}
return; return;
} }
if (objectClass == decorators.targetEntity()) { if (objectClass == decorators.targetEntity()) {
// Maybe in a second step we do not like this but this way is efficient too.
Long parentIdTmp = null; // get the curentObject primary key
UUID parendUuidTmp = null; final Field primaryField = AnnotationTools.getPrimaryKeyField(data.getClass());
try { final String primaryKeyName = AnnotationTools.getFieldNameRaw(primaryField);
final Object value = doc.get(fieldName); final Object primaryKey = doc.get(primaryKeyName, primaryField.getType());
if (value instanceof final Long valueCasted) { // get the remotes objects
parentIdTmp = valueCasted; final List<?> returnValue = ioDb.getsWhere(decorators.targetEntity(),
} else if (value instanceof final UUID valueCasted) { new Condition(new QueryCondition(decorators.mappedBy(), "=", primaryKey)));
parendUuidTmp = valueCasted; // inject in the current data field
} if (returnValue.size() != 0) {
} catch (final Exception ex) { field.set(data, returnValue);
LOGGER.error("fail to find the correct type... {}", ex.getMessage());
}
final Long parentId = parentIdTmp;
final UUID parendUuid = parendUuidTmp;
final String mappingKey = decorators.mappedBy();
// We get the parent ID ... ==> need to request the list of elements
if (objectClass == Long.class) {
LOGGER.error("Need to retreive all primary key of all elements");
//field.set(data, idList);
return;
} else if (objectClass == UUID.class) {
LOGGER.error("Need to retreive all primary key of all elements");
//field.set(data, idList);
return;
}
if (objectClass == decorators.targetEntity()) {
if (parentId != null) {
final LazyGetter lambda = () -> {
@SuppressWarnings("unchecked")
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(mappingKey, "=", parentId)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
} else if (parendUuid != null) {
final LazyGetter lambda = () -> {
@SuppressWarnings("unchecked")
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(mappingKey, "=", parendUuid)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
} }
} }
} }

View File

@ -5,13 +5,10 @@ import java.sql.SQLException;
import java.util.List; import java.util.List;
import org.bson.Document; import org.bson.Document;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DBAccessMorphia; import org.kar.archidata.dataAccess.DBAccessMorphia;
import org.kar.archidata.dataAccess.LazyGetter; import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryOptions; import org.kar.archidata.dataAccess.QueryOptions;
import jakarta.validation.constraints.NotNull;
public interface DataAccessAddOn { public interface DataAccessAddOn {
/** Get the Class of the declaration annotation /** Get the Class of the declaration annotation
* @return The annotation class */ * @return The annotation class */
@ -57,16 +54,6 @@ public interface DataAccessAddOn {
return false; return false;
} }
void generateQuery(
@NotNull String tableName,
@NotNull final String primaryKey,
@NotNull Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull String name,
@NotNull CountInOut count,
QueryOptions options) throws Exception;
// Return the number of colomn read // Return the number of colomn read
void fillFromDoc( void fillFromDoc(
final DBAccessMorphia ioDb, final DBAccessMorphia ioDb,

View File

@ -16,7 +16,6 @@ import org.kar.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.annotation.DataJson; import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.dataAccess.CountInOut; import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DBAccess; import org.kar.archidata.dataAccess.DBAccess;
import org.kar.archidata.dataAccess.DBAccessMorphia;
import org.kar.archidata.dataAccess.DBAccessSQL; import org.kar.archidata.dataAccess.DBAccessSQL;
import org.kar.archidata.dataAccess.DataFactory; import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter; import org.kar.archidata.dataAccess.LazyGetter;
@ -254,34 +253,28 @@ public class AddOnDataJson implements DataAccessAddOn {
final Object id, final Object id,
final String columnList, final String columnList,
final Object remoteKey) throws Exception { final Object remoteKey) throws Exception {
if (ioDb instanceof final DBAccessSQL daSQL) { 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", id.getClass()),
new OptionSpecifyType("id", id.getClass()), new OptionSpecifyType("covers", remoteKey.getClass(), true));
new OptionSpecifyType("covers", remoteKey.getClass(), true)); if (columnId != null && !columnId.equals("id")) {
if (columnId != null && !columnId.equals("id")) { options.add(new OptionRenameColumn("id", columnId));
options.add(new OptionRenameColumn("id", columnId));
}
if (columnList != null && !columnList.equals("covers")) {
options.add(new OptionRenameColumn("covers", columnList));
}
final TableCoversGeneric data = ioDb.get(TableCoversGeneric.class, id, options.getAllArray());
if (data.covers == null) {
return;
}
final List<Object> newList = new ArrayList<>();
for (final Object elem : data.covers) {
if (elem.equals(remoteKey)) {
continue;
}
newList.add(elem);
}
data.covers = newList;
ioDb.update(data, data.id, List.of("covers"), options.getAllArray());
} else if (ioDb instanceof final DBAccessMorphia dam) {
} else {
throw new DataAccessException("DataAccess Not managed");
} }
if (columnList != null && !columnList.equals("covers")) {
options.add(new OptionRenameColumn("covers", columnList));
}
final TableCoversGeneric data = ioDb.get(TableCoversGeneric.class, id, options.getAllArray());
if (data.covers == null) {
return;
}
final List<Object> newList = new ArrayList<>();
for (final Object elem : data.covers) {
if (elem.equals(remoteKey)) {
continue;
}
newList.add(elem);
}
data.covers = newList;
ioDb.update(data, data.id, List.of("covers"), options.getAllArray());
} }
} }

View File

@ -9,7 +9,7 @@ import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table; import jakarta.persistence.Table;
@Table(name = "TypeManyToOneLongRoot") @Table(name = "TypeManyToOneLongRoot")
//for Mongo //for Morphia
@Entity(value = "TypeManyToOneLongRoot") @Entity(value = "TypeManyToOneLongRoot")
public class TypeManyToOneLongRootExpand extends GenericData { public class TypeManyToOneLongRootExpand extends GenericData {