[FIX-DOC] update migration and some docs

This commit is contained in:
Edouard DUPIN 2023-11-26 22:54:30 +01:00
parent c3d2eff5be
commit 3d526aaede
7 changed files with 90 additions and 50 deletions

View File

@ -36,7 +36,7 @@ public class AnnotationTools {
return element.getSimpleName(); return element.getSimpleName();
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @SQLTableName on " + element.getClass().getCanonicalName()); throw new Exception("Must not have more than 1 element @Table on " + element.getClass().getCanonicalName());
} }
final String tmp = ((Table) annotation[0]).name(); final String tmp = ((Table) annotation[0]).name();
if (tmp == null) { if (tmp == null) {
@ -51,7 +51,7 @@ public class AnnotationTools {
return null; return null;
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @SQLComment on " + element.getClass().getCanonicalName()); throw new Exception("Must not have more than 1 element @DataComment on " + element.getClass().getCanonicalName());
} }
return ((DataComment) annotation[0]).value(); return ((DataComment) annotation[0]).value();
} }
@ -62,7 +62,7 @@ public class AnnotationTools {
return null; return null;
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @SQLDefault on " + element.getClass().getCanonicalName()); throw new Exception("Must not have more than 1 element @DataDefault on " + element.getClass().getCanonicalName());
} }
return ((DataDefault) annotation[0]).value(); return ((DataDefault) annotation[0]).value();
} }
@ -70,12 +70,13 @@ public class AnnotationTools {
public static Integer getLimitSize(final Field element) throws Exception { public static Integer getLimitSize(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class);
if (annotation.length == 0) { if (annotation.length == 0) {
return null; return 255;
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @SQLLimitSize on " + element.getClass().getCanonicalName()); throw new Exception("Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
} }
return ((Column) annotation[0]).length(); final int length = ((Column) annotation[0]).length();
return length <= 0 ? null : length;
} }
public static boolean isAnnotationGroup(final Field field, final Class<?> annotationType) { public static boolean isAnnotationGroup(final Field field, final Class<?> annotationType) {

View File

@ -40,8 +40,20 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.ws.rs.InternalServerErrorException; import jakarta.ws.rs.InternalServerErrorException;
/* TODO list:
- useful code to manage external query: List<T> query<T>(class<T> clazz, String query, List<Object> parameters);
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM TABLE2");
ResultSetMetaData rsmd = rs.getMetaData();
String name = rsmd.getColumnName(1);
- Manage to group of SQL action to permit to commit only at the end.
*/
/** Data access is an abstraction class that permit to access on the DB with a function wrapping that permit to minimize the SQL writing of SQL code. This interface support the SQL and SQLite
* back-end. */
public class DataAccess { public class DataAccess {
static final Logger LOGGER = LoggerFactory.getLogger(DataAccess.class); static final Logger LOGGER = LoggerFactory.getLogger(DataAccess.class);
// by default we manage some add-on that permit to manage non-native model (like json serialization, List of external key as String list...)
static final List<DataAccessAddOn> addOn = new ArrayList<>(); static final List<DataAccessAddOn> addOn = new ArrayList<>();
static { static {
@ -51,20 +63,12 @@ public class DataAccess {
addOn.add(new AddOnDataJson()); addOn.add(new AddOnDataJson());
} }
/** Add a new add-on on the current management.
* @param addOn instantiate object on the Add-on */
public static void addAddOn(final DataAccessAddOn addOn) { public static void addAddOn(final DataAccessAddOn addOn) {
DataAccess.addOn.add(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 DataAccess() {
} }
@ -777,7 +781,6 @@ public class DataAccess {
} }
} }
whereInjectValue(ps, condition, iii); whereInjectValue(ps, condition, iii);
return ps.executeUpdate(); return ps.executeUpdate();
} catch (final SQLException ex) { } catch (final SQLException ex) {
ex.printStackTrace(); ex.printStackTrace();
@ -805,8 +808,6 @@ public class DataAccess {
ps.setDouble(iii.value, tmp); ps.setDouble(iii.value, tmp);
} else if (value instanceof final Boolean tmp) { } else if (value instanceof final Boolean tmp) {
ps.setBoolean(iii.value, tmp); ps.setBoolean(iii.value, tmp);
} else if (value instanceof final Boolean tmp) {
ps.setBoolean(iii.value, tmp);
} else if (value instanceof final Timestamp tmp) { } else if (value instanceof final Timestamp tmp) {
ps.setTimestamp(iii.value, tmp); ps.setTimestamp(iii.value, tmp);
} else if (value instanceof final Date tmp) { } else if (value instanceof final Date tmp) {
@ -822,8 +823,7 @@ public class DataAccess {
} }
} }
public static void whereAppendQuery(final StringBuilder query, final String tableName, final QueryItem condition, final QueryOptions options, final String deletedFieldName) public static void whereAppendQuery(final StringBuilder query, final String tableName, final QueryItem condition, final QueryOptions options, final String deletedFieldName) {
throws ExceptionDBInterface {
boolean exclude_deleted = true; boolean exclude_deleted = true;
if (options != null) { if (options != null) {
exclude_deleted = !options.exist(AccessDeletedItems.class); exclude_deleted = !options.exist(AccessDeletedItems.class);
@ -1058,6 +1058,12 @@ public class DataAccess {
return delete(clazz, id, null); return delete(clazz, id, null);
} }
/** Delete items with the specific Id (cf @Id) and some options. If the Entity is manage as a softDeleted model, then it is flag as removed (if not already done before).
* @param <ID_TYPE> Type of the reference @Id
* @param clazz Data model that might remove element
* @param id Unique Id of the model
* @param options (Optional) Options of the request
* @return Number of element that is removed. */
public static <ID_TYPE> int delete(final Class<?> clazz, final ID_TYPE id, final QueryOptions options) throws Exception { public static <ID_TYPE> int delete(final Class<?> clazz, final ID_TYPE id, final QueryOptions options) throws Exception {
final String hasDeletedFieldName = AnnotationTools.getDeletedFieldName(clazz); final String hasDeletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
if (hasDeletedFieldName != null) { if (hasDeletedFieldName != null) {
@ -1067,6 +1073,11 @@ public class DataAccess {
} }
} }
/** Delete items with the specific condition and some options. If the Entity is manage as a softDeleted model, then it is flag as removed (if not already done before).
* @param clazz Data model that might remove element.
* @param condition Condition to remove elements.
* @param options (Optional) Options of the request.
* @return Number of element that is removed. */
public static int deleteWhere(final Class<?> clazz, final QueryItem condition, final QueryOptions options) throws Exception { public static int deleteWhere(final Class<?> clazz, final QueryItem condition, final QueryOptions options) throws Exception {
final String hasDeletedFieldName = AnnotationTools.getDeletedFieldName(clazz); final String hasDeletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
if (hasDeletedFieldName != null) { if (hasDeletedFieldName != null) {
@ -1107,13 +1118,6 @@ public class DataAccess {
return deleteSoftWhere(clazz, getTableIdCondition(clazz, id), options); return deleteSoftWhere(clazz, getTableIdCondition(clazz, 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, final QueryOptions options) throws Exception { public static int deleteSoftWhere(final Class<?> clazz, final QueryItem condition, final QueryOptions options) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz, options); final String tableName = AnnotationTools.getTableName(clazz, options);
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz); final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
@ -1163,7 +1167,6 @@ public class DataAccess {
query.append("` SET `"); query.append("` SET `");
query.append(deletedFieldName); query.append(deletedFieldName);
query.append("`=false "); query.append("`=false ");
/* is is needed only for SQLite ??? query.append("`modify_date`="); query.append(getDBNow()); query.append(", "); */
// need to disable the deleted false because the model must be unselected to be updated. // need to disable the deleted false because the model must be unselected to be updated.
options.add(QueryOptions.ACCESS_DELETED_ITEMS); options.add(QueryOptions.ACCESS_DELETED_ITEMS);
whereAppendQuery(query, tableName, condition, options, deletedFieldName); whereAppendQuery(query, tableName, condition, options, deletedFieldName);

View File

@ -48,13 +48,20 @@ public class MigrationEngine {
} }
/** Get the current version/migration name /** Get the current version/migration name
* @return Model represent the last migration. If null then no migration has been done. */ * @return Model represent the last migration. If null then no migration has been done.
public Migration getCurrentVersion() { * @throws MigrationException */
public Migration getCurrentVersion() throws MigrationException {
if (!DataAccess.isTableExist("KAR_migration")) { if (!DataAccess.isTableExist("KAR_migration")) {
return null; return null;
} }
try { try {
final List<Migration> data = DataAccess.gets(Migration.class, new QueryOptions(QueryOptions.READ_ALL_COLOMN)); List<Migration> data = null;
try {
data = DataAccess.gets(Migration.class, new QueryOptions(QueryOptions.READ_ALL_COLOMN));
} catch (final Exception e) {
// Previous version does not have the same timeCode...
data = DataAccess.gets(Migration.class);
}
if (data == null) { if (data == null) {
LOGGER.error("Can not collect the migration table in the DB:{}"); LOGGER.error("Can not collect the migration table in the DB:{}");
return null; return null;
@ -63,16 +70,16 @@ public class MigrationEngine {
LOGGER.error("Fail to Request migration table in the DB: empty size"); LOGGER.error("Fail to Request migration table in the DB: empty size");
return null; return null;
} }
LOGGER.debug("List of migrations:"); LOGGER.info("List of migrations:");
for (final Migration elem : data) { for (final Migration elem : data) {
LOGGER.debug(" - date={} name={} end={}", elem.updatedAt, elem.name, elem.terminated); LOGGER.info(" - date={} name={} end={}", elem.updatedAt, elem.name, elem.terminated);
} }
return data.get(data.size() - 1); return data.get(data.size() - 1);
} catch (final Exception ex) { } catch (final Exception ex) {
LOGGER.error("Fail to Request migration table in the DB:{}", ex.getMessage()); LOGGER.error("Fail to Request migration table in the DB:{}", ex.getMessage());
ex.printStackTrace(); ex.printStackTrace();
} }
return null; throw new MigrationException("Can not retreive Migration model");
} }
/** Process the automatic migration of the system The function wait the Administrator intervention to correct the bug. /** Process the automatic migration of the system The function wait the Administrator intervention to correct the bug.
@ -99,10 +106,13 @@ public class MigrationEngine {
public void migrateErrorThrow(final DBConfig config) throws MigrationException { public void migrateErrorThrow(final DBConfig config) throws MigrationException {
LOGGER.info("Execute migration ... [BEGIN]"); LOGGER.info("Execute migration ... [BEGIN]");
// check the integrity of the migrations: // check the integrity of the migrations:
LOGGER.info("List of availlable Migration: ");
for (final MigrationInterface elem : this.datas) { for (final MigrationInterface elem : this.datas) {
if (elem == null) { if (elem == null) {
LOGGER.info(" - null");
throw new MigrationException("Add a null migration"); throw new MigrationException("Add a null migration");
} }
LOGGER.info(" - {}", elem.getName());
if (elem == this.init) { if (elem == this.init) {
throw new MigrationException("Add a migration that is the initialization migration"); throw new MigrationException("Add a migration that is the initialization migration");
} }
@ -142,6 +152,7 @@ public class MigrationEngine {
LOGGER.info("DB '{}' exist.", config.getDbName()); LOGGER.info("DB '{}' exist.", config.getDbName());
// STEP 2: Check migration table exist: // STEP 2: Check migration table exist:
LOGGER.info("Verify existance of migration table '{}'", "KAR_migration"); LOGGER.info("Verify existance of migration table '{}'", "KAR_migration");
// TODO: set the class in parameters instead of string...
exist = DataAccess.isTableExist("KAR_migration"); exist = DataAccess.isTableExist("KAR_migration");
if (!exist) { if (!exist) {
LOGGER.info("'{}' Does not exist create a new one...", "KAR_migration"); LOGGER.info("'{}' Does not exist create a new one...", "KAR_migration");
@ -180,18 +191,22 @@ public class MigrationEngine {
throw new MigrationException("An error occured in the last migration: '" + currentVersion.name + "' defect @" + currentVersion.stepId + "/" + currentVersion.count); throw new MigrationException("An error occured in the last migration: '" + currentVersion.name + "' defect @" + currentVersion.stepId + "/" + currentVersion.count);
} }
LOGGER.info("Upgrade the system Current version: {}", currentVersion.name); LOGGER.info("Upgrade the system Current version: {}", currentVersion.name);
boolean find = this.init != null && this.init.getName() == currentVersion.name; boolean find = this.init != null && this.init.getName().equals(currentVersion.name);
if (currentVersion.name.equals(this.init.getName())) { if (find) {
toApply = this.datas; toApply = this.datas;
} else { } else {
for (int iii = 0; iii < this.datas.size(); iii++) { LOGGER.info(" ===> Check what must be apply:");
for (final MigrationInterface elem : this.datas) {
LOGGER.info(" - {}", elem.getName());
if (!find) { if (!find) {
if (this.datas.get(iii).getName() == currentVersion.name) { if (currentVersion.name.equals(elem.getName())) {
LOGGER.info(" == current version");
find = true; find = true;
} }
continue; continue;
} }
toApply.add(this.datas.get(iii)); LOGGER.info(" ++ add ");
toApply.add(elem);
} }
} }
} }
@ -241,13 +256,18 @@ public class MigrationEngine {
migrationResult.name = elem.getName(); migrationResult.name = elem.getName();
migrationResult.stepId = 0; migrationResult.stepId = 0;
migrationResult.terminated = false; migrationResult.terminated = false;
migrationResult.count = elem.getNumberOfStep(); try {
migrationResult.count = elem.getNumberOfStep();
} catch (final Exception e) {
e.printStackTrace();
throw new MigrationException("Fail to get number of migration step (maybe generation fail): " + e.getLocalizedMessage());
}
migrationResult.log = log.toString(); migrationResult.log = log.toString();
try { try {
migrationResult = DataAccess.insert(migrationResult); migrationResult = DataAccess.insert(migrationResult);
} catch (final Exception e) { } catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
throw new MigrationException("Fail to insert migration Log in the migration table: " + e.getLocalizedMessage());
} }
boolean ret = true; boolean ret = true;
try { try {
@ -256,7 +276,7 @@ public class MigrationEngine {
log.append("\nFail in the migration apply "); log.append("\nFail in the migration apply ");
log.append(e.getLocalizedMessage()); log.append(e.getLocalizedMessage());
e.printStackTrace(); e.printStackTrace();
throw new MigrationException("Migration fial: '" + migrationResult.name + "' defect @" + migrationResult.stepId + "/" + migrationResult.count); throw new MigrationException("Migration fail: '" + migrationResult.name + "' defect @" + migrationResult.stepId + "/" + migrationResult.count);
} }
if (ret) { if (ret) {
migrationResult.terminated = true; migrationResult.terminated = true;
@ -264,6 +284,7 @@ public class MigrationEngine {
DataAccess.update(migrationResult, migrationResult.id, List.of("terminated")); DataAccess.update(migrationResult, migrationResult.id, List.of("terminated"));
} catch (final Exception e) { } catch (final Exception e) {
e.printStackTrace(); e.printStackTrace();
throw new MigrationException("Fail to update migration Log in the migration table: " + e.getLocalizedMessage());
} }
} else { } else {
try { try {
@ -272,13 +293,15 @@ public class MigrationEngine {
DataAccess.update(migrationResult, migrationResult.id, List.of("log")); DataAccess.update(migrationResult, migrationResult.id, List.of("log"));
} catch (final Exception e) { } catch (final Exception e) {
e.printStackTrace(); e.printStackTrace();
throw new MigrationException("Fail to update migration Log in the migration table: " + e.getLocalizedMessage() + " WITH: An error occured in the migration (OUTSIDE detection): '"
+ migrationResult.name + "' defect @" + migrationResult.stepId + "/" + migrationResult.count);
} }
throw new MigrationException("An error occured in the migration (OUTSIDE detection): '" + migrationResult.name + "' defect @" + migrationResult.stepId + "/" + migrationResult.count); throw new MigrationException("An error occured in the migration (OUTSIDE detection): '" + migrationResult.name + "' defect @" + migrationResult.stepId + "/" + migrationResult.count);
} }
LOGGER.info("Migrate: [{}/{}] {} [ END ]", id, count, elem.getName()); LOGGER.info("Migrate: [{}/{}] {} [ END ]", id, count, elem.getName());
} }
public void revertTo(final DBEntry entry, final String migrationName) { public void revertTo(final DBEntry entry, final String migrationName) throws MigrationException {
final Migration currentVersion = getCurrentVersion(); final Migration currentVersion = getCurrentVersion();
final List<MigrationInterface> toApply = new ArrayList<>(); final List<MigrationInterface> toApply = new ArrayList<>();
boolean find = false; boolean find = false;

View File

@ -23,5 +23,5 @@ public interface MigrationInterface {
/** Get the number of step in the migration process. /** Get the number of step in the migration process.
* @return count of SQL access. */ * @return count of SQL access. */
int getNumberOfStep(); int getNumberOfStep() throws Exception;
} }

View File

@ -26,13 +26,18 @@ record Action(String action, List<String> filterDB) {
public class MigrationSqlStep implements MigrationInterface { public class MigrationSqlStep implements MigrationInterface {
final static Logger LOGGER = LoggerFactory.getLogger(MigrationSqlStep.class); final static Logger LOGGER = LoggerFactory.getLogger(MigrationSqlStep.class);
private final List<Action> actions = new ArrayList<>(); private final List<Action> actions = new ArrayList<>();
private boolean isGenerated = false;
@Override @Override
public String getName() { public String getName() {
return getClass().getCanonicalName(); return getClass().getCanonicalName();
} }
public void display() { public void display() throws Exception {
if (!this.isGenerated) {
this.isGenerated = true;
generateStep();
}
for (int iii = 0; iii < this.actions.size(); iii++) { for (int iii = 0; iii < this.actions.size(); iii++) {
final Action action = this.actions.get(iii); final Action action = this.actions.get(iii);
LOGGER.info(" >>>> SQL ACTION : {}/{} ==> filter='{}'\n{}", iii, this.actions.size(), action.filterDB(), action.action()); LOGGER.info(" >>>> SQL ACTION : {}/{} ==> filter='{}'\n{}", iii, this.actions.size(), action.filterDB(), action.action());
@ -49,7 +54,10 @@ public class MigrationSqlStep implements MigrationInterface {
@Override @Override
public boolean applyMigration(final DBEntry entry, final StringBuilder log, final Migration model) throws Exception { public boolean applyMigration(final DBEntry entry, final StringBuilder log, final Migration model) throws Exception {
generateStep(); if (!this.isGenerated) {
this.isGenerated = true;
generateStep();
}
for (int iii = 0; iii < this.actions.size(); iii++) { for (int iii = 0; iii < this.actions.size(); iii++) {
log.append("action [" + (iii + 1) + "/" + this.actions.size() + "]\n"); log.append("action [" + (iii + 1) + "/" + this.actions.size() + "]\n");
LOGGER.info(" >>>> SQL ACTION : {}/{}", iii + 1, this.actions.size()); LOGGER.info(" >>>> SQL ACTION : {}/{}", iii + 1, this.actions.size());
@ -128,7 +136,11 @@ public class MigrationSqlStep implements MigrationInterface {
} }
@Override @Override
public int getNumberOfStep() { public int getNumberOfStep() throws Exception {
if (!this.isGenerated) {
this.isGenerated = true;
generateStep();
}
return this.actions.size(); return this.actions.size();
} }

View File

@ -29,5 +29,6 @@ public class Migration extends GenericDataSoftDelete {
@DataComment("number of element in the migration") @DataComment("number of element in the migration")
public Integer count; public Integer count;
@DataComment("Log generate by the migration") @DataComment("Log generate by the migration")
@Column(length = 0)
public String log = ""; public String log = "";
} }

View File

@ -15,10 +15,10 @@ import jakarta.persistence.Table;
public class GenericToken extends GenericDataSoftDelete { public class GenericToken extends GenericDataSoftDelete {
@Column(nullable = false) @Column(nullable = false)
public Long parentId; public Long parentId;
@Column(nullable = false) @Column(nullable = false, length = 0)
public String name; public String name;
@Column(nullable = false) @Column(nullable = false)
public Timestamp endValidityTime = null; public Timestamp endValidityTime = null;
@Column(nullable = false) @Column(nullable = false, length = 0)
public String token; public String token;
} }