[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();
}
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();
if (tmp == null) {
@ -51,7 +51,7 @@ public class AnnotationTools {
return null;
}
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();
}
@ -62,7 +62,7 @@ public class AnnotationTools {
return null;
}
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();
}
@ -70,12 +70,13 @@ public class AnnotationTools {
public static Integer getLimitSize(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class);
if (annotation.length == 0) {
return null;
return 255;
}
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) {

View File

@ -40,8 +40,20 @@ import com.fasterxml.jackson.databind.ObjectMapper;
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 {
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 {
@ -51,20 +63,12 @@ public class DataAccess {
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) {
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() {
}
@ -777,7 +781,6 @@ public class DataAccess {
}
}
whereInjectValue(ps, condition, iii);
return ps.executeUpdate();
} catch (final SQLException ex) {
ex.printStackTrace();
@ -805,8 +808,6 @@ public class DataAccess {
ps.setDouble(iii.value, tmp);
} else if (value instanceof final Boolean 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) {
ps.setTimestamp(iii.value, 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)
throws ExceptionDBInterface {
public static void whereAppendQuery(final StringBuilder query, final String tableName, final QueryItem condition, final QueryOptions options, final String deletedFieldName) {
boolean exclude_deleted = true;
if (options != null) {
exclude_deleted = !options.exist(AccessDeletedItems.class);
@ -1058,6 +1058,12 @@ public class DataAccess {
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 {
final String hasDeletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
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 {
final String hasDeletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
if (hasDeletedFieldName != null) {
@ -1107,13 +1118,6 @@ public class DataAccess {
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 {
final String tableName = AnnotationTools.getTableName(clazz, options);
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
@ -1163,7 +1167,6 @@ public class DataAccess {
query.append("` SET `");
query.append(deletedFieldName);
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.
options.add(QueryOptions.ACCESS_DELETED_ITEMS);
whereAppendQuery(query, tableName, condition, options, deletedFieldName);

View File

@ -48,13 +48,20 @@ public class MigrationEngine {
}
/** Get the current version/migration name
* @return Model represent the last migration. If null then no migration has been done. */
public Migration getCurrentVersion() {
* @return Model represent the last migration. If null then no migration has been done.
* @throws MigrationException */
public Migration getCurrentVersion() throws MigrationException {
if (!DataAccess.isTableExist("KAR_migration")) {
return null;
}
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) {
LOGGER.error("Can not collect the migration table in the DB:{}");
return null;
@ -63,16 +70,16 @@ public class MigrationEngine {
LOGGER.error("Fail to Request migration table in the DB: empty size");
return null;
}
LOGGER.debug("List of migrations:");
LOGGER.info("List of migrations:");
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);
} catch (final Exception ex) {
LOGGER.error("Fail to Request migration table in the DB:{}", ex.getMessage());
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.
@ -99,10 +106,13 @@ public class MigrationEngine {
public void migrateErrorThrow(final DBConfig config) throws MigrationException {
LOGGER.info("Execute migration ... [BEGIN]");
// check the integrity of the migrations:
LOGGER.info("List of availlable Migration: ");
for (final MigrationInterface elem : this.datas) {
if (elem == null) {
LOGGER.info(" - null");
throw new MigrationException("Add a null migration");
}
LOGGER.info(" - {}", elem.getName());
if (elem == this.init) {
throw new MigrationException("Add a migration that is the initialization migration");
}
@ -142,6 +152,7 @@ public class MigrationEngine {
LOGGER.info("DB '{}' exist.", config.getDbName());
// STEP 2: Check migration table exist:
LOGGER.info("Verify existance of migration table '{}'", "KAR_migration");
// TODO: set the class in parameters instead of string...
exist = DataAccess.isTableExist("KAR_migration");
if (!exist) {
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);
}
LOGGER.info("Upgrade the system Current version: {}", currentVersion.name);
boolean find = this.init != null && this.init.getName() == currentVersion.name;
if (currentVersion.name.equals(this.init.getName())) {
boolean find = this.init != null && this.init.getName().equals(currentVersion.name);
if (find) {
toApply = this.datas;
} 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 (this.datas.get(iii).getName() == currentVersion.name) {
if (currentVersion.name.equals(elem.getName())) {
LOGGER.info(" == current version");
find = true;
}
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.stepId = 0;
migrationResult.terminated = false;
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();
try {
migrationResult = DataAccess.insert(migrationResult);
} catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new MigrationException("Fail to insert migration Log in the migration table: " + e.getLocalizedMessage());
}
boolean ret = true;
try {
@ -256,7 +276,7 @@ public class MigrationEngine {
log.append("\nFail in the migration apply ");
log.append(e.getLocalizedMessage());
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) {
migrationResult.terminated = true;
@ -264,6 +284,7 @@ public class MigrationEngine {
DataAccess.update(migrationResult, migrationResult.id, List.of("terminated"));
} catch (final Exception e) {
e.printStackTrace();
throw new MigrationException("Fail to update migration Log in the migration table: " + e.getLocalizedMessage());
}
} else {
try {
@ -272,13 +293,15 @@ public class MigrationEngine {
DataAccess.update(migrationResult, migrationResult.id, List.of("log"));
} catch (final Exception e) {
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);
}
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 List<MigrationInterface> toApply = new ArrayList<>();
boolean find = false;

View File

@ -23,5 +23,5 @@ public interface MigrationInterface {
/** Get the number of step in the migration process.
* @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 {
final static Logger LOGGER = LoggerFactory.getLogger(MigrationSqlStep.class);
private final List<Action> actions = new ArrayList<>();
private boolean isGenerated = false;
@Override
public String getName() {
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++) {
final Action action = this.actions.get(iii);
LOGGER.info(" >>>> SQL ACTION : {}/{} ==> filter='{}'\n{}", iii, this.actions.size(), action.filterDB(), action.action());
@ -49,7 +54,10 @@ public class MigrationSqlStep implements MigrationInterface {
@Override
public boolean applyMigration(final DBEntry entry, final StringBuilder log, final Migration model) throws Exception {
if (!this.isGenerated) {
this.isGenerated = true;
generateStep();
}
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());
@ -128,7 +136,11 @@ public class MigrationSqlStep implements MigrationInterface {
}
@Override
public int getNumberOfStep() {
public int getNumberOfStep() throws Exception {
if (!this.isGenerated) {
this.isGenerated = true;
generateStep();
}
return this.actions.size();
}

View File

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

View File

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