From 8d271601be88ef161c26bf1431948df8cc2edfd3 Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Thu, 2 Nov 2023 15:14:55 +0100 Subject: [PATCH] [DEV] refacto dataAccess and ManyToMany interface (get Long) --- .classpath | 1 + .project | 10 - .../archidata/annotation/AnnotationTools.java | 42 +++ .../kar/archidata/dataAccess/DataAccess.java | 318 ++++++++++-------- .../archidata/dataAccess/DataAccessAddOn.java | 7 +- .../kar/archidata/dataAccess/DataFactory.java | 55 +-- .../archidata/dataAccess/QueryOptions.java | 12 +- .../dataAccess/addOn/AddOnManyToMany.java | 208 ++++-------- .../addOn/AddOnManyToManyOrdered.java | 24 +- .../dataAccess/addOn/AddOnManyToOne.java | 8 +- .../AddOnSQLTableExternalForeinKeyAsList.java | 8 +- .../dataAccess/addOn/model/LinkTable.java | 25 ++ .../archidata/migration/MigrationEngine.java | 50 +-- .../migration/MigrationInterface.java | 3 +- .../archidata/migration/MigrationSqlStep.java | 27 +- .../Migration.java} | 4 +- .../test/kar/archidata/TestManyToMany.java | 165 +++++++++ .../archidata/model/TypeManyToManyRemote.java | 15 + .../archidata/model/TypeManyToManyRoot.java | 22 ++ 19 files changed, 626 insertions(+), 378 deletions(-) create mode 100644 src/org/kar/archidata/dataAccess/addOn/model/LinkTable.java rename src/org/kar/archidata/migration/{MigrationModel.java => model/Migration.java} (90%) create mode 100644 test/src/test/kar/archidata/TestManyToMany.java create mode 100644 test/src/test/kar/archidata/model/TypeManyToManyRemote.java create mode 100644 test/src/test/kar/archidata/model/TypeManyToManyRoot.java 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; +}