diff --git a/.classpath b/.classpath
index c10152e..7cc9ec0 100644
--- a/.classpath
+++ b/.classpath
@@ -27,6 +27,7 @@
+
diff --git a/.project b/.project
index 17915bb..1cb3dcd 100644
--- a/.project
+++ b/.project
@@ -10,16 +10,6 @@
-
- org.eclipse.ui.externaltools.ExternalToolBuilder
- full,incremental,
-
-
- LaunchConfigHandle
- <project>/.externalToolBuilders/org.eclipse.jdt.core.javabuilder.launch
-
-
-
org.eclipse.m2e.core.maven2Builder
diff --git a/src/org/kar/archidata/annotation/AnnotationTools.java b/src/org/kar/archidata/annotation/AnnotationTools.java
index 61d0839..1cd7c17 100644
--- a/src/org/kar/archidata/annotation/AnnotationTools.java
+++ b/src/org/kar/archidata/annotation/AnnotationTools.java
@@ -2,7 +2,10 @@ package org.kar.archidata.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import org.kar.archidata.dataAccess.QueryOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -15,6 +18,18 @@ import jakarta.persistence.Table;
public class AnnotationTools {
static final Logger LOGGER = LoggerFactory.getLogger(AnnotationTools.class);
+ public static String getTableName(final Class> clazz, final QueryOptions options) throws Exception {
+ if (options != null) {
+ final Object data = options.get(QueryOptions.OVERRIDE_TABLE_NAME);
+ if (data instanceof final String optionString) {
+ return optionString;
+ } else if (data != null) {
+ LOGGER.error("'{}' ==> has not a String value: {}", QueryOptions.SQL_DELETED_DISABLE, data);
+ }
+ }
+ return AnnotationTools.getTableName(clazz);
+ }
+
public static String getTableName(final Class> element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Table.class);
if (annotation.length == 0) {
@@ -205,5 +220,32 @@ public class AnnotationTools {
}
return null;
}
+
+ public static List getFieldsNames(final Class> clazz) throws Exception {
+ return getFieldsNamesFilter(clazz, false);
+ }
+
+ public static List getAllFieldsNames(final Class> clazz) throws Exception {
+ return getFieldsNamesFilter(clazz, true);
+ }
+
+ private static List getFieldsNamesFilter(final Class> clazz, final boolean full) throws Exception {
+ final List out = new ArrayList<>();
+ for (final Field elem : clazz.getFields()) {
+ // static field is only for internal global declaration ==> remove it ..
+ if (java.lang.reflect.Modifier.isStatic(elem.getModifiers())) {
+ continue;
+ }
+ if (!full && AnnotationTools.isGenericField(elem)) {
+ continue;
+ }
+ out.add(AnnotationTools.getFieldName(elem));
+ }
+ return out;
+ }
+
+ public static boolean isGenericField(final Field elem) throws Exception {
+ return AnnotationTools.isPrimaryKey(elem) || AnnotationTools.isCreatedAtField(elem) || AnnotationTools.isUpdateAtField(elem);
+ }
}
diff --git a/src/org/kar/archidata/dataAccess/DataAccess.java b/src/org/kar/archidata/dataAccess/DataAccess.java
index c3a38fc..63c22b8 100644
--- a/src/org/kar/archidata/dataAccess/DataAccess.java
+++ b/src/org/kar/archidata/dataAccess/DataAccess.java
@@ -1,9 +1,12 @@
package org.kar.archidata.dataAccess;
import java.io.IOException;
+import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
import java.sql.Types;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -11,9 +14,8 @@ import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
+import java.util.List;
-import org.aopalliance.reflect.Class;
-import org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.List;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.CreationTimestamp;
@@ -30,43 +32,39 @@ import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.protobuf.Timestamp;
-import com.mysql.cj.x.protobuf.MysqlxDatatypes.Scalar.String;
-import com.mysql.cj.xdevapi.Statement;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.ws.rs.InternalServerErrorException;
-import javassist.bytecode.Descriptor.Iterator;
public class DataAccess {
static final Logger LOGGER = LoggerFactory.getLogger(DataAccess.class);
static final List addOn = new ArrayList<>();
-
+
static {
addOn.add(new AddOnManyToMany());
addOn.add(new AddOnManyToOne());
addOn.add(new AddOnSQLTableExternalForeinKeyAsList());
}
-
+
public static void addAddOn(final DataAccessAddOn addOn) {
DataAccess.addOn.add(addOn);
}
-
+
public static class ExceptionDBInterface extends Exception {
private static final long serialVersionUID = 1L;
public int errorID;
-
+
public ExceptionDBInterface(final int errorId, final String message) {
super(message);
this.errorID = errorId;
}
}
-
+
public DataAccess() {
-
+
}
-
+
public static boolean isDBExist(final String name) throws InternalServerErrorException {
if ("sqlite".equals(ConfigBaseVariable.getDBType())) {
// no base manage in sqLite ...
@@ -108,7 +106,7 @@ public class DataAccess {
}
throw new InternalServerErrorException("Can Not manage the DB-access");
}
-
+
public static boolean createDB(final String name) {
if ("sqlite".equals(ConfigBaseVariable.getDBType())) {
// no base manage in sqLite ...
@@ -123,7 +121,7 @@ public class DataAccess {
return false;
}
}
-
+
public static boolean isTableExist(final String name) throws InternalServerErrorException {
try {
String request = "";
@@ -163,7 +161,7 @@ public class DataAccess {
}
throw new InternalServerErrorException("Can Not manage the DB-access");
}
-
+
/**
* extract a list of "-" separated element from a SQL input data.
* @param rs Result Set of the BDD
@@ -184,7 +182,7 @@ public class DataAccess {
}
return out;
}
-
+
protected static void setValuedb(final Class> type, final T data, int index, final Field field, final PreparedStatement ps) throws Exception {
if (type == Long.class) {
final Object tmp = field.get(data);
@@ -280,7 +278,7 @@ public class DataAccess {
throw new Exception("Unknown Field Type");
}
}
-
+
// TODO: maybe wrap this if the use of sqlite ==> maybe some problems came with sqlite ...
protected static void setValueFromDb(final Class> type, final T data, final int index, final Field field, final ResultSet rs) throws Exception {
if (type == Long.class) {
@@ -362,11 +360,22 @@ public class DataAccess {
field.set(data, tmp);
}
} else if (type == Date.class) {
- final Timestamp tmp = rs.getTimestamp(index);
- if (rs.wasNull()) {
- field.set(data, null);
- } else {
- field.set(data, Date.from(tmp.toInstant()));
+ try {
+ final Timestamp tmp = rs.getTimestamp(index);
+ if (rs.wasNull()) {
+ field.set(data, null);
+ } else {
+ field.set(data, Date.from(tmp.toInstant()));
+ }
+ } catch (final SQLException ex) {
+ final String tmp = rs.getString(index);
+ LOGGER.error("Fail to parse the SQL time !!! {}", tmp);
+ LOGGER.error("Fail to parse the SQL time !!! {}", Date.parse(tmp));
+ if (rs.wasNull()) {
+ field.set(data, null);
+ } else {
+ field.set(data, Date.parse(tmp));
+ }
}
} else if (type == LocalDate.class) {
final java.sql.Date tmp = rs.getDate(index);
@@ -407,7 +416,7 @@ public class DataAccess {
throw new Exception("Unknown Field Type");
}
}
-
+
public static boolean isAddOnField(final Field field) {
final boolean ret = AnnotationTools.isAnnotationGroup(field, DataAddOn.class);
if (ret) {
@@ -422,7 +431,7 @@ public class DataAccess {
}
return ret;
}
-
+
public static DataAccessAddOn findAddOnforField(final Field field) {
for (final DataAccessAddOn elem : addOn) {
if (elem.isCompatibleField(field)) {
@@ -433,19 +442,22 @@ public class DataAccess {
}
public static T insert(final T data) throws Exception {
+ return insert(data, null);
+ }
+
+ public static T insert(final T data, final QueryOptions options) throws Exception {
final Class> clazz = data.getClass();
- //public static NodeSmall createNode(String typeInNode, String name, String descrition, Long parentId) {
DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
// real add in the BDD:
try {
- final String tableName = AnnotationTools.getTableName(clazz);
+ final String tableName = AnnotationTools.getTableName(clazz, options);
//boolean createIfNotExist = clazz.getDeclaredAnnotationsByType(SQLIfNotExists.class).length != 0;
final StringBuilder querry = new StringBuilder();
querry.append("INSERT INTO `");
querry.append(tableName);
querry.append("` (");
-
+
boolean firstField = true;
int count = 0;
for (final Field elem : clazz.getFields()) {
@@ -457,7 +469,7 @@ public class DataAccess {
continue;
}
final DataAccessAddOn addOn = findAddOnforField(elem);
- if (addOn != null && addOn.isExternal()) {
+ if (addOn != null && !addOn.canInsert()) {
continue;
}
final boolean createTime = elem.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0;
@@ -496,7 +508,7 @@ public class DataAccess {
querry.append("?");
}
querry.append(")");
- //LOGGER.warn("generate the querry: '{}'", querry.toString());
+ LOGGER.warn("generate the querry: '{}'", querry.toString());
// prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(querry.toString(), Statement.RETURN_GENERATED_KEYS);
Field primaryKeyField = null;
@@ -511,7 +523,7 @@ public class DataAccess {
continue;
}
final DataAccessAddOn addOn = findAddOnforField(elem);
- if (addOn != null && addOn.isExternal()) {
+ if (addOn != null && !addOn.canInsert()) {
continue;
}
final boolean createTime = elem.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0;
@@ -575,16 +587,27 @@ public class DataAccess {
}
return data;
}
-
+
// seems a good idea, but very dangerous if we not filter input data... if set an id it can be complicated...
public static T insertWithJson(final Class clazz, final String jsonData) throws Exception {
final ObjectMapper mapper = new ObjectMapper();
// parse the object to be sure the data are valid:
final T data = mapper.readValue(jsonData, clazz);
-
+
return insert(data);
}
-
+
+ /**
+ * Update an object with the inserted json data
+ *
+ * @param Type of the object to insert
+ * @param Master key on the object manage with @Id
+ * @param clazz Class reference of the insertion model
+ * @param id Key to insert data
+ * @param jsonData Json data (partial) values to update
+ * @return the number of object updated
+ * @throws Exception
+ */
public static int updateWithJson(final Class clazz, final ID_TYPE id, final String jsonData) throws Exception {
// Find the ID field type ....
final Field idField = AnnotationTools.getIdField(clazz);
@@ -592,14 +615,14 @@ public class DataAccess {
throw new Exception("The class have no annotation @Id ==> can not determine the default type searching");
}
// check the compatibility of the id and the declared ID
- if (id instanceof idField.getType()) {
+ final Class> typeClass = idField.getType();
+ if (id == typeClass) {
throw new Exception("Request update with the wriong type ...");
}
-
// Udpade Json Value
- return updateWithJson(clazz, QueryCondition(AnnotationTools.getFieldName(idField), "=", id), jsonData);
+ return updateWithJson(clazz, new QueryCondition(AnnotationTools.getFieldName(idField), "=", id), jsonData);
}
-
+
public static int updateWithJson(final Class clazz, final QueryItem condition, final String jsonData) throws Exception {
final ObjectMapper mapper = new ObjectMapper();
// parse the object to be sure the data are valid:
@@ -607,17 +630,19 @@ public class DataAccess {
// Read the tree to filter injection of data:
final JsonNode root = mapper.readTree(jsonData);
final List keys = new ArrayList<>();
- final Iterator iterator = root.fieldNames();
+ final var iterator = root.fieldNames();
iterator.forEachRemaining(e -> keys.add(e));
- return update(data, id, keys);
- return 0;
+ return update(data, condition, keys);
+ }
+
+ public static int update(final T data, final ID_TYPE id) throws Exception {
+ return update(data, id, null);
}
public static int update(final T data, final QueryItem condition) throws Exception {
-
- return 0;
+ return update(data, condition, null);
}
-
+
/**
*
* @param
@@ -627,42 +652,50 @@ public class DataAccess {
* @return the affected rows.
* @throws Exception
*/
- public static int update(final T data, final long id, final List filterValue) throws Exception {
+ public static int update(final T data, final ID_TYPE id, final List filterValue) throws Exception {
+ // Find the ID field type ....
+ final Field idField = AnnotationTools.getIdField(data.getClass());
+ if (idField == null) {
+ throw new Exception("The class have no annotation @Id ==> can not determine the default type searching");
+ }
+ // check the compatibility of the id and the declared ID
+ final Class> typeClass = idField.getType();
+ if (id == typeClass) {
+ throw new Exception("Request update with the wriong type ...");
+ }
+ return update(data, new QueryCondition(AnnotationTools.getFieldName(idField), "=", id), filterValue);
+ }
+
+ public static int update(final T data, final QueryItem condition, final QueryOptions options, final List filterValue) throws Exception {
final Class> clazz = data.getClass();
//public static NodeSmall createNode(String typeInNode, String name, String description, Long parentId) {
-
+
DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
// real add in the BDD:
try {
- final String tableName = AnnotationTools.getTableName(clazz);
+ final String tableName = AnnotationTools.getTableName(clazz, options);
//boolean createIfNotExist = clazz.getDeclaredAnnotationsByType(SQLIfNotExists.class).length != 0;
final StringBuilder querry = new StringBuilder();
querry.append("UPDATE `");
querry.append(tableName);
querry.append("` SET ");
-
+
boolean firstField = true;
- Field primaryKeyField = null;
for (final Field elem : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(elem.getModifiers())) {
continue;
}
- if (AnnotationTools.isPrimaryKey(elem)) {
- primaryKeyField = elem;
+ final String name = AnnotationTools.getFieldName(elem);
+ if (filterValue != null) {
+ if (!filterValue.contains(name)) {
+ continue;
+ }
+ } else if (AnnotationTools.isGenericField(elem)) {
continue;
}
final DataAccessAddOn addOn = findAddOnforField(elem);
- if (addOn != null && addOn.isExternal()) {
- continue;
- }
- final boolean createTime = AnnotationTools.isCreatedAtField(elem);
- if (createTime) {
- continue;
- }
- final String name = AnnotationTools.getFieldName(elem);
- final boolean updateTime = AnnotationTools.isUpdateAtField(elem);
- if (!updateTime && !filterValue.contains(name)) {
+ if (addOn != null && !addOn.canUpdate()) {
continue;
}
if (!elem.getClass().isPrimitive()) {
@@ -678,19 +711,13 @@ public class DataAccess {
}
querry.append(" `");
querry.append(name);
- querry.append("` = ");
- if (updateTime) {
- querry.append(getDBNow());
- querry.append(" ");
- } else {
- querry.append("? ");
- }
+ querry.append("` = ? ");
}
- querry.append(" WHERE `");
- querry.append(AnnotationTools.getFieldName(primaryKeyField));
- querry.append("` = ?");
+ querry.append(" ");
+ final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
+ whereAppendQuery(querry, tableName, condition, null, deletedFieldName);
firstField = true;
- // logger.debug("generate the querry: '{}'", querry.toString());
+ LOGGER.debug("generate the querry: '{}'", querry.toString());
// prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(querry.toString(), Statement.RETURN_GENERATED_KEYS);
int iii = 1;
@@ -699,22 +726,18 @@ public class DataAccess {
if (java.lang.reflect.Modifier.isStatic(elem.getModifiers())) {
continue;
}
- if (AnnotationTools.isPrimaryKey(elem)) {
+ final String name = AnnotationTools.getFieldName(elem);
+ if (filterValue != null) {
+ if (!filterValue.contains(name)) {
+ continue;
+ }
+ } else if (AnnotationTools.isGenericField(elem)) {
continue;
}
final DataAccessAddOn addOn = findAddOnforField(elem);
if (addOn != null && !addOn.canUpdate()) {
continue;
}
- final boolean createTime = elem.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0;
- if (createTime) {
- continue;
- }
- final String name = AnnotationTools.getFieldName(elem);
- final boolean updateTime = elem.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0;
- if (updateTime || !filterValue.contains(name)) {
- continue;
- }
if (addOn == null) {
final Class> type = elem.getType();
if (!type.isPrimitive()) {
@@ -728,7 +751,8 @@ public class DataAccess {
iii = addOn.insertData(ps, data, iii);
}
}
- ps.setLong(iii++, id);
+ iii = whereInjectValue(ps, condition, iii);
+
return ps.executeUpdate();
} catch (final SQLException ex) {
ex.printStackTrace();
@@ -738,7 +762,7 @@ public class DataAccess {
}
return 0;
}
-
+
static void addElement(final PreparedStatement ps, final Object value, final int iii) throws Exception {
if (value instanceof final Long tmp) {
ps.setLong(iii, tmp);
@@ -772,7 +796,7 @@ public class DataAccess {
throw new Exception("Not manage type ==> need to add it ...");
}
}
-
+
public static void whereAppendQuery(final StringBuilder querry, final String tableName, final QueryItem condition, final QueryOptions options, final String deletedFieldName)
throws ExceptionDBInterface {
boolean exclude_deleted = true;
@@ -797,7 +821,7 @@ public class DataAccess {
}
querry.append(" WHERE (");
condition.generateQuerry(querry, tableName);
-
+
querry.append(") ");
if (exclude_deleted && deletedFieldName != null) {
querry.append("AND ");
@@ -807,40 +831,40 @@ public class DataAccess {
querry.append(" = false ");
}
}
-
- public static void whereInjectValue(final PreparedStatement ps, final QueryItem condition) throws Exception {
+
+ public static int whereInjectValue(final PreparedStatement ps, final QueryItem condition, int iii) throws Exception {
// Check if we have a condition to generate
if (condition == null) {
- return;
+ return iii;
}
- int iii = 1;
iii = condition.injectQuerry(ps, iii);
+ return iii;
}
-
+
public static int executeSimpleQuerry(final String querry, final boolean root) throws SQLException, IOException {
final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig, root);
final Statement stmt = entry.connection.createStatement();
return stmt.executeUpdate(querry);
}
-
+
public static int executeSimpleQuerry(final String querry) throws SQLException, IOException {
return executeSimpleQuerry(querry, false);
}
-
+
public static boolean executeQuerry(final String querry, final boolean root) throws SQLException, IOException {
final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig, root);
final Statement stmt = entry.connection.createStatement();
return stmt.execute(querry);
}
-
+
public static boolean executeQuerry(final String querry) throws SQLException, IOException {
return executeQuerry(querry, false);
}
-
+
public static T getWhere(final Class clazz, final QueryItem condition) throws Exception {
return getWhere(clazz, condition, null);
}
-
+
public static T getWhere(final Class clazz, final QueryItem condition, final QueryOptions options) throws Exception {
final List values = getsWhere(clazz, condition, options, 1);
if (values.size() == 0) {
@@ -848,23 +872,23 @@ public class DataAccess {
}
return values.get(0);
}
-
+
public static List getsWhere(final Class clazz, final QueryItem condition) throws Exception {
return getsWhere(clazz, condition, null, null, null);
}
-
+
public static List getsWhere(final Class clazz, final QueryItem condition, final QueryOptions options) throws Exception {
return getsWhere(clazz, condition, null, options, null);
}
-
+
public static List getsWhere(final Class clazz, final QueryItem condition, final QueryOptions options, final Integer linit) throws Exception {
return getsWhere(clazz, condition, null, options, linit);
}
-
- // TODO: set limit as an querry Option...
+
+ // TODO: set limit as an query Option...
@SuppressWarnings("unchecked")
public static List getsWhere(final Class clazz, final QueryItem condition, final String orderBy, final QueryOptions options, final Integer linit) throws Exception {
-
+
boolean readAllfields = false;
if (options != null) {
final Object data = options.get(QueryOptions.SQL_NOT_READ_DISABLE);
@@ -874,18 +898,18 @@ public class DataAccess {
LOGGER.error("'{}' ==> has not a boolean value: {}", QueryOptions.SQL_NOT_READ_DISABLE, data);
}
}
-
+
DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
final List outs = new ArrayList<>();
// real add in the BDD:
try {
- final String tableName = AnnotationTools.getTableName(clazz);
+ final String tableName = AnnotationTools.getTableName(clazz, options);
//boolean createIfNotExist = clazz.getDeclaredAnnotationsByType(SQLIfNotExists.class).length != 0;
final StringBuilder querry = new StringBuilder();
querry.append("SELECT ");
//querry.append(tableName);
//querry.append(" SET ");
-
+
boolean firstField = true;
int count = 0;
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
@@ -895,7 +919,7 @@ public class DataAccess {
continue;
}
final DataAccessAddOn addOn = findAddOnforField(elem);
- if (addOn != null && addOn.isExternal()) {
+ if (addOn != null && !addOn.canRetrieve(elem)) {
continue;
}
// TODO: Manage it with AddOn
@@ -936,7 +960,7 @@ public class DataAccess {
LOGGER.debug("generate the querry: '{}'", querry.toString());
// prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(querry.toString(), Statement.RETURN_GENERATED_KEYS);
- whereInjectValue(ps, condition);
+ whereInjectValue(ps, condition, 1);
// execute the request
final ResultSet rs = ps.executeQuery();
while (rs.next()) {
@@ -949,7 +973,7 @@ public class DataAccess {
continue;
}
final DataAccessAddOn addOn = findAddOnforField(elem);
- if (addOn != null && addOn.isExternal()) {
+ if (addOn != null && !addOn.canRetrieve(elem)) {
continue;
}
// TODO: Manage it with AddOn
@@ -968,7 +992,7 @@ public class DataAccess {
final T out = (T) data;
outs.add(out);
}
-
+
} catch (final SQLException ex) {
ex.printStackTrace();
throw ex;
@@ -980,12 +1004,12 @@ public class DataAccess {
}
return outs;
}
-
+
// TODO : detect the @Id
public static T get(final Class clazz, final long id) throws Exception {
return get(clazz, id, null);
}
-
+
public static T get(final Class clazz, final long id, final QueryOptions options) throws Exception {
Field primaryKeyField = null;
for (final Field elem : clazz.getFields()) {
@@ -1002,44 +1026,48 @@ public class DataAccess {
}
throw new Exception("Missing primary Key...");
}
-
+
public static String getCurrentTimeStamp() {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
}
-
+
public static List gets(final Class clazz) throws Exception {
return getsWhere(clazz, null);
}
-
+
public static List gets(final Class clazz, final QueryOptions options) throws Exception {
return getsWhere(clazz, null, options);
}
-
+
// TODO : detect the @Id
public static int delete(final Class> clazz, final long id) throws Exception {
+ return delete(clazz, id, null);
+ }
+
+ public static int delete(final Class> clazz, final long id, final QueryOptions options) throws Exception {
final String hasDeletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
if (hasDeletedFieldName != null) {
- return deleteSoft(clazz, id);
+ return deleteSoft(clazz, id, options);
} else {
- return deleteHard(clazz, id);
+ return deleteHard(clazz, id, options);
}
}
-
- public static int deleteWhere(final Class> clazz, final QueryItem condition) throws Exception {
+
+ public static int deleteWhere(final Class> clazz, final QueryItem condition, final QueryOptions options) throws Exception {
final String hasDeletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
if (hasDeletedFieldName != null) {
- return deleteSoftWhere(clazz, condition);
+ return deleteSoftWhere(clazz, condition, options);
} else {
- return deleteHardWhere(clazz, condition);
+ return deleteHardWhere(clazz, condition, options);
}
}
-
- public static int deleteHard(final Class> clazz, final long id) throws Exception {
- return deleteHardWhere(clazz, new QueryCondition("id", "=", id));
+
+ public static int deleteHard(final Class> clazz, final long id, final QueryOptions options) throws Exception {
+ return deleteHardWhere(clazz, new QueryCondition("id", "=", id), options);
}
-
- public static int deleteHardWhere(final Class> clazz, final QueryItem condition) throws Exception {
- final String tableName = AnnotationTools.getTableName(clazz);
+
+ public static int deleteHardWhere(final Class> clazz, final QueryItem condition, final QueryOptions options) throws Exception {
+ final String tableName = AnnotationTools.getTableName(clazz, options);
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
// find the deleted field
@@ -1052,27 +1080,27 @@ public class DataAccess {
try {
LOGGER.debug("APPLY: {}", querry.toString());
final PreparedStatement ps = entry.connection.prepareStatement(querry.toString());
- whereInjectValue(ps, condition);
+ whereInjectValue(ps, condition, 1);
return ps.executeUpdate();
} finally {
entry.close();
entry = null;
}
}
-
- private static int deleteSoft(final Class> clazz, final long id) throws Exception {
- return deleteSoftWhere(clazz, new QueryCondition("id", "=", id));
+
+ private static int deleteSoft(final Class> clazz, final long id, final QueryOptions options) throws Exception {
+ return deleteSoftWhere(clazz, new QueryCondition("id", "=", id), options);
}
-
+
public static String getDBNow() {
if (!"sqlite".equals(ConfigBaseVariable.getDBType())) {
return "now(3)";
}
return "DATE()";
}
-
- public static int deleteSoftWhere(final Class> clazz, final QueryItem condition) throws Exception {
- final String tableName = AnnotationTools.getTableName(clazz);
+
+ public static int deleteSoftWhere(final Class> clazz, final QueryItem condition, final QueryOptions options) throws Exception {
+ final String tableName = AnnotationTools.getTableName(clazz, options);
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
/*
String updateFieldName = null;
@@ -1081,7 +1109,7 @@ public class DataAccess {
}
*/
// find the deleted field
-
+
DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
final StringBuilder querry = new StringBuilder();
querry.append("UPDATE `");
@@ -1102,7 +1130,7 @@ public class DataAccess {
try {
LOGGER.debug("APPLY UPDATE: {}", querry.toString());
final PreparedStatement ps = entry.connection.prepareStatement(querry.toString());
- whereInjectValue(ps, condition);
+ whereInjectValue(ps, condition, 1);
return ps.executeUpdate();
} finally {
entry.close();
@@ -1111,11 +1139,15 @@ public class DataAccess {
}
public static int unsetDelete(final Class> clazz, final long id) throws Exception {
- return unsetDeleteWhere(clazz, new QueryCondition("id", "=", id));
+ return unsetDeleteWhere(clazz, new QueryCondition("id", "=", id), null);
}
- public static int unsetDeleteWhere(final Class> clazz, final QueryItem condition) throws Exception {
- final String tableName = AnnotationTools.getTableName(clazz);
+ public static int unsetDelete(final Class> clazz, final long id, final QueryOptions options) throws Exception {
+ return unsetDeleteWhere(clazz, new QueryCondition("id", "=", id), options);
+ }
+
+ public static int unsetDeleteWhere(final Class> clazz, final QueryItem condition, final QueryOptions options) throws Exception {
+ final String tableName = AnnotationTools.getTableName(clazz, options);
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
if (deletedFieldName == null) {
throw new Exception("The class " + clazz.getCanonicalName() + " has no deleted field");
@@ -1134,16 +1166,16 @@ public class DataAccess {
querry.append(", ");
*/
// need to disable the deleted false because the model must be unselected to be updated.
- final QueryOptions options = new QueryOptions(QueryOptions.SQL_DELETED_DISABLE, true);
+ options.put(QueryOptions.SQL_DELETED_DISABLE, true);
whereAppendQuery(querry, tableName, condition, options, deletedFieldName);
try {
final PreparedStatement ps = entry.connection.prepareStatement(querry.toString());
- whereInjectValue(ps, condition);
+ whereInjectValue(ps, condition, 1);
return ps.executeUpdate();
} finally {
entry.close();
entry = null;
}
}
-
+
}
\ No newline at end of file
diff --git a/src/org/kar/archidata/dataAccess/DataAccessAddOn.java b/src/org/kar/archidata/dataAccess/DataAccessAddOn.java
index 0ec2600..2c95a1a 100644
--- a/src/org/kar/archidata/dataAccess/DataAccessAddOn.java
+++ b/src/org/kar/archidata/dataAccess/DataAccessAddOn.java
@@ -39,8 +39,11 @@ public interface DataAccessAddOn {
*/
int insertData(PreparedStatement ps, Object data, int iii) throws SQLException;
- // External mean that the type of the object is absolutely not obvious...
- boolean isExternal();
+ // Element can insert in the single request
+ boolean canInsert();
+
+ // Element can be retrieve with the specific mode
+ boolean canRetrieve(final Field field);
int generateQuerry(@NotNull String tableName, @NotNull Field elem, @NotNull StringBuilder querry, @NotNull String name, @NotNull int elemCount, QueryOptions options);
diff --git a/src/org/kar/archidata/dataAccess/DataFactory.java b/src/org/kar/archidata/dataAccess/DataFactory.java
index 10a2be7..c4160a6 100644
--- a/src/org/kar/archidata/dataAccess/DataFactory.java
+++ b/src/org/kar/archidata/dataAccess/DataFactory.java
@@ -20,7 +20,7 @@ import jakarta.persistence.GenerationType;
public class DataFactory {
static final Logger LOGGER = LoggerFactory.getLogger(DataFactory.class);
-
+
public static String convertTypeInSQL(final Class> type, final String fieldName) throws Exception {
if (!"sqlite".equals(ConfigBaseVariable.getDBType())) {
if (type == Long.class || type == long.class) {
@@ -117,21 +117,21 @@ public class DataFactory {
}
throw new Exception("Imcompatible type of element in object for: " + type.getCanonicalName());
}
-
+
public static void createTablesSpecificType(final String tableName, final Field elem, final StringBuilder mainTableBuilder, final List preOtherTables, final List postOtherTables,
final boolean createIfNotExist, final boolean createDrop, final int fieldId, final Class> classModel) throws Exception {
final String name = AnnotationTools.getFieldName(elem);
final Integer limitSize = AnnotationTools.getLimitSize(elem);
final boolean notNull = AnnotationTools.getNotNull(elem);
-
+
final boolean primaryKey = AnnotationTools.isPrimaryKey(elem);
final GenerationType strategy = AnnotationTools.getStrategy(elem);
-
+
final boolean createTime = elem.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0;
final boolean updateTime = elem.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0;
final String comment = AnnotationTools.getComment(elem);
final String defaultValue = AnnotationTools.getDefault(elem);
-
+
if (fieldId == 0) {
mainTableBuilder.append("\n\t\t`");
} else {
@@ -191,10 +191,10 @@ public class DataFactory {
triggerBuilder.append(name);
triggerBuilder.append(" = datetime('now') WHERE id = NEW.id; \n");
triggerBuilder.append("END;");
-
+
postOtherTables.add(triggerBuilder.toString());
}
-
+
mainTableBuilder.append(" ");
}
} else {
@@ -229,11 +229,11 @@ public class DataFactory {
mainTableBuilder.append("DEFAULT ");
mainTableBuilder.append(defaultValue);
mainTableBuilder.append(" ");
-
+
}
if (primaryKey && "sqlite".equals(ConfigBaseVariable.getDBType())) {
mainTableBuilder.append("PRIMARY KEY ");
-
+
}
if (strategy == GenerationType.IDENTITY) {
if (!"sqlite".equals(ConfigBaseVariable.getDBType())) {
@@ -244,14 +244,14 @@ public class DataFactory {
} else if (strategy != null) {
throw new Exception("Can not generate a stategy different of IDENTITY");
}
-
+
if (comment != null && !"sqlite".equals(ConfigBaseVariable.getDBType())) {
mainTableBuilder.append("COMMENT '");
mainTableBuilder.append(comment.replace('\'', '\''));
mainTableBuilder.append("' ");
}
}
-
+
private static boolean isFieldFromSuperClass(final Class> model, final String filedName) {
final Class> superClass = model.getSuperclass();
if (superClass == null) {
@@ -271,13 +271,24 @@ public class DataFactory {
}
return false;
}
-
+
public static List createTable(final Class> clazz) throws Exception {
- return createTable(clazz, true);
+ return createTable(clazz, null);
}
-
- public static List createTable(final Class> clazz, final boolean createDrop) throws Exception {
- final String tableName = AnnotationTools.getTableName(clazz);
+
+ public static List createTable(final Class> clazz, final QueryOptions options) throws Exception {
+ final String tableName = AnnotationTools.getTableName(clazz, options);
+
+ boolean createDrop = false;
+ if (options != null) {
+ final Object data = options.get(QueryOptions.CREATE_DROP_TABLE);
+ if (data instanceof final Boolean optionBoolean) {
+ createDrop = optionBoolean;
+ } else if (data != null) {
+ LOGGER.error("'{}' ==> has not a Boolean value: {}", QueryOptions.CREATE_DROP_TABLE, data);
+ }
+ }
+
final boolean createIfNotExist = clazz.getDeclaredAnnotationsByType(SQLIfNotExists.class).length != 0;
final List preActionList = new ArrayList<>();
final List postActionList = new ArrayList<>();
@@ -297,7 +308,7 @@ public class DataFactory {
int fieldId = 0;
LOGGER.debug("===> TABLE `{}`", tableName);
final List primaryKeys = new ArrayList<>();
-
+
for (final Field elem : clazz.getFields()) {
// DEtect the primary key (support only one primary key right now...
if (AnnotationTools.isPrimaryKey(elem)) {
@@ -311,7 +322,7 @@ public class DataFactory {
Class> currentClazz = clazz;
while (currentClazz != null) {
fieldId = 0;
- LOGGER.info("parse class: '{}'", currentClazz.getCanonicalName());
+ LOGGER.trace("parse class: '{}'", currentClazz.getCanonicalName());
for (final Field elem : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(elem.getModifiers())) {
@@ -327,10 +338,10 @@ public class DataFactory {
continue;
}
alreadyAdded.add(dataName);
- LOGGER.info(" + '{}'", elem.getName());
+ LOGGER.trace(" + '{}'", elem.getName());
if (DataAccess.isAddOnField(elem)) {
final DataAccessAddOn addOn = DataAccess.findAddOnforField(elem);
- LOGGER.info("Create type for: {} ==> {} (ADD-ON)", AnnotationTools.getFieldName(elem), elem.getType());
+ LOGGER.trace("Create type for: {} ==> {} (ADD-ON)", AnnotationTools.getFieldName(elem), elem.getType());
if (addOn != null) {
addOn.createTables(tableName, elem, tmpOut, preActionList, postActionList, createIfNotExist, createDrop, fieldId);
} else {
@@ -338,7 +349,7 @@ public class DataFactory {
"Element matked as add-on but add-on does not loaded: table:" + tableName + " field name=" + AnnotationTools.getFieldName(elem) + " type=" + elem.getType());
}
} else {
- LOGGER.info("Create type for: {} ==> {}", AnnotationTools.getFieldName(elem), elem.getType());
+ LOGGER.trace("Create type for: {} ==> {}", AnnotationTools.getFieldName(elem), elem.getType());
DataFactory.createTablesSpecificType(tableName, elem, tmpOut, preActionList, postActionList, createIfNotExist, createDrop, fieldId, elem.getType());
}
fieldId++;
@@ -378,5 +389,5 @@ public class DataFactory {
preActionList.addAll(postActionList);
return preActionList;
}
-
+
}
\ No newline at end of file
diff --git a/src/org/kar/archidata/dataAccess/QueryOptions.java b/src/org/kar/archidata/dataAccess/QueryOptions.java
index e0e27ef..3370439 100644
--- a/src/org/kar/archidata/dataAccess/QueryOptions.java
+++ b/src/org/kar/archidata/dataAccess/QueryOptions.java
@@ -6,6 +6,8 @@ import java.util.Map;
public class QueryOptions {
public static final String SQL_NOT_READ_DISABLE = "SQLNotRead_disable";
public static final String SQL_DELETED_DISABLE = "SQLDeleted_disable";
+ public static final String OVERRIDE_TABLE_NAME = "SQL_OVERRIDE_TABLE_NAME";
+ public static final String CREATE_DROP_TABLE = "CREATE_DROP_TABLE";
private final Map options = new HashMap<>();
@@ -13,26 +15,26 @@ public class QueryOptions {
}
- public QueryOptions(String key, Object value) {
+ public QueryOptions(final String key, final Object value) {
this.options.put(key, value);
}
- public QueryOptions(String key, Object value, String key2, Object value2) {
+ public QueryOptions(final String key, final Object value, final String key2, final Object value2) {
this.options.put(key, value);
this.options.put(key2, value2);
}
- public QueryOptions(String key, Object value, String key2, Object value2, String key3, Object value3) {
+ public QueryOptions(final String key, final Object value, final String key2, final Object value2, final String key3, final Object value3) {
this.options.put(key, value);
this.options.put(key2, value2);
this.options.put(key3, value3);
}
- public void put(String key, Object value) {
+ public void put(final String key, final Object value) {
this.options.put(key, value);
}
- public Object get(String value) {
+ public Object get(final String value) {
return this.options.get(value);
}
diff --git a/src/org/kar/archidata/dataAccess/addOn/AddOnManyToMany.java b/src/org/kar/archidata/dataAccess/addOn/AddOnManyToMany.java
index 6b682e1..6d5df0f 100644
--- a/src/org/kar/archidata/dataAccess/addOn/AddOnManyToMany.java
+++ b/src/org/kar/archidata/dataAccess/addOn/AddOnManyToMany.java
@@ -4,16 +4,16 @@ import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
-import java.sql.Statement;
import java.util.List;
-import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.annotation.AnnotationTools;
-import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataAccessAddOn;
-import org.kar.archidata.dataAccess.DataAccess.ExceptionDBInterface;
-import org.kar.archidata.db.DBEntry;
+import org.kar.archidata.dataAccess.DataFactory;
+import org.kar.archidata.dataAccess.QueryAnd;
+import org.kar.archidata.dataAccess.QueryCondition;
+import org.kar.archidata.dataAccess.QueryOptions;
+import org.kar.archidata.dataAccess.addOn.model.LinkTable;
import org.kar.archidata.util.ConfigBaseVariable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -24,47 +24,60 @@ import jakarta.validation.constraints.NotNull;
public class AddOnManyToMany implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToMany.class);
static final String SEPARATOR = "-";
-
+
@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 int insertData(final PreparedStatement ps, final Object data, int iii) throws SQLException {
+ public int insertData(final PreparedStatement ps, final Object data, final int iii) throws SQLException {
return iii;
}
-
+
@Override
- public boolean isExternal() {
- // TODO Auto-generated method stub
+ public boolean canInsert() {
return false;
}
-
+
@Override
- public int generateQuerry(@NotNull final String tableName, @NotNull final Field elem, @NotNull final StringBuilder querry, @NotNull final String name, @NotNull final int elemCount,
- QueryOptions options) {
+ public boolean canRetrieve(final Field field) {
+ return true;
+ }
+
+ 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;
+ }
+
+ @Override
+ public int generateQuerry(@NotNull final String tableName, @NotNull final Field elem, @NotNull final StringBuilder querry, @NotNull final String name, @NotNull final int elemCount,
+ final QueryOptions options) {
+ final String linkTableName = generateLinkTableName(tableName, name);
+
final String tmpVariable = "tmp_" + Integer.toString(elemCount);
querry.append(" (SELECT GROUP_CONCAT(");
querry.append(tmpVariable);
- querry.append(".");
- querry.append(localName);
- querry.append("_id ");
+ querry.append(".object2Id");
if (ConfigBaseVariable.getDBType().equals("sqlite")) {
querry.append(", ");
} else {
@@ -73,9 +86,7 @@ public class AddOnManyToMany implements DataAccessAddOn {
querry.append("'");
querry.append(SEPARATOR);
querry.append("') FROM ");
- querry.append(tableName);
- querry.append("_link_");
- querry.append(localName);
+ querry.append(linkTableName);
querry.append(" ");
querry.append(tmpVariable);
querry.append(" WHERE ");
@@ -85,12 +96,13 @@ public class AddOnManyToMany implements DataAccessAddOn {
querry.append(".id = ");
querry.append(tmpVariable);
querry.append(".");
- querry.append(tableName);
- querry.append("_id GROUP BY ");
- querry.append(tmpVariable);
- querry.append(".");
- querry.append(tableName);
- querry.append("_id ) AS ");
+ querry.append("object1Id ");
+ if (!ConfigBaseVariable.getDBType().equals("sqlite")) {
+ querry.append(" GROUP BY ");
+ querry.append(tmpVariable);
+ querry.append(".object2Id");
+ }
+ querry.append(") AS ");
querry.append(name);
querry.append(" ");
/*
@@ -102,135 +114,43 @@ public class AddOnManyToMany implements DataAccessAddOn {
*/
return 1;
}
-
+
@Override
- public int fillFromQuerry(final ResultSet rs, final Field elem, final Object data, final int count, QueryOptions options) throws SQLException, IllegalArgumentException, IllegalAccessException {
- List idList = DataAccess.getListOfIds(rs, count, SEPARATOR);
+ public int fillFromQuerry(final ResultSet rs, final Field elem, final Object data, final int count, final QueryOptions options)
+ throws SQLException, IllegalArgumentException, IllegalAccessException {
+ final List idList = DataAccess.getListOfIds(rs, count, SEPARATOR);
elem.set(data, idList);
return 1;
}
-
+
@Override
public boolean canUpdate() {
return false;
}
-
- public static void addLink(final Class> clazz, final long localKey, final String table, final long remoteKey) throws Exception {
+
+ public static void addLink(final Class> clazz, final long localKey, final String column, final long remoteKey) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
- DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
- long uniqueSQLID = -1;
- // real add in the BDD:
- try {
- // prepare the request:
- final String querry = "INSERT INTO " + tableName + "_link_" + table + " (create_date, modify_date, " + tableName + "_id, " + table + "_id)" + " VALUES (" + DataAccess.getDBNow() + ", "
- + DataAccess.getDBNow() + ", ?, ?)";
- final PreparedStatement ps = entry.connection.prepareStatement(querry, Statement.RETURN_GENERATED_KEYS);
- int iii = 1;
- ps.setLong(iii++, localKey);
- ps.setLong(iii++, remoteKey);
- // execute the request
- final int affectedRows = ps.executeUpdate();
- if (affectedRows == 0) {
- throw new SQLException("Creating data failed, no rows affected.");
- }
- // retrieve uid inserted
- try (ResultSet generatedKeys = ps.getGeneratedKeys()) {
- if (generatedKeys.next()) {
- uniqueSQLID = generatedKeys.getLong(1);
- } else {
- throw new SQLException("Creating user failed, no ID obtained (1).");
- }
- } catch (final Exception ex) {
- LOGGER.debug("Can not get the UID key inserted ... ");
- ex.printStackTrace();
- throw new SQLException("Creating user failed, no ID obtained (2).");
- }
- } catch (final SQLException ex) {
- ex.printStackTrace();
- throw new ExceptionDBInterface(500, "SQL error: " + ex.getMessage());
- } finally {
- entry.close();
- entry = null;
- }
+ final String linkTableName = generateLinkTableName(tableName, column);
+ final LinkTable insertElement = new LinkTable(localKey, remoteKey);
+ final QueryOptions options = new QueryOptions(QueryOptions.OVERRIDE_TABLE_NAME, linkTableName);
+ DataAccess.insert(insertElement, options);
+
}
-
- public static void removeLink(final Class> clazz, final long localKey, final String table, final long remoteKey) throws Exception {
+
+ public static int removeLink(final Class> clazz, final long localKey, final String column, final long remoteKey) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
- DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
- final String querry = "UPDATE `" + tableName + "_link_" + table + "` SET `modify_date`=" + DataAccess.getDBNow() + ", `deleted`=true WHERE `" + tableName + "_id` = ? AND `" + table
- + "_id` = ?";
- try {
- final PreparedStatement ps = entry.connection.prepareStatement(querry);
- int iii = 1;
- ps.setLong(iii++, localKey);
- ps.setLong(iii++, remoteKey);
- ps.executeUpdate();
- } catch (final SQLException ex) {
- ex.printStackTrace();
- throw new ExceptionDBInterface(500, "SQL error: " + ex.getMessage());
- } finally {
- entry.close();
- entry = null;
- }
+ final String linkTableName = generateLinkTableName(tableName, column);
+ final QueryOptions options = new QueryOptions(QueryOptions.OVERRIDE_TABLE_NAME, linkTableName);
+ final QueryAnd condition = new QueryAnd(new QueryCondition("object1Id", "=", localKey), new QueryCondition("object2Id", "=", remoteKey));
+ return DataAccess.deleteWhere(LinkTable.class, condition, options);
}
-
- // TODO : refacto this table to manage a generic table with dynamic name to be serializable with the default system
+
@Override
- public void createTables(final String tableName, final Field elem, final StringBuilder mainTableBuilder, final List preActionList, List postActionList,
+ public void createTables(final String tableName, final Field elem, final StringBuilder mainTableBuilder, final List preActionList, final List postActionList,
final boolean createIfNotExist, final boolean createDrop, final int fieldId) throws Exception {
- final String name = AnnotationTools.getFieldName(elem);
- String localName = name;
- if (name.endsWith("s")) {
- localName = name.substring(0, name.length() - 1);
- }
- if (createIfNotExist && createDrop) {
- final StringBuilder tableTmp = new StringBuilder();
- tableTmp.append("DROP TABLE IF EXISTS `");
- tableTmp.append(tableName);
- tableTmp.append("_link_");
- tableTmp.append(localName);
- tableTmp.append("`;");
- postActionList.add(tableTmp.toString());
- }
- final StringBuilder otherTable = new StringBuilder();
- otherTable.append("CREATE TABLE `");
- otherTable.append(tableName);
- otherTable.append("_link_");
- otherTable.append(localName);
- otherTable.append("`(\n");
- if (!ConfigBaseVariable.getDBType().equals("sqlite")) {
- otherTable.append("\t\t`id` bigint NOT NULL AUTO_INCREMENT,\n");
- otherTable.append("\t\t`deleted` tinyint(1) NOT NULL DEFAULT '0',\n");
- otherTable.append("\t\t`createdAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),\n");
- otherTable.append("\t\t`updatedAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),\n");
- } else {
- otherTable.append("\t\t`id` INTEGER PRIMARY KEY AUTOINCREMENT,\n");
- otherTable.append("\t\t`deleted` INTEGER NOT NULL DEFAULT '0',\n");
- otherTable.append("\t\t`createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP,\n");
- otherTable.append("\t\t`updatedAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP,\n");
- }
- otherTable.append("\t\t`");
- otherTable.append(tableName);
- if (!ConfigBaseVariable.getDBType().equals("sqlite")) {
- otherTable.append("_id` bigint NOT NULL,\n");
- } else {
- otherTable.append("_id` INTEGER NOT NULL,\n");
- }
- otherTable.append("\t\t`");
- otherTable.append(localName);
- if (!ConfigBaseVariable.getDBType().equals("sqlite")) {
- otherTable.append("_id` bigint NOT NULL\n");
- } else {
- otherTable.append("_id` INTEGER NOT NULL\n");
- }
- if (!ConfigBaseVariable.getDBType().equals("sqlite")) {
- otherTable.append("\t, PRIMARY KEY (`id`)\n");
- }
- otherTable.append("\t)");
- if (!ConfigBaseVariable.getDBType().equals("sqlite")) {
- otherTable.append(" ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n\n");
- }
- otherTable.append(";");
- postActionList.add(otherTable.toString());
+ final String linkTableName = generateLinkTableNameField(tableName, elem);
+ final QueryOptions options = new QueryOptions(QueryOptions.OVERRIDE_TABLE_NAME, linkTableName);
+ final List sqlCommand = DataFactory.createTable(LinkTable.class, options);
+ postActionList.addAll(sqlCommand);
}
}
diff --git a/src/org/kar/archidata/dataAccess/addOn/AddOnManyToManyOrdered.java b/src/org/kar/archidata/dataAccess/addOn/AddOnManyToManyOrdered.java
index 54b619a..ac69fa3 100644
--- a/src/org/kar/archidata/dataAccess/addOn/AddOnManyToManyOrdered.java
+++ b/src/org/kar/archidata/dataAccess/addOn/AddOnManyToManyOrdered.java
@@ -10,10 +10,10 @@ import java.util.List;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.addOn.DataAddOnManyToManyOrdered;
-import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.DataAccess;
-import org.kar.archidata.dataAccess.DataAccessAddOn;
import org.kar.archidata.dataAccess.DataAccess.ExceptionDBInterface;
+import org.kar.archidata.dataAccess.DataAccessAddOn;
+import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.db.DBEntry;
import org.kar.archidata.util.ConfigBaseVariable;
import org.slf4j.Logger;
@@ -46,18 +46,23 @@ public class AddOnManyToManyOrdered implements DataAccessAddOn {
}
@Override
- public int insertData(final PreparedStatement ps, final Object data, int iii) throws SQLException {
+ public int insertData(final PreparedStatement ps, final Object data, final int iii) throws SQLException {
return iii;
}
-
+
@Override
- public boolean isExternal() {
- // TODO Auto-generated method stub
+ public boolean canInsert() {
return false;
}
@Override
- public int generateQuerry(@NotNull String tableName, @NotNull Field elem, @NotNull StringBuilder querry, @NotNull String name, @NotNull int elemCount, QueryOptions options) {
+ public boolean canRetrieve(final Field field) {
+ return false;
+ }
+
+ @Override
+ public int generateQuerry(@NotNull final String tableName, @NotNull final Field elem, @NotNull final StringBuilder querry, @NotNull final String name, @NotNull final int elemCount,
+ final QueryOptions options) {
String localName = name;
if (name.endsWith("s")) {
localName = name.substring(0, name.length() - 1);
@@ -103,7 +108,8 @@ public class AddOnManyToManyOrdered implements DataAccessAddOn {
}
@Override
- public int fillFromQuerry(final ResultSet rs, final Field elem, final Object data, final int count, QueryOptions options) throws SQLException, IllegalArgumentException, IllegalAccessException {
+ public int fillFromQuerry(final ResultSet rs, final Field elem, final Object data, final int count, final QueryOptions options)
+ throws SQLException, IllegalArgumentException, IllegalAccessException {
//throw new IllegalAccessException("This Add-on has not the capability to insert data directly in DB");
return 0;
}
@@ -174,7 +180,7 @@ public class AddOnManyToManyOrdered implements DataAccessAddOn {
// TODO : refacto this table to manage a generic table with dynamic name to be serializable with the default system
@Override
- public void createTables(final String tableName, final Field elem, final StringBuilder mainTableBuilder, final List preActionList, List postActionList,
+ public void createTables(final String tableName, final Field elem, final StringBuilder mainTableBuilder, final List preActionList, final List postActionList,
final boolean createIfNotExist, final boolean createDrop, final int fieldId) throws Exception {
final String name = AnnotationTools.getFieldName(elem);
String localName = name;
diff --git a/src/org/kar/archidata/dataAccess/addOn/AddOnManyToOne.java b/src/org/kar/archidata/dataAccess/addOn/AddOnManyToOne.java
index 848a095..295dfd5 100644
--- a/src/org/kar/archidata/dataAccess/addOn/AddOnManyToOne.java
+++ b/src/org/kar/archidata/dataAccess/addOn/AddOnManyToOne.java
@@ -89,8 +89,12 @@ public class AddOnManyToOne implements DataAccessAddOn {
}
@Override
- public boolean isExternal() {
- // TODO Auto-generated method stub
+ public boolean canInsert() {
+ return false;
+ }
+
+ @Override
+ public boolean canRetrieve(final Field field) {
return false;
}
diff --git a/src/org/kar/archidata/dataAccess/addOn/AddOnSQLTableExternalForeinKeyAsList.java b/src/org/kar/archidata/dataAccess/addOn/AddOnSQLTableExternalForeinKeyAsList.java
index a1ce9be..ed96168 100644
--- a/src/org/kar/archidata/dataAccess/addOn/AddOnSQLTableExternalForeinKeyAsList.java
+++ b/src/org/kar/archidata/dataAccess/addOn/AddOnSQLTableExternalForeinKeyAsList.java
@@ -70,8 +70,12 @@ public class AddOnSQLTableExternalForeinKeyAsList implements DataAccessAddOn {
}
@Override
- public boolean isExternal() {
- // TODO Auto-generated method stub
+ public boolean canInsert() {
+ return false;
+ }
+
+ @Override
+ public boolean canRetrieve(final Field field) {
return false;
}
diff --git a/src/org/kar/archidata/dataAccess/addOn/model/LinkTable.java b/src/org/kar/archidata/dataAccess/addOn/model/LinkTable.java
new file mode 100644
index 0000000..546988f
--- /dev/null
+++ b/src/org/kar/archidata/dataAccess/addOn/model/LinkTable.java
@@ -0,0 +1,25 @@
+package org.kar.archidata.dataAccess.addOn.model;
+
+import org.kar.archidata.annotation.SQLComment;
+import org.kar.archidata.model.GenericDataSoftDelete;
+
+import jakarta.persistence.Column;
+
+public class LinkTable extends GenericDataSoftDelete {
+ public LinkTable() {
+ // nothing to do...
+ }
+
+ public LinkTable(final long object1Id, final long object2Id) {
+ this.object1Id = object1Id;
+ this.object2Id = object2Id;
+ }
+
+ @SQLComment("Object reference 1")
+ @Column(nullable = false)
+ public Long object1Id;
+ @SQLComment("Object reference 2")
+ @Column(nullable = false)
+ public Long object2Id;
+
+}
diff --git a/src/org/kar/archidata/migration/MigrationEngine.java b/src/org/kar/archidata/migration/MigrationEngine.java
index 5a9f638..0c60b81 100644
--- a/src/org/kar/archidata/migration/MigrationEngine.java
+++ b/src/org/kar/archidata/migration/MigrationEngine.java
@@ -10,24 +10,25 @@ import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.db.DBConfig;
import org.kar.archidata.db.DBEntry;
+import org.kar.archidata.migration.model.Migration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MigrationEngine {
final static Logger LOGGER = LoggerFactory.getLogger(MigrationEngine.class);
-
+
// List of order migrations
private final List datas;
// initialization of the migration if the DB is not present...
private MigrationInterface init;
-
+
/**
* Migration engine constructor (empty).
*/
public MigrationEngine() {
this(new ArrayList<>(), null);
}
-
+
/**
* Migration engine constructor (specific mode).
* @param datas All the migration ordered.
@@ -37,7 +38,7 @@ public class MigrationEngine {
this.datas = datas;
this.init = init;
}
-
+
/**
* Add a Migration in the list
* @param migration Migration to add.
@@ -45,7 +46,7 @@ public class MigrationEngine {
public void add(final MigrationInterface migration) {
this.datas.add(migration);
}
-
+
/**
* Set first initialization class
* @param migration migration class for first init.
@@ -53,17 +54,17 @@ public class MigrationEngine {
public void setInit(final MigrationInterface migration) {
this.init = migration;
}
-
+
/**
* Get the current version/migration name
* @return Model represent the last migration. If null then no migration has been done.
*/
- public MigrationModel getCurrentVersion() {
+ public Migration getCurrentVersion() {
if (!DataAccess.isTableExist("KAR_migration")) {
return null;
}
try {
- final List data = DataAccess.gets(MigrationModel.class, new QueryOptions("SQLNotRead_disable", true));
+ final List data = DataAccess.gets(Migration.class, new QueryOptions("SQLNotRead_disable", true));
if (data == null) {
LOGGER.error("Can not collect the migration table in the DB:{}");
return null;
@@ -73,7 +74,7 @@ public class MigrationEngine {
return null;
}
LOGGER.debug("List of migrations:");
- for (final MigrationModel elem : data) {
+ for (final Migration elem : data) {
LOGGER.debug(" - date={} name={} end={}", elem.updatedAt, elem.name, elem.terminated);
}
return data.get(data.size() - 1);
@@ -83,7 +84,7 @@ public class MigrationEngine {
}
return null;
}
-
+
/**
* Process the automatic migration of the system
* @param config SQL connection for the migration
@@ -92,7 +93,7 @@ public class MigrationEngine {
*/
public void migrate(final DBConfig config) throws InterruptedException, IOException {
LOGGER.info("Execute migration ... [BEGIN]");
-
+
// STEP 1: Check the DB exist:
LOGGER.info("Verify existance of '{}'", config.getDbName());
boolean exist = DataAccess.isDBExist(config.getDbName());
@@ -117,7 +118,7 @@ public class MigrationEngine {
// create the table:
List sqlQuery;
try {
- sqlQuery = DataFactory.createTable(MigrationModel.class, false);
+ sqlQuery = DataFactory.createTable(Migration.class);
} catch (final Exception ex) {
ex.printStackTrace();
while (true) {
@@ -136,7 +137,7 @@ public class MigrationEngine {
}
}
}
- final MigrationModel currentVersion = getCurrentVersion();
+ final Migration currentVersion = getCurrentVersion();
List toApply = new ArrayList<>();
if (currentVersion == null) {
//This is a first migration
@@ -151,7 +152,8 @@ public class MigrationEngine {
} else {
// we insert a placeholder to simulate all migration is well done.
final String placeholderName = this.datas.get(this.datas.size() - 1).getName();
- MigrationModel migrationResult = new MigrationModel();
+ Migration migrationResult = new Migration();
+ migrationResult.id = 1000L;
migrationResult.name = placeholderName;
migrationResult.stepId = 0;
migrationResult.terminated = true;
@@ -195,12 +197,14 @@ public class MigrationEngine {
}
LOGGER.info("Execute migration ... [ END ]");
}
-
+
public void migrateSingle(final DBEntry entry, final MigrationInterface elem, final int id, final int count) {
- LOGGER.info("Migrate: [{}/{}] {} [BEGIN]", id, count, elem.getName());
+ LOGGER.info("---------------------------------------------------------");
+ LOGGER.info("-- Migrate: [{}/{}] {} [BEGIN]", id, count, elem.getName());
+ LOGGER.info("---------------------------------------------------------");
final StringBuilder log = new StringBuilder();
- log.append("Start migration");
- MigrationModel migrationResult = new MigrationModel();
+ log.append("Start migration\n");
+ Migration migrationResult = new Migration();
migrationResult.name = elem.getName();
migrationResult.stepId = 0;
migrationResult.terminated = false;
@@ -212,7 +216,7 @@ public class MigrationEngine {
// TODO Auto-generated catch block
e.printStackTrace();
}
-
+
if (elem.applyMigration(entry, log, migrationResult)) {
migrationResult.terminated = true;
try {
@@ -241,9 +245,9 @@ public class MigrationEngine {
}
LOGGER.info("Migrate: [{}/{}] {} [ END ]", id, count, elem.getName());
}
-
+
public void revertTo(final DBEntry entry, final String migrationName) {
- final MigrationModel currentVersion = getCurrentVersion();
+ final Migration currentVersion = getCurrentVersion();
final List toApply = new ArrayList<>();
boolean find = false;
for (int iii = this.datas.size() - 1; iii >= 0; iii--) {
@@ -264,10 +268,10 @@ public class MigrationEngine {
revertSingle(entry, elem, id, count);
}
}
-
+
public void revertSingle(final DBEntry entry, final MigrationInterface elem, final int id, final int count) {
LOGGER.info("Revert migration: {} [BEGIN]", elem.getName());
-
+
LOGGER.info("Revert migration: {} [ END ]", elem.getName());
}
}
diff --git a/src/org/kar/archidata/migration/MigrationInterface.java b/src/org/kar/archidata/migration/MigrationInterface.java
index f0100fa..c76a0f5 100644
--- a/src/org/kar/archidata/migration/MigrationInterface.java
+++ b/src/org/kar/archidata/migration/MigrationInterface.java
@@ -1,6 +1,7 @@
package org.kar.archidata.migration;
import org.kar.archidata.db.DBEntry;
+import org.kar.archidata.migration.model.Migration;
public interface MigrationInterface {
/**
@@ -16,7 +17,7 @@ public interface MigrationInterface {
* @param migration Migration post data on each step...
* @return true if migration is finished.
*/
- boolean applyMigration(DBEntry entry, StringBuilder log, MigrationModel model);
+ boolean applyMigration(DBEntry entry, StringBuilder log, Migration model);
/**
* Remove a migration the system to the previous version.
diff --git a/src/org/kar/archidata/migration/MigrationSqlStep.java b/src/org/kar/archidata/migration/MigrationSqlStep.java
index 47152c7..6e57fa3 100644
--- a/src/org/kar/archidata/migration/MigrationSqlStep.java
+++ b/src/org/kar/archidata/migration/MigrationSqlStep.java
@@ -8,6 +8,7 @@ import java.util.List;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.db.DBEntry;
+import org.kar.archidata.migration.model.Migration;
import org.kar.archidata.util.ConfigBaseVariable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -18,7 +19,7 @@ record Action(
public Action(final String action) {
this(action, List.of());
}
-
+
public Action(final String action, final String filterDB) {
this(action, List.of(filterDB));
}
@@ -27,26 +28,26 @@ record Action(
public class MigrationSqlStep implements MigrationInterface {
final static Logger LOGGER = LoggerFactory.getLogger(MigrationSqlStep.class);
private final List actions = new ArrayList<>();
-
+
@Override
public String getName() {
return getClass().getCanonicalName();
}
-
+
public void display() {
for (int iii = 0; iii < this.actions.size(); iii++) {
final Action action = this.actions.get(iii);
LOGGER.info(" >>>> SQL ACTION : {}/{} ==> filter='{}'\n{}", iii, this.actions.size(), action.filterDB(), action.action());
}
}
-
+
@Override
- public boolean applyMigration(final DBEntry entry, final StringBuilder log, final MigrationModel model) {
+ public boolean applyMigration(final DBEntry entry, final StringBuilder log, final Migration model) {
for (int iii = 0; iii < this.actions.size(); iii++) {
log.append("action [" + (iii + 1) + "/" + this.actions.size() + "]\n");
LOGGER.info(" >>>> SQL ACTION : {}/{}", iii + 1, this.actions.size());
final Action action = this.actions.get(iii);
-
+
LOGGER.info("SQL request: ```{}``` on '{}' current={}", action.action(), action.filterDB(), ConfigBaseVariable.getDBType());
log.append("SQL: " + action.action() + " on " + action.filterDB() + "\n");
boolean isValid = true;
@@ -97,30 +98,30 @@ public class MigrationSqlStep implements MigrationInterface {
}
return true;
}
-
+
@Override
public boolean revertMigration(final DBEntry entry, final StringBuilder log) {
return false;
}
-
+
public void addAction(final String action) {
this.actions.add(new Action(action));
}
-
+
public void addAction(final String action, final String filterdBType) {
this.actions.add(new Action(action, filterdBType));
}
-
+
public void addClass(final Class> clazz) throws Exception {
- final List tmp = DataFactory.createTable(clazz, false);
+ final List tmp = DataFactory.createTable(clazz);
for (final String elem : tmp) {
this.actions.add(new Action(elem));
}
}
-
+
@Override
public int getNumberOfStep() {
return this.actions.size();
}
-
+
}
diff --git a/src/org/kar/archidata/migration/MigrationModel.java b/src/org/kar/archidata/migration/model/Migration.java
similarity index 90%
rename from src/org/kar/archidata/migration/MigrationModel.java
rename to src/org/kar/archidata/migration/model/Migration.java
index 5110d66..8a01c50 100644
--- a/src/org/kar/archidata/migration/MigrationModel.java
+++ b/src/org/kar/archidata/migration/model/Migration.java
@@ -1,4 +1,4 @@
-package org.kar.archidata.migration;
+package org.kar.archidata.migration.model;
import org.kar.archidata.annotation.SQLComment;
import org.kar.archidata.annotation.SQLDefault;
@@ -16,7 +16,7 @@ import jakarta.persistence.Table;
@Table(name = "KAR_migration")
@SQLIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL)
-public class MigrationModel extends GenericDataSoftDelete {
+public class Migration extends GenericDataSoftDelete {
@SQLComment("Name of the migration")
@Column(length = 256)
public String name;
diff --git a/test/src/test/kar/archidata/TestManyToMany.java b/test/src/test/kar/archidata/TestManyToMany.java
new file mode 100644
index 0000000..8bb98f9
--- /dev/null
+++ b/test/src/test/kar/archidata/TestManyToMany.java
@@ -0,0 +1,165 @@
+package test.kar.archidata;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.kar.archidata.GlobalConfiguration;
+import org.kar.archidata.dataAccess.DataAccess;
+import org.kar.archidata.dataAccess.DataFactory;
+import org.kar.archidata.dataAccess.addOn.AddOnManyToMany;
+import org.kar.archidata.db.DBEntry;
+import org.kar.archidata.util.ConfigBaseVariable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import test.kar.archidata.model.TypeManyToManyRemote;
+import test.kar.archidata.model.TypeManyToManyRoot;
+
+@ExtendWith(StepwiseExtension.class)
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class TestManyToMany {
+ final static private Logger LOGGER = LoggerFactory.getLogger(TestManyToMany.class);
+
+ @BeforeAll
+ public static void configureWebServer() throws Exception {
+ ConfigBaseVariable.dbType = "sqlite";
+ ConfigBaseVariable.dbHost = "memory";
+ // for test we need to connect all time the DB
+ ConfigBaseVariable.dbKeepConnected = "true";
+
+ // Connect the dataBase...
+ final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
+ entry.connect();
+ }
+
+ @AfterAll
+ public static void removeDataBase() throws IOException {
+ LOGGER.info("Remove the test db");
+ DBEntry.closeAllForceMode();
+ ConfigBaseVariable.clearAllValue();
+ }
+
+ @Order(1)
+ @Test
+ public void testCreateTable() throws Exception {
+ final List sqlCommand2 = DataFactory.createTable(TypeManyToManyRoot.class);
+ final List sqlCommand = DataFactory.createTable(TypeManyToManyRemote.class);
+ sqlCommand.addAll(sqlCommand2);
+ for (final String elem : sqlCommand) {
+ LOGGER.debug("request: '{}'", elem);
+ DataAccess.executeSimpleQuerry(elem, false);
+ }
+ }
+
+ @Order(2)
+ @Test
+ public void testSimpleInsertAndRetieve() throws Exception {
+ final TypeManyToManyRoot test = new TypeManyToManyRoot();
+ test.otherData = "kjhlkjlkj";
+ final TypeManyToManyRoot insertedData = DataAccess.insert(test);
+ Assertions.assertNotNull(insertedData);
+ Assertions.assertNotNull(insertedData.id);
+ Assertions.assertTrue(insertedData.id >= 0);
+ Assertions.assertNull(insertedData.remote);
+
+ // Try to retrieve all the data:
+ final TypeManyToManyRoot retrieve = DataAccess.get(TypeManyToManyRoot.class, insertedData.id);
+
+ Assertions.assertNotNull(retrieve);
+ Assertions.assertNotNull(retrieve.id);
+ Assertions.assertEquals(insertedData.id, retrieve.id);
+ Assertions.assertNotNull(retrieve.otherData);
+ Assertions.assertEquals(insertedData.otherData, retrieve.otherData);
+ Assertions.assertNull(retrieve.remote);
+
+ DataAccess.delete(TypeManyToManyRoot.class, insertedData.id);
+ }
+
+ @Order(3)
+ @Test
+ public void testSimpleInsertAndRetieveZZZ() throws Exception {
+
+ TypeManyToManyRemote remote = new TypeManyToManyRemote();
+ remote.data = "remote1";
+ final TypeManyToManyRemote insertedRemote1 = DataAccess.insert(remote);
+ Assertions.assertEquals(insertedRemote1.data, remote.data);
+
+ remote = new TypeManyToManyRemote();
+ remote.data = "remote2";
+ final TypeManyToManyRemote insertedRemote2 = DataAccess.insert(remote);
+ Assertions.assertEquals(insertedRemote2.data, remote.data);
+
+ final TypeManyToManyRoot test = new TypeManyToManyRoot();
+ test.otherData = "kjhlkjlkj";
+ final TypeManyToManyRoot insertedData = DataAccess.insert(test);
+ Assertions.assertNotNull(insertedData);
+ Assertions.assertNotNull(insertedData.id);
+ Assertions.assertTrue(insertedData.id >= 0);
+ Assertions.assertNull(insertedData.remote);
+
+ // Try to retrieve all the data:
+ TypeManyToManyRoot retrieve = DataAccess.get(TypeManyToManyRoot.class, insertedData.id);
+
+ Assertions.assertNotNull(retrieve);
+ Assertions.assertNotNull(retrieve.id);
+ Assertions.assertEquals(insertedData.id, retrieve.id);
+ Assertions.assertNotNull(retrieve.otherData);
+ Assertions.assertEquals(insertedData.otherData, retrieve.otherData);
+ Assertions.assertNull(retrieve.remote);
+
+ // Add remote elements
+ AddOnManyToMany.addLink(TypeManyToManyRoot.class, retrieve.id, "remote", insertedRemote1.id);
+ AddOnManyToMany.addLink(TypeManyToManyRoot.class, retrieve.id, "remote", insertedRemote2.id);
+
+ retrieve = DataAccess.get(TypeManyToManyRoot.class, insertedData.id);
+
+ Assertions.assertNotNull(retrieve);
+ Assertions.assertNotNull(retrieve.id);
+ Assertions.assertEquals(insertedData.id, retrieve.id);
+ Assertions.assertNotNull(retrieve.otherData);
+ Assertions.assertEquals(insertedData.otherData, retrieve.otherData);
+ Assertions.assertNotNull(retrieve.remote);
+ Assertions.assertEquals(retrieve.remote.size(), 2);
+ Assertions.assertEquals(retrieve.remote.get(0), insertedRemote1.id);
+ Assertions.assertEquals(retrieve.remote.get(1), insertedRemote2.id);
+
+ // Remove an element
+ int count = AddOnManyToMany.removeLink(TypeManyToManyRoot.class, retrieve.id, "remote", insertedRemote1.id);
+ Assertions.assertEquals(1, count);
+
+ retrieve = DataAccess.get(TypeManyToManyRoot.class, insertedData.id);
+
+ Assertions.assertNotNull(retrieve);
+ Assertions.assertNotNull(retrieve.id);
+ Assertions.assertEquals(insertedData.id, retrieve.id);
+ Assertions.assertNotNull(retrieve.otherData);
+ Assertions.assertEquals(insertedData.otherData, retrieve.otherData);
+ Assertions.assertNotNull(retrieve.remote);
+ Assertions.assertEquals(retrieve.remote.size(), 1);
+ Assertions.assertEquals(retrieve.remote.get(0), insertedRemote2.id);
+
+ // Remove the second element
+ count = AddOnManyToMany.removeLink(TypeManyToManyRoot.class, retrieve.id, "remote", insertedRemote2.id);
+ Assertions.assertEquals(1, count);
+
+ retrieve = DataAccess.get(TypeManyToManyRoot.class, insertedData.id);
+
+ Assertions.assertNotNull(retrieve);
+ Assertions.assertNotNull(retrieve.id);
+ Assertions.assertEquals(insertedData.id, retrieve.id);
+ Assertions.assertNotNull(retrieve.otherData);
+ Assertions.assertEquals(insertedData.otherData, retrieve.otherData);
+ Assertions.assertNull(retrieve.remote);
+
+ DataAccess.delete(TypeManyToManyRoot.class, insertedData.id);
+ }
+
+}
\ No newline at end of file
diff --git a/test/src/test/kar/archidata/model/TypeManyToManyRemote.java b/test/src/test/kar/archidata/model/TypeManyToManyRemote.java
new file mode 100644
index 0000000..8546c94
--- /dev/null
+++ b/test/src/test/kar/archidata/model/TypeManyToManyRemote.java
@@ -0,0 +1,15 @@
+package test.kar.archidata.model;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+
+public class TypeManyToManyRemote {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(nullable = false, unique = true)
+ public Long id = null;
+
+ public String data;
+}
\ No newline at end of file
diff --git a/test/src/test/kar/archidata/model/TypeManyToManyRoot.java b/test/src/test/kar/archidata/model/TypeManyToManyRoot.java
new file mode 100644
index 0000000..2d1a5bb
--- /dev/null
+++ b/test/src/test/kar/archidata/model/TypeManyToManyRoot.java
@@ -0,0 +1,22 @@
+package test.kar.archidata.model;
+
+import java.util.List;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.ManyToMany;
+
+public class TypeManyToManyRoot {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(nullable = false, unique = true)
+ public Long id = null;
+
+ public String otherData;
+
+ @ManyToMany(fetch = FetchType.LAZY, targetEntity = TypeManyToManyRemote.class)
+ public List remote;
+}