continue transformation

This commit is contained in:
Edouard DUPIN 2024-10-30 00:03:47 +01:00
parent af0beb8301
commit 751c0eebb7
21 changed files with 1871 additions and 31 deletions

View File

@ -23,6 +23,11 @@ import org.bson.types.ObjectId;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.CreationTimestamp;
import org.kar.archidata.annotation.UpdateTimestamp;
import org.kar.archidata.dataAccess.addOnMongo.AddOnDataJson;
import org.kar.archidata.dataAccess.addOnMongo.AddOnManyToMany;
import org.kar.archidata.dataAccess.addOnMongo.AddOnManyToOne;
import org.kar.archidata.dataAccess.addOnMongo.AddOnOneToMany;
import org.kar.archidata.dataAccess.addOnMongo.DataAccessAddOn;
import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.FilterValue;
@ -60,13 +65,12 @@ public class DataAccessMorphia extends DataAccess {
// by default we manage some add-on that permit to manage non-native model (like json serialization, List of external key as String list...)
static final List<DataAccessAddOn> addOn = new ArrayList<>();
/*static {
static {
addOn.add(new AddOnManyToMany());
addOn.add(new AddOnManyToOne());
addOn.add(new AddOnOneToMany());
addOn.add(new AddOnDataJson());
}
*/
/** Add a new add-on on the current management.
* @param addOn instantiate object on the Add-on

View File

@ -23,10 +23,11 @@ import java.util.UUID;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.CreationTimestamp;
import org.kar.archidata.annotation.UpdateTimestamp;
import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
import org.kar.archidata.dataAccess.addOn.AddOnManyToMany;
import org.kar.archidata.dataAccess.addOn.AddOnManyToOne;
import org.kar.archidata.dataAccess.addOn.AddOnOneToMany;
import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
import org.kar.archidata.dataAccess.addOnSQL.AddOnManyToMany;
import org.kar.archidata.dataAccess.addOnSQL.AddOnManyToOne;
import org.kar.archidata.dataAccess.addOnSQL.AddOnOneToMany;
import org.kar.archidata.dataAccess.addOnSQL.DataAccessAddOn;
import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.DBInterfaceRoot;

View File

@ -14,6 +14,7 @@ import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.CreationTimestamp;
import org.kar.archidata.annotation.DataIfNotExists;
import org.kar.archidata.annotation.UpdateTimestamp;
import org.kar.archidata.dataAccess.addOnSQL.DataAccessAddOn;
import org.kar.archidata.dataAccess.options.CreateDropTable;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.ConfigBaseVariable;

View File

@ -0,0 +1,459 @@
package org.kar.archidata.dataAccess.addOnMongo;
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.List;
import java.util.UUID;
import org.bson.Document;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataAccessMorphia;
import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.addOnSQL.model.TableCoversLongLong;
import org.kar.archidata.dataAccess.addOnSQL.model.TableCoversLongUUID;
import org.kar.archidata.dataAccess.addOnSQL.model.TableCoversUUIDLong;
import org.kar.archidata.dataAccess.addOnSQL.model.TableCoversUUIDUUID;
import org.kar.archidata.dataAccess.options.OverrideTableName;
import org.kar.archidata.exception.DataAccessException;
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 com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
import jakarta.validation.constraints.NotNull;
public class AddOnDataJson implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnDataJson.class);
@Override
public Class<?> getAnnotationClass() {
return DataJson.class;
}
@Override
public String getSQLFieldType(final Field elem) throws DataAccessException {
final String fieldName = AnnotationTools.getFieldName(elem);
return DataFactory.convertTypeInSQL(String.class, fieldName);
}
@Override
public boolean isCompatibleField(final Field elem) {
final DataJson decorators = elem.getDeclaredAnnotation(DataJson.class);
return decorators != null;
}
@Override
public void insertData(
final DataAccessMorphia ioDb,
final PreparedStatement ps,
final Field field,
final Object rootObject,
final CountInOut iii)
throws IllegalArgumentException, IllegalAccessException, SQLException, JsonProcessingException {
final Object data = field.get(rootObject);
if (data == null) {
ps.setNull(iii.value, Types.VARCHAR);
}
final ObjectMapper objectMapper = new ObjectMapper();
final String dataString = objectMapper.writeValueAsString(data);
ps.setString(iii.value, dataString);
iii.inc();
}
@Override
public boolean canInsert(final Field field) {
return true;
}
@Override
public boolean isInsertAsync(final Field field) {
return false;
}
@Override
public boolean canRetrieve(final Field field) {
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
public void fillFromQuery(
final DataAccessMorphia ioDb,
final ResultSet rs,
final Field field,
final Object data,
final CountInOut count,
final QueryOptions options,
final List<LazyGetter> lazyCall) throws Exception {
final String jsonData = rs.getString(count.value);
count.inc();
if (!rs.wasNull()) {
final ObjectMapper objectMapper = new ObjectMapper();
if (field.getType() == List.class) {
final ParameterizedType listType = (ParameterizedType) field.getGenericType();
final Class<?> listClass = (Class<?>) listType.getActualTypeArguments()[0];
if (listClass == Long.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Long>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
if (listClass == Float.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Float>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
if (listClass == Double.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Double>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
if (listClass == Integer.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Integer>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
if (listClass == Short.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Short>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
if (listClass == String.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<String>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
if (listClass == UUID.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<UUID>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
LOGGER.warn("Maybe fail to translate Model in datajson list: List<{}>", listClass.getCanonicalName());
}
final Object dataParsed = objectMapper.readValue(jsonData, field.getType());
field.set(data, dataParsed);
}
}
@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) throws Exception {
DataFactory.createTablesSpecificType(tableName, primaryField, field, mainTableBuilder, preActionList,
postActionList, createIfNotExist, createDrop, fieldId, JsonValue.class);
}
public static void addLink(
final DataAccess ioDb,
final Class<?> clazz,
final Long id,
final String column,
final Long remoteKey) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversLongLong data = ioDb.get(TableCoversLongLong.class, id, new OverrideTableName(tableName));
if (data.covers == null) {
data.covers = new ArrayList<>();
}
for (final Long elem : data.covers) {
if (elem.equals(remoteKey)) {
return;
}
}
data.covers.add(remoteKey);
ioDb.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
}
/**
* Adds a remoteKey to the covers list of a data entry identified by the given class type and ID.
* If the covers list is null, it initializes it. If the remoteKey already exists in the list,
* the method returns without making any changes.
*
* @param clazz The class type to retrieve the table name from.
* @param id The ID of the data object to fetch.
* @param column The name of the column (currently not used, but may be used for specifying a field name).
* @param remoteKey The UUID to add to the covers list.
* @throws Exception If an error occurs during data retrieval or update.
*/
public static void addLink(
final DataAccess ioDb,
final Class<?> clazz,
final Long id,
final String column,
final UUID remoteKey) throws Exception {
if (ioDb instanceof final DataAccessMorphia daSQL) {
final String tableName = AnnotationTools.getTableName(clazz);
// TODO: Get primary key name
final TableCoversLongUUID data = ioDb.get(TableCoversLongUUID.class, id, new OverrideTableName(tableName));
if (data.covers == null) {
data.covers = new ArrayList<>();
}
for (final UUID elem : data.covers) {
if (elem.equals(remoteKey)) {
return;
}
}
data.covers.add(remoteKey);
ioDb.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));// TODO: ,new OverrideFieldName("covers", column));
} else if (ioDb instanceof final DataAccessMorphia dam) {
} else {
throw new DataAccessException("DataAccess Not managed");
}
}
/**
* Adds a remoteKey to the covers list of a data entry identified by the given class type and ID.
* If the covers list is null, it initializes it. If the remoteKey already exists in the list,
* the method returns without making any changes.
*
* @param clazz The class type to retrieve the table name from.
* @param id The ID of the data object to fetch.
* @param column The name of the column (currently not used, but may be used for specifying a field name).
* @param remoteKey The UUID to add to the covers list.
* @throws Exception If an error occurs during data retrieval or update.
*/
public static void addLink(
final DataAccess ioDb,
final Class<?> clazz,
final UUID uuid,
final String column,
final UUID remoteKey) throws Exception {
if (ioDb instanceof final DataAccessMorphia daSQL) {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversUUIDUUID data = ioDb.get(TableCoversUUIDUUID.class, uuid,
new OverrideTableName(tableName));
if (data.covers == null) {
data.covers = new ArrayList<>();
}
for (final UUID elem : data.covers) {
if (elem.equals(remoteKey)) {
return;
}
}
data.covers.add(remoteKey);
ioDb.update(data, data.uuid, List.of("covers"), new OverrideTableName(tableName));
} else if (ioDb instanceof final DataAccessMorphia dam) {
} else {
throw new DataAccessException("DataAccess Not managed");
}
}
public static void addLink(
final DataAccess ioDb,
final Class<?> clazz,
final UUID uuid,
final String column,
final Long remoteKey) throws Exception {
if (ioDb instanceof final DataAccessMorphia daSQL) {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversUUIDLong data = ioDb.get(TableCoversUUIDLong.class, uuid,
new OverrideTableName(tableName));
if (data.covers == null) {
data.covers = new ArrayList<>();
}
for (final Long elem : data.covers) {
if (elem.equals(remoteKey)) {
return;
}
}
data.covers.add(remoteKey);
ioDb.update(data, data.uuid, List.of("covers"), new OverrideTableName(tableName));
} else if (ioDb instanceof final DataAccessMorphia dam) {
} else {
throw new DataAccessException("DataAccess Not managed");
}
}
public static void removeLink(
final DataAccess ioDb,
final Class<?> clazz,
final UUID uuid,
final String column,
final Long remoteKey) throws Exception {
if (ioDb instanceof final DataAccessMorphia daSQL) {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversUUIDLong data = ioDb.get(TableCoversUUIDLong.class, uuid,
new OverrideTableName(tableName));
if (data.covers == null) {
return;
}
final List<Long> newList = new ArrayList<>();
for (final Long elem : data.covers) {
if (elem.equals(remoteKey)) {
continue;
}
newList.add(elem);
}
data.covers = newList;
ioDb.update(data, data.uuid, List.of("covers"), new OverrideTableName(tableName));
} else if (ioDb instanceof final DataAccessMorphia dam) {
} else {
throw new DataAccessException("DataAccess Not managed");
}
}
public static void removeLink(
final DataAccess ioDb,
final Class<?> clazz,
final UUID uuid,
final String column,
final UUID remoteKey) throws Exception {
if (ioDb instanceof final DataAccessMorphia daSQL) {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversUUIDUUID data = ioDb.get(TableCoversUUIDUUID.class, uuid,
new OverrideTableName(tableName));
if (data.covers == null) {
return;
}
final List<UUID> newList = new ArrayList<>();
for (final UUID elem : data.covers) {
if (elem.equals(remoteKey)) {
continue;
}
newList.add(elem);
}
data.covers = newList;
ioDb.update(data, data.uuid, List.of("covers"), new OverrideTableName(tableName));
} else if (ioDb instanceof final DataAccessMorphia dam) {
} else {
throw new DataAccessException("DataAccess Not managed");
}
}
public static void removeLink(
final DataAccess ioDb,
final Class<?> clazz,
final Long id,
final String column,
final Long remoteKey) throws Exception {
if (ioDb instanceof final DataAccessMorphia daSQL) {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversLongLong data = ioDb.get(TableCoversLongLong.class, id, new OverrideTableName(tableName));
if (data.covers == null) {
return;
}
final List<Long> newList = new ArrayList<>();
for (final Long elem : data.covers) {
if (elem.equals(remoteKey)) {
continue;
}
newList.add(elem);
}
data.covers = newList;
ioDb.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
} else if (ioDb instanceof final DataAccessMorphia dam) {
} else {
throw new DataAccessException("DataAccess Not managed");
}
}
public static void removeLink(
final DataAccess ioDb,
final Class<?> clazz,
final Long id,
final String column,
final UUID remoteKey) throws Exception {
if (ioDb instanceof final DataAccessMorphia daSQL) {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversLongUUID data = ioDb.get(TableCoversLongUUID.class, id, new OverrideTableName(tableName));
if (data.covers == null) {
return;
}
final List<UUID> newList = new ArrayList<>();
for (final UUID elem : data.covers) {
if (elem.equals(remoteKey)) {
continue;
}
newList.add(elem);
}
data.covers = newList;
} else if (ioDb instanceof final DataAccessMorphia dam) {
final String collectionName = AnnotationTools.getCollectionName(clazz);
final Field primaryfield = AnnotationTools.getPrimaryKeyField(clazz);
final String primaryFieldName = AnnotationTools.getFieldName(primaryfield);
final MongoCollection<Document> collection = dam.getInterface().getDatastore().getDatabase()
.getCollection(collectionName);
// retrieve previous value:
final Document ret = collection.find(Filters.eq(primaryFieldName, id)).first();
if (ret == null) {
throw new DataAccessException("Element does not exist ...");
}
final List<UUID> newList = new ArrayList<>();
final List listValues = ret.get(remoteKey, newList.getClass());
/*
final Document actions = new Document();
// update value:
final Document actions = new Document();
if (!docSet.isEmpty()) {
actions.append("$set", docSet);
}
if (!docUnSet.isEmpty()) {
actions.append("$unset", docUnSet);
}
LOGGER.info("update some values: {}", actions.toJson());
final UpdateResult ret = collection.updateMany(filters, actions);
return ret.getModifiedCount();
final TableCoversLongUUID data = ioDb.getDocument(tableName, id);
if (data.covers == null) {
return;
}
final List<UUID> newList = new ArrayList<>();
for (final UUID elem : data.covers) {
if (elem.equals(remoteKey)) {
continue;
}
newList.add(elem);
}
data.covers = newList;
*/
} else {
throw new DataAccessException("DataAccess Not managed");
}
}
}

View File

@ -0,0 +1,593 @@
package org.kar.archidata.dataAccess.addOnMongo;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataAccessMorphia;
import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryAnd;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.QueryInList;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.addOnSQL.model.LinkTableLongLong;
import org.kar.archidata.dataAccess.addOnSQL.model.LinkTableLongUUID;
import org.kar.archidata.dataAccess.addOnSQL.model.LinkTableUUIDLong;
import org.kar.archidata.dataAccess.addOnSQL.model.LinkTableUUIDUUID;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.OverrideTableName;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToMany;
import jakarta.validation.constraints.NotNull;
public class AddOnManyToMany implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToMany.class);
static final String SEPARATOR_LONG = "-";
static final String SEPARATOR_UUID = "_";
@Override
public Class<?> getAnnotationClass() {
return ManyToMany.class;
}
@Override
public String getSQLFieldType(final Field elem) {
return null;
}
@Override
public boolean isCompatibleField(final Field elem) {
final ManyToMany decorators = elem.getDeclaredAnnotation(ManyToMany.class);
return decorators != null;
}
@Override
public void insertData(
final DataAccessMorphia ioDb,
final PreparedStatement ps,
final Field field,
final Object rootObject,
final CountInOut iii) throws SQLException, IllegalArgumentException, IllegalAccessException {
}
@Override
public boolean canInsert(final Field field) {
return false;
}
@Override
public boolean canRetrieve(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) {
return true;
}
final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class);
if (decorators == null) {
return false;
}
if (decorators.targetEntity() == objectClass) {
return true;
}
return false;
}
public static String generateLinkTableNameField(final String tableName, final Field field) throws Exception {
final String name = AnnotationTools.getFieldName(field);
return generateLinkTableName(tableName, name);
}
public static String generateLinkTableName(final String tableName, final String name) {
String localName = name;
if (name.endsWith("s")) {
localName = name.substring(0, name.length() - 1);
}
return tableName + "_link_" + localName;
}
public void generateConcatQuery(
@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 {
final ManyToMany manyToMany = AnnotationTools.getManyToMany(field);
String linkTableName = generateLinkTableName(tableName, name);
if (manyToMany.mappedBy() != null && manyToMany.mappedBy().length() != 0) {
// TODO: get the remote table name .....
final String remoteTableName = AnnotationTools.getTableName(manyToMany.targetEntity());
linkTableName = generateLinkTableName(remoteTableName, manyToMany.mappedBy());
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
final String tmpVariable = "tmp_" + Integer.toString(count.value);
querySelect.append(" (SELECT GROUP_CONCAT(");
querySelect.append(tmpVariable);
if (manyToMany.mappedBy() == null || manyToMany.mappedBy().length() == 0) {
querySelect.append(".object2Id ");
} else {
querySelect.append(".object1Id ");
}
if ("sqlite".equals(ConfigBaseVariable.getDBType())) {
querySelect.append(", ");
} else {
querySelect.append("SEPARATOR ");
}
querySelect.append("'");
if (objectClass == Long.class) {
querySelect.append(SEPARATOR_LONG);
} else if (objectClass == UUID.class) {} else {
final Class<?> foreignKeyType = AnnotationTools.getPrimaryKeyField(objectClass).getType();
if (foreignKeyType == Long.class) {
querySelect.append(SEPARATOR_LONG);
}
}
querySelect.append("') FROM ");
querySelect.append(linkTableName);
querySelect.append(" ");
querySelect.append(tmpVariable);
querySelect.append(" WHERE ");
querySelect.append(tmpVariable);
querySelect.append(".deleted = false");
querySelect.append(" AND ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(primaryKey);
querySelect.append(" = ");
querySelect.append(tmpVariable);
querySelect.append(".");
if (manyToMany.mappedBy() == null || manyToMany.mappedBy().length() == 0) {
querySelect.append("object1Id ");
} else {
querySelect.append("object2Id ");
}
if (!"sqlite".equals(ConfigBaseVariable.getDBType())) {
querySelect.append(" GROUP BY ");
querySelect.append(tmpVariable);
if (manyToMany.mappedBy() == null || manyToMany.mappedBy().length() == 0) {
querySelect.append(".object1Id");
} else {
querySelect.append(".object2Id");
}
}
querySelect.append(") AS ");
querySelect.append(name);
querySelect.append(" ");
/* " (SELECT GROUP_CONCAT(tmp.data_id SEPARATOR '-')" + " FROM cover_link_node tmp" + " WHERE tmp.deleted = false" +
* " AND node.id = tmp.node_id" + " GROUP BY tmp.node_id) AS covers" + */
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];
// TODO: manage better the eager and lazy !!
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()) {
if (decorators.fetch() == FetchType.EAGER) {
throw new DataAccessException("EAGER is not supported for list of element...");
} else {
generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options);
}
}
}
@Override
public void fillFromQuery(
final DataAccessMorphia 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) {
LOGGER.error("Can not ManyToMany with other than List Model: {}", field.getType().getCanonicalName());
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass == Long.class) {
final List<Long> idList = ioDb.getListOfIds(rs, count.value, SEPARATOR_LONG);
field.set(data, idList);
count.inc();
return;
} else if (objectClass == UUID.class) {
final List<UUID> idList = ioDb.getListOfRawUUIDs(rs, count.value);
field.set(data, idList);
count.inc();
return;
}
final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class);
if (decorators == null) {
return;
}
if (objectClass == decorators.targetEntity()) {
final Class<?> foreignKeyType = AnnotationTools.getPrimaryKeyField(objectClass).getType();
if (decorators.fetch() == FetchType.EAGER) {
throw new DataAccessException("EAGER is not supported for list of element...");
} else if (foreignKeyType == Long.class) {
final List<Long> idList = ioDb.getListOfIds(rs, count.value, SEPARATOR_LONG);
// field.set(data, idList);
count.inc();
if (idList != null && idList.size() > 0) {
final String idField = AnnotationTools.getFieldName(AnnotationTools.getIdField(objectClass));
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
final List<Long> childs = new ArrayList<>(idList);
// TODO: update to have get with abstract types ....
@SuppressWarnings("unchecked")
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryInList<>(idField, childs)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
} else if (foreignKeyType == UUID.class) {
final List<UUID> idList = ioDb.getListOfRawUUIDs(rs, count.value);
// field.set(data, idList);
count.inc();
if (idList != null && idList.size() > 0) {
final String idField = AnnotationTools.getFieldName(AnnotationTools.getIdField(objectClass));
// 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 ....
@SuppressWarnings("unchecked")
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryInList<>(idField, childs)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
}
}
}
@Override
public boolean isUpdateAsync(final Field field) {
return true;
}
@Override
public void asyncUpdate(
final DataAccessMorphia ioDb,
final String tableName,
final Object localKey,
final Field field,
final Object data,
final List<LazyGetter> actions) throws Exception {
if (field.getType() != List.class) {
LOGGER.error("Can not ManyToMany with other than List Model: {}", field.getType().getCanonicalName());
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass != Long.class && objectClass != UUID.class) {
throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<"
+ objectClass.getCanonicalName() + ">");
}
final String columnName = AnnotationTools.getFieldName(field);
final String linkTableName = generateLinkTableName(tableName, columnName);
if (localKey instanceof final Long localKeyLong) {
if (objectClass == Long.class) {
actions.add(() -> {
ioDb.deleteWhere(LinkTableLongLong.class, new OverrideTableName(linkTableName),
new Condition(new QueryCondition("object1Id", "=", localKeyLong)));
});
asyncInsert(ioDb, tableName, localKey, field, data, actions);
} else {
actions.add(() -> {
ioDb.deleteWhere(LinkTableLongUUID.class, new OverrideTableName(linkTableName),
new Condition(new QueryCondition("object1Id", "=", localKeyLong)));
});
asyncInsert(ioDb, tableName, localKey, field, data, actions);
}
} else if (localKey instanceof final UUID localKeyUUID) {
if (objectClass == Long.class) {
actions.add(() -> {
ioDb.deleteWhere(LinkTableUUIDLong.class, new OverrideTableName(linkTableName),
new Condition(new QueryCondition("object1Id", "=", localKeyUUID)));
});
asyncInsert(ioDb, tableName, localKey, field, data, actions);
} else {
actions.add(() -> {
ioDb.deleteWhere(LinkTableUUIDUUID.class, new OverrideTableName(linkTableName),
new Condition(new QueryCondition("object1Id", "=", localKeyUUID)));
});
asyncInsert(ioDb, tableName, localKey, field, data, actions);
}
}
}
@Override
public boolean isInsertAsync(final Field field) {
return true;
}
@Override
public void asyncInsert(
final DataAccessMorphia ioDb,
final String tableName,
final Object localKey,
final Field field,
final Object data,
final List<LazyGetter> actions) throws Exception {
if (data == null) {
return;
}
if (field.getType() != List.class) {
LOGGER.error("Can not ManyToMany with other than List Model: {}", field.getType().getCanonicalName());
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass != Long.class && objectClass != UUID.class) {
throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<"
+ objectClass.getCanonicalName() + ">");
}
final String columnName = AnnotationTools.getFieldName(field);
final String linkTableName = generateLinkTableName(tableName, columnName);
if (localKey instanceof final Long localKeyLong) {
if (objectClass == Long.class) {
// ========================================================
// == Link a "Long" primary Key with List<Long>
// ========================================================
@SuppressWarnings("unchecked")
final List<Long> dataCasted = (List<Long>) data;
if (dataCasted.size() == 0) {
return;
}
final List<LinkTableLongLong> insertElements = new ArrayList<>();
for (final Long remoteKey : dataCasted) {
if (remoteKey == null) {
throw new DataAccessException("Try to insert remote key with null value");
}
insertElements.add(new LinkTableLongLong(localKeyLong, remoteKey));
}
if (insertElements.size() == 0) {
LOGGER.warn("Insert multiple link without any value (may have null in the list): {}", dataCasted);
return;
}
actions.add(() -> {
ioDb.insertMultiple(insertElements, new OverrideTableName(linkTableName));
});
} else {
// ========================================================
// == Link a "Long" primary Key with List<UUID>
// ========================================================
@SuppressWarnings("unchecked")
final List<UUID> dataCasted = (List<UUID>) data;
if (dataCasted.size() == 0) {
return;
}
final List<LinkTableLongUUID> insertElements = new ArrayList<>();
for (final UUID remoteKey : dataCasted) {
if (remoteKey == null) {
throw new DataAccessException("Try to insert remote key with null value");
}
insertElements.add(new LinkTableLongUUID(localKeyLong, remoteKey));
}
if (insertElements.size() == 0) {
LOGGER.warn("Insert multiple link without any value (may have null in the list): {}", dataCasted);
return;
}
actions.add(() -> {
ioDb.insertMultiple(insertElements, new OverrideTableName(linkTableName));
});
}
} else if (localKey instanceof final UUID localKeyUUID) {
if (objectClass == Long.class) {
// ========================================================
// == Link a "UUID" primary Key with List<Long>
// ========================================================
@SuppressWarnings("unchecked")
final List<Long> dataCasted = (List<Long>) data;
if (dataCasted.size() == 0) {
return;
}
final List<LinkTableUUIDLong> insertElements = new ArrayList<>();
for (final Long remoteKey : dataCasted) {
if (remoteKey == null) {
throw new DataAccessException("Try to insert remote key with null value");
}
insertElements.add(new LinkTableUUIDLong(localKeyUUID, remoteKey));
}
if (insertElements.size() == 0) {
LOGGER.warn("Insert multiple link without any value (may have null in the list): {}", dataCasted);
return;
}
actions.add(() -> {
ioDb.insertMultiple(insertElements, new OverrideTableName(linkTableName));
});
} else {
// ========================================================
// == Link a "UUID" primary Key with List<UUID>
// ========================================================
@SuppressWarnings("unchecked")
final List<UUID> dataCasted = (List<UUID>) data;
if (dataCasted.size() == 0) {
return;
}
final List<LinkTableUUIDUUID> insertElements = new ArrayList<>();
for (final UUID remoteKey : dataCasted) {
if (remoteKey == null) {
throw new DataAccessException("Try to insert remote key with null value");
}
insertElements.add(new LinkTableUUIDUUID(localKeyUUID, remoteKey));
}
if (insertElements.size() == 0) {
LOGGER.warn("Insert multiple link without any value (may have null in the list): {}", dataCasted);
return;
}
actions.add(() -> {
ioDb.insertMultiple(insertElements, new OverrideTableName(linkTableName));
});
}
} else {
throw new DataAccessException("Not manage access of remte key like ManyToMany other than Long or UUID: "
+ localKey.getClass().getCanonicalName());
}
}
@Override
public void drop(final DataAccessMorphia ioDb, final String tableName, final Field field) throws Exception {
final String columnName = AnnotationTools.getFieldName(field);
final String linkTableName = generateLinkTableName(tableName, columnName);
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass != Long.class && objectClass != UUID.class) {
throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<"
+ objectClass.getCanonicalName() + ">");
}
ioDb.drop(LinkTableLongLong.class, new OverrideTableName(linkTableName));
}
@Override
public void cleanAll(final DataAccessMorphia ioDb, final String tableName, final Field field) throws Exception {
final String columnName = AnnotationTools.getFieldName(field);
final String linkTableName = generateLinkTableName(tableName, columnName);
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass != Long.class && objectClass != UUID.class) {
throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<"
+ objectClass.getCanonicalName() + ">");
}
ioDb.cleanAll(LinkTableLongLong.class, new OverrideTableName(linkTableName));
}
public static void addLink(
final DataAccess ioDb,
final Class<?> clazz,
final long localKey,
final String column,
final long remoteKey) throws Exception {
if (ioDb instanceof final DataAccessMorphia daSQL) {
final String tableName = AnnotationTools.getTableName(clazz);
final String linkTableName = generateLinkTableName(tableName, column);
/* final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; if (objectClass != Long.class && objectClass != UUID.class) { throw new
* DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<" + objectClass.getCanonicalName() + ">"); } */
final LinkTableLongLong insertElement = new LinkTableLongLong(localKey, remoteKey);
daSQL.insert(insertElement, new OverrideTableName(linkTableName));
} else if (ioDb instanceof final DataAccessMorphia dam) {
} else {
throw new DataAccessException("DataAccess Not managed");
}
}
public static long removeLink(
final DataAccess ioDb,
final Class<?> clazz,
final long localKey,
final String column,
final long remoteKey) throws Exception {
if (ioDb instanceof final DataAccessMorphia daSQL) {
final String tableName = AnnotationTools.getTableName(clazz);
final String linkTableName = generateLinkTableName(tableName, column);
return daSQL.deleteWhere(LinkTableLongLong.class, new OverrideTableName(linkTableName),
new Condition(new QueryAnd(new QueryCondition("object1Id", "=", localKey),
new QueryCondition("object2Id", "=", remoteKey))));
} else if (ioDb instanceof final DataAccessMorphia dam) {
return 0L;
} else {
throw new DataAccessException("DataAccess Not managed");
}
}
@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) throws Exception {
final ManyToMany manyToMany = AnnotationTools.getManyToMany(field);
if (manyToMany.mappedBy() != null && manyToMany.mappedBy().length() != 0) {
// not the reference model to create base:
return;
}
final String linkTableName = generateLinkTableNameField(tableName, field);
final QueryOptions options = new QueryOptions(new OverrideTableName(linkTableName));
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass != Long.class && objectClass != UUID.class) {
throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<"
+ objectClass.getCanonicalName() + ">");
}
final Class<?> primaryType = primaryField.getType();
if (primaryType == Long.class) {
if (objectClass == Long.class) {
final List<String> sqlCommand = DataFactory.createTable(LinkTableLongLong.class, options);
postActionList.addAll(sqlCommand);
} else {
final List<String> sqlCommand = DataFactory.createTable(LinkTableLongUUID.class, options);
postActionList.addAll(sqlCommand);
}
} else if (primaryType == UUID.class) {
if (objectClass == Long.class) {
final List<String> sqlCommand = DataFactory.createTable(LinkTableUUIDLong.class, options);
postActionList.addAll(sqlCommand);
} else {
final List<String> sqlCommand = DataFactory.createTable(LinkTableUUIDUUID.class, options);
postActionList.addAll(sqlCommand);
}
} else {
throw new DataAccessException("Can not ManyToMany with other than primary key type Long or UUID Model: "
+ primaryType.getCanonicalName());
}
}
}

View File

@ -0,0 +1,308 @@
package org.kar.archidata.dataAccess.addOnMongo;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Types;
import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DataAccessMorphia;
import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.UuidUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToOne;
import jakarta.validation.constraints.NotNull;
public class AddOnManyToOne implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToMany.class);
@Override
public Class<?> getAnnotationClass() {
return ManyToOne.class;
}
@Override
public String getSQLFieldType(final Field field) throws Exception {
final String fieldName = AnnotationTools.getFieldName(field);
try {
return DataFactory.convertTypeInSQL(field.getType(), fieldName);
} catch (final Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public boolean isCompatibleField(final Field elem) {
return elem.getDeclaredAnnotation(ManyToOne.class) != null;
}
@Override
public void insertData(
final DataAccessMorphia ioDb,
final PreparedStatement ps,
final Field field,
final Object rootObject,
final CountInOut iii) throws Exception {
final Object data = field.get(rootObject);
if (data == null) {
if (field.getType() == Long.class) {
ps.setNull(iii.value, Types.BIGINT);
} else if (field.getType() == Integer.class) {
ps.setNull(iii.value, Types.INTEGER);
} else if (field.getType() == Short.class) {
ps.setNull(iii.value, Types.INTEGER);
} else if (field.getType() == String.class) {
ps.setNull(iii.value, Types.VARCHAR);
} else if (field.getType() == UUID.class) {
ps.setNull(iii.value, Types.BINARY);
}
} else if (field.getType() == Long.class) {
final Long dataTyped = (Long) data;
ps.setLong(iii.value, dataTyped);
} else if (field.getType() == Integer.class) {
final Integer dataTyped = (Integer) data;
ps.setInt(iii.value, dataTyped);
} else if (field.getType() == Short.class) {
final Short dataTyped = (Short) data;
ps.setShort(iii.value, dataTyped);
} else if (field.getType() == String.class) {
final String dataTyped = (String) data;
ps.setString(iii.value, dataTyped);
} else if (field.getType() == UUID.class) {
final UUID dataTyped = (UUID) data;
LOGGER.info("Generate UUTD for DB: {}", dataTyped);
final byte[] dataByte = UuidUtils.asBytes(dataTyped);
ps.setBytes(iii.value, dataByte);
} else {
final Field idField = AnnotationTools.getFieldOfId(field.getType());
final Object uid = idField.get(data);
if (uid == null) {
ps.setNull(iii.value, Types.BIGINT);
throw new DataAccessException("Not implemented adding subClasses ==> add it manualy before...");
} else {
final Long dataLong = (Long) uid;
ps.setLong(iii.value, dataLong);
}
}
iii.inc();
}
@Override
public boolean canInsert(final Field field) {
if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class
|| field.getType() == String.class || field.getType() == UUID.class) {
return true;
}
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
if (field.getType() == decorators.targetEntity()) {
return true;
}
return false;
}
@Override
public boolean isInsertAsync(final Field field) throws Exception {
return false;
}
@Override
public boolean canRetrieve(final Field field) {
final Class<?> classType = field.getType();
if (classType == Long.class || classType == Integer.class || classType == Short.class
|| classType == String.class || classType == UUID.class) {
return true;
}
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
if (field.getType() == decorators.targetEntity()) {
return true;
}
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()) {
if (decorators.fetch() == FetchType.EAGER) {
// TODO: rework this to have a lazy mode ...
DataAccessMorphia.generateSelectField(querySelect, query, field.getType(), options, count);
final Class<?> subType = field.getType();
final String subTableName = AnnotationTools.getTableName(subType);
final Field idField = AnnotationTools.getFieldOfId(subType);
query.append("LEFT OUTER JOIN `");
query.append(subTableName);
query.append("` ON ");
query.append(subTableName);
query.append(".");
query.append(AnnotationTools.getFieldName(idField));
query.append(" = ");
query.append(tableName);
query.append(".");
query.append(AnnotationTools.getFieldName(field));
} else {
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(name);
count.inc();
return;
}
}
/* SELECT k.id, r.id FROM `right` k LEFT OUTER JOIN `rightDescription` r ON k.rightDescriptionId=r.id */
}
@Override
public void fillFromQuery(
final DataAccessMorphia 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() == Long.class) {
final Long foreignKey = rs.getLong(count.value);
count.inc();
if (!rs.wasNull()) {
field.set(data, foreignKey);
}
return;
}
if (field.getType() == Integer.class) {
final Integer foreignKey = rs.getInt(count.value);
count.inc();
if (!rs.wasNull()) {
field.set(data, foreignKey);
}
return;
}
if (field.getType() == Short.class) {
final Short foreignKey = rs.getShort(count.value);
count.inc();
if (!rs.wasNull()) {
field.set(data, foreignKey);
}
return;
}
if (field.getType() == String.class) {
final String foreignKey = rs.getString(count.value);
count.inc();
if (!rs.wasNull()) {
field.set(data, foreignKey);
}
return;
}
if (field.getType() == UUID.class) {
final byte[] tmp = rs.getBytes(count.value);
count.inc();
if (!rs.wasNull()) {
final UUID foreignKey = UuidUtils.asUuid(tmp);
field.set(data, foreignKey);
}
return;
}
final Class<?> objectClass = field.getType();
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
if (decorators == null) {
return;
}
if (objectClass == decorators.targetEntity()) {
if (decorators.fetch() == FetchType.EAGER) {
final CountInOut countNotNull = new CountInOut(0);
final Object dataNew = ioDb.createObjectFromSQLRequest(rs, objectClass, count, countNotNull, options,
lazyCall);
if (dataNew != null && countNotNull.value != 0) {
field.set(data, dataNew);
}
return;
}
final Field remotePrimaryKeyField = AnnotationTools.getFieldOfId(objectClass);
final Class<?> remotePrimaryKeyType = remotePrimaryKeyField.getType();
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 ...
final Long foreignKey = rs.getLong(count.value);
count.inc();
if (!rs.wasNull()) {
// 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.get(decorators.targetEntity(), foreignKey);
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
} 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 ...
final UUID foreignKey = ioDb.getListOfRawUUID(rs, count.value);
count.inc();
if (foreignKey != null) {
// 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.get(decorators.targetEntity(), foreignKey);
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
}
}
}
// TODO : refacto this table to manage a generic table with dynamic name to be serialisable with the default system
@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) throws Exception {
final Class<?> classType = field.getType();
if (classType == Long.class || classType == Integer.class || classType == Short.class
|| classType == String.class || classType == UUID.class) {
DataFactory.createTablesSpecificType(tableName, primaryField, field, mainTableBuilder, preActionList,
postActionList, createIfNotExist, createDrop, fieldId, classType);
} else {
LOGGER.error("Support only the Long remote field of ecternal primary keys...");
DataFactory.createTablesSpecificType(tableName, primaryField, field, mainTableBuilder, preActionList,
postActionList, createIfNotExist, createDrop, fieldId, Long.class);
}
}
}

View File

@ -0,0 +1,324 @@
package org.kar.archidata.dataAccess.addOnMongo;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DataAccessMorphia;
import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.QueryOptions;
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.LoggerFactory;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany;
import jakarta.validation.constraints.NotNull;
public class AddOnOneToMany implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnOneToMany.class);
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
public Class<?> getAnnotationClass() {
return OneToMany.class;
}
@Override
public String getSQLFieldType(final Field field) throws Exception {
final String fieldName = AnnotationTools.getFieldName(field);
try {
return DataFactory.convertTypeInSQL(Long.class, fieldName);
} catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
@Override
public boolean isCompatibleField(final Field field) {
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
return decorators != null;
}
@Override
public void insertData(
final DataAccessMorphia ioDb,
final PreparedStatement ps,
final Field field,
final Object rootObject,
final CountInOut iii) throws SQLException, IllegalArgumentException, IllegalAccessException {
throw new IllegalAccessException("Can not generate an inset of @OneToMany");
}
@Override
public boolean canInsert(final Field field) {
return false;
}
@Override
public boolean isInsertAsync(final Field field) throws Exception {
// TODO: can be implemented later...
return false;
}
@Override
public boolean canRetrieve(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) {
return true;
}
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
if (decorators == null) {
return false;
}
if (decorators.targetEntity() == objectClass) {
return true;
}
return false;
}
public void generateConcatQuery(
@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 String remoteTablePrimaryKeyName = AnnotationTools
.getFieldName(AnnotationTools.getPrimaryKeyField(targetEntity));
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);
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
public void fillFromQuery(
final DataAccessMorphia 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) {
LOGGER.error("Can not OneToMany with other than List Model: {}", field.getType().getCanonicalName());
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
if (decorators == null) {
return;
}
if (objectClass == Long.class) {
final List<Long> idList = ioDb.getListOfIds(rs, count.value, SEPARATOR_LONG);
field.set(data, idList);
count.inc();
return;
} else if (objectClass == UUID.class) {
final List<UUID> idList = ioDb.getListOfRawUUIDs(rs, count.value);
field.set(data, idList);
count.inc();
return;
}
if (objectClass == decorators.targetEntity()) {
Long parentIdTmp = null;
UUID parendUuidTmp = null;
try {
final String modelData = rs.getString(count.value);
parentIdTmp = Long.valueOf(modelData);
count.inc();
} catch (final NumberFormatException ex) {
final List<UUID> idList = ioDb.getListOfRawUUIDs(rs, count.value);
parendUuidTmp = idList.get(0);
count.inc();
}
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 (decorators.fetch() == FetchType.EAGER) {
throw new DataAccessException("EAGER is not supported for list of element...");
} else if (parentId != null) {
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
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);
}
}
}
}
// TODO : refacto this table to manage a generic table with dynamic name to be serialize with the default system
@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) throws Exception {
// This is a remote field ==> nothing to generate (it is stored in the remote object
}
}

View File

@ -0,0 +1,149 @@
package org.kar.archidata.dataAccess.addOnMongo;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DataAccessMorphia;
import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryOptions;
import jakarta.validation.constraints.NotNull;
public interface DataAccessAddOn {
/** Get the Class of the declaration annotation
* @return The annotation class */
Class<?> getAnnotationClass();
/** Get the SQL type that is needed to declare for the specific Field Type.
* @param elem Field to declare.
* @return SQL type to create. */
String getSQLFieldType(Field elem) throws Exception;
/** Check if the field is manage by the local add-on
* @param elem Field to inspect.
* @return True of the field is manage by the current Add-on. */
boolean isCompatibleField(Field elem);
/** Insert data in the specific field (the field must be in the current db, otherwiise it does not work at all.
* @param ps DB statement interface.
* @param data The date to inject.
* @param iii The index of injection
* @return the new index of injection in case of multiple value management
* @throws SQLException */
void insertData(final DataAccessMorphia ioDb, PreparedStatement ps, final Field field, Object data, CountInOut iii)
throws Exception, SQLException, IllegalArgumentException, IllegalAccessException;
/** Element can insert in the single request
* @param field
* @return */
default boolean canInsert(final Field field) {
return false;
}
/** Element can be retrieve with the specific mode
* @param field
* @return */
default boolean canRetrieve(final Field field) {
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
void fillFromQuery(
final DataAccessMorphia ioDb,
ResultSet rs,
Field field,
Object data,
CountInOut count,
QueryOptions options,
final List<LazyGetter> lazyCall)
throws Exception, SQLException, IllegalArgumentException, IllegalAccessException;
/** Create associated table of the specific element.
* @param tableName
* @param elem
* @param mainTableBuilder
* @param ListOtherTables
* @param createIfNotExist
* @param createDrop
* @param fieldId
* @throws Exception */
void createTables(
String tableName,
final Field primaryField,
Field field,
StringBuilder mainTableBuilder,
List<String> preActionList,
List<String> postActionList,
boolean createIfNotExist,
boolean createDrop,
int fieldId) throws Exception;
/** Some action must be done asynchronously for update or remove element
* @param field
* @return */
default boolean isInsertAsync(final Field field) throws Exception {
return false;
}
/** 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. */
default void asyncInsert(
final DataAccessMorphia ioDb,
final String tableName,
final Object localId,
final Field field,
final Object data,
final List<LazyGetter> actions) throws Exception {
}
/** Some action must be done asynchronously for update or remove element
* @param field
* @return */
default boolean isUpdateAsync(final Field field) throws Exception {
return false;
}
/** 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. */
default void asyncUpdate(
final DataAccessMorphia ioDb,
final String tableName,
final Object localId,
final Field field,
final Object data,
final List<LazyGetter> actions) throws Exception {
}
default void drop(final DataAccessMorphia ioDb, final String tableName, final Field field) throws Exception {
}
default void cleanAll(final DataAccessMorphia ioDb, final String tableName, final Field field) throws Exception {
}
}

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess.addOn;
package org.kar.archidata.dataAccess.addOnSQL;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
@ -15,16 +15,15 @@ import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataAccessAddOn;
import org.kar.archidata.dataAccess.DataAccessMorphia;
import org.kar.archidata.dataAccess.DataAccessSQL;
import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.addOn.model.TableCoversLongLong;
import org.kar.archidata.dataAccess.addOn.model.TableCoversLongUUID;
import org.kar.archidata.dataAccess.addOn.model.TableCoversUUIDLong;
import org.kar.archidata.dataAccess.addOn.model.TableCoversUUIDUUID;
import org.kar.archidata.dataAccess.addOnSQL.model.TableCoversLongLong;
import org.kar.archidata.dataAccess.addOnSQL.model.TableCoversLongUUID;
import org.kar.archidata.dataAccess.addOnSQL.model.TableCoversUUIDLong;
import org.kar.archidata.dataAccess.addOnSQL.model.TableCoversUUIDUUID;
import org.kar.archidata.dataAccess.options.OverrideTableName;
import org.kar.archidata.exception.DataAccessException;
import org.slf4j.Logger;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess.addOn;
package org.kar.archidata.dataAccess.addOnSQL;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
@ -12,7 +12,6 @@ import java.util.UUID;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataAccessAddOn;
import org.kar.archidata.dataAccess.DataAccessMorphia;
import org.kar.archidata.dataAccess.DataAccessSQL;
import org.kar.archidata.dataAccess.DataFactory;
@ -21,10 +20,10 @@ import org.kar.archidata.dataAccess.QueryAnd;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.QueryInList;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.addOn.model.LinkTableLongLong;
import org.kar.archidata.dataAccess.addOn.model.LinkTableLongUUID;
import org.kar.archidata.dataAccess.addOn.model.LinkTableUUIDLong;
import org.kar.archidata.dataAccess.addOn.model.LinkTableUUIDUUID;
import org.kar.archidata.dataAccess.addOnSQL.model.LinkTableLongLong;
import org.kar.archidata.dataAccess.addOnSQL.model.LinkTableLongUUID;
import org.kar.archidata.dataAccess.addOnSQL.model.LinkTableUUIDLong;
import org.kar.archidata.dataAccess.addOnSQL.model.LinkTableUUIDUUID;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.OverrideTableName;
import org.kar.archidata.exception.DataAccessException;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess.addOn;
package org.kar.archidata.dataAccess.addOnSQL;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
@ -9,7 +9,6 @@ import java.util.UUID;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DataAccessAddOn;
import org.kar.archidata.dataAccess.DataAccessSQL;
import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess.addOn;
package org.kar.archidata.dataAccess.addOnSQL;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
@ -12,7 +12,6 @@ import java.util.stream.Collectors;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DataAccessAddOn;
import org.kar.archidata.dataAccess.DataAccessSQL;
import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess;
package org.kar.archidata.dataAccess.addOnSQL;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
@ -6,6 +6,11 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DataAccessSQL;
import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryOptions;
import jakarta.validation.constraints.NotNull;
public interface DataAccessAddOn {

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess.addOn.model;
package org.kar.archidata.dataAccess.addOnSQL.model;
import org.kar.archidata.model.UUIDGenericDataSoftDelete;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess.addOn.model;
package org.kar.archidata.dataAccess.addOnSQL.model;
import java.util.UUID;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess.addOn.model;
package org.kar.archidata.dataAccess.addOnSQL.model;
import java.util.UUID;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess.addOn.model;
package org.kar.archidata.dataAccess.addOnSQL.model;
import java.util.UUID;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess.addOn.model;
package org.kar.archidata.dataAccess.addOnSQL.model;
import java.util.List;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess.addOn.model;
package org.kar.archidata.dataAccess.addOnSQL.model;
import java.util.ArrayList;
import java.util.List;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess.addOn.model;
package org.kar.archidata.dataAccess.addOnSQL.model;
import java.util.ArrayList;
import java.util.List;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess.addOn.model;
package org.kar.archidata.dataAccess.addOnSQL.model;
import java.util.List;
import java.util.UUID;