[DEV] start full refacto of SQL interface

This commit is contained in:
Edouard DUPIN 2024-10-05 12:28:49 +02:00
parent 6b0c392ff3
commit c144cd378e
21 changed files with 3897 additions and 930 deletions

View File

@ -11,6 +11,7 @@ import org.kar.archidata.exception.DataAccessException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import dev.morphia.annotations.Entity;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable; import jakarta.annotation.Nullable;
import jakarta.persistence.Column; import jakarta.persistence.Column;
@ -31,6 +32,7 @@ import jakarta.ws.rs.DefaultValue;
public class AnnotationTools { public class AnnotationTools {
static final Logger LOGGER = LoggerFactory.getLogger(AnnotationTools.class); static final Logger LOGGER = LoggerFactory.getLogger(AnnotationTools.class);
// For SQL declaration table Name
public static String getTableName(final Class<?> clazz, final QueryOptions options) throws DataAccessException { public static String getTableName(final Class<?> clazz, final QueryOptions options) throws DataAccessException {
if (options != null) { if (options != null) {
final List<OverrideTableName> data = options.get(OverrideTableName.class); final List<OverrideTableName> data = options.get(OverrideTableName.class);
@ -41,16 +43,13 @@ public class AnnotationTools {
return AnnotationTools.getTableName(clazz); return AnnotationTools.getTableName(clazz);
} }
public static String getTableName(final Class<?> element) throws DataAccessException { // For SQL declaration table Name
public static String getTableName(final Class<?> element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Table.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(Table.class);
if (annotation.length == 0) { if (annotation.length == 0) {
// when no annotation is detected, then the table name is the class name // when no annotation is detected, then the table name is the class name
return element.getSimpleName(); return element.getSimpleName();
} }
if (annotation.length > 1) {
throw new DataAccessException(
"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) {
return element.getSimpleName(); return element.getSimpleName();
@ -58,6 +57,31 @@ public class AnnotationTools {
return tmp; return tmp;
} }
public static String getCollectionName(final Class<?> clazz, final QueryOptions options) {
if (options != null) {
// TODO: maybe change OverrideTableName with OverrideCollectionName
final List<OverrideTableName> data = options.get(OverrideTableName.class);
if (data.size() == 1) {
return data.get(0).getName();
}
}
return AnnotationTools.getCollectionName(clazz);
}
// For No-SQL Table/Collection Name
public static String getCollectionName(final Class<?> clazz) {
final Annotation[] annotation = clazz.getDeclaredAnnotationsByType(Entity.class);
if (annotation.length == 0) {
// when no annotation is detected, then the table name is the class name
return clazz.getSimpleName();
}
final String tmp = ((Entity) annotation[0]).value();
if (tmp == null) {
return clazz.getSimpleName();
}
return tmp;
}
public static boolean getSchemaReadOnly(final Field element) throws DataAccessException { public static boolean getSchemaReadOnly(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class);
if (annotation.length == 0) { if (annotation.length == 0) {

View File

@ -9,12 +9,9 @@ import java.sql.ResultSetMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.sql.Types;
import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalTime; import java.time.LocalTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -22,24 +19,19 @@ import java.util.UUID;
import org.kar.archidata.annotation.AnnotationTools; import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.CreationTimestamp; import org.kar.archidata.annotation.CreationTimestamp;
import org.kar.archidata.annotation.UpdateTimestamp; import org.kar.archidata.annotation.UpdateTimestamp;
import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
import org.kar.archidata.dataAccess.addOn.AddOnManyToMany;
import org.kar.archidata.dataAccess.addOn.AddOnManyToOne;
import org.kar.archidata.dataAccess.addOn.AddOnOneToMany;
import org.kar.archidata.dataAccess.options.CheckFunction; import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.dataAccess.options.Condition; import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.DBInterfaceOption; import org.kar.archidata.dataAccess.options.DBInterfaceOption;
import org.kar.archidata.dataAccess.options.DBInterfaceRoot;
import org.kar.archidata.dataAccess.options.FilterValue; import org.kar.archidata.dataAccess.options.FilterValue;
import org.kar.archidata.dataAccess.options.GroupBy; import org.kar.archidata.dataAccess.options.GroupBy;
import org.kar.archidata.dataAccess.options.Limit; import org.kar.archidata.dataAccess.options.Limit;
import org.kar.archidata.dataAccess.options.OrderBy; import org.kar.archidata.dataAccess.options.OrderBy;
import org.kar.archidata.dataAccess.options.QueryOption; import org.kar.archidata.dataAccess.options.QueryOption;
import org.kar.archidata.dataAccess.options.TransmitKey; import org.kar.archidata.dataAccess.options.TransmitKey;
import org.kar.archidata.db.DBEntry; import org.kar.archidata.db.DbInterface;
import org.kar.archidata.db.DbInterfaceMorphia;
import org.kar.archidata.db.DbInterfaceSQL;
import org.kar.archidata.exception.DataAccessException; import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.kar.archidata.tools.DateTools;
import org.kar.archidata.tools.UuidUtils; import org.kar.archidata.tools.UuidUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -57,737 +49,31 @@ import jakarta.ws.rs.InternalServerErrorException;
/** 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 /** 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. */ * back-end. */
public class DataAccess { public abstract 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 { static public final DataAccess greateInterface(final DbInterface io) {
addOn.add(new AddOnManyToMany()); if (io instanceof final DbInterfaceMorphia ioMorphia) {
addOn.add(new AddOnManyToOne()); return DataAccessMorphia(ioMorphia);
addOn.add(new AddOnOneToMany()); } else if (io instanceof final DbInterfaceSQL ioSQL) {
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 DataAccess() {
}
} }
public static boolean isDBExist(final String name, final QueryOption... option) public static boolean isDBExist(final String name, final QueryOption... option)
throws InternalServerErrorException { throws InternalServerErrorException {
final QueryOptions options = new QueryOptions(option);
if ("sqlite".equals(ConfigBaseVariable.getDBType())) {
// no base manage in sqLite ...
// TODO: check if the file exist or not ...
return true;
}
DBEntry entry;
try {
entry = DBInterfaceOption.getAutoEntry(options);
} catch (final IOException ex) {
ex.printStackTrace();
LOGGER.error("Can not check if the DB exist!!! {}", ex.getMessage());
// TODO: TO test
return false;
}
try {
// TODO : Maybe connect with a temporary not specified connection interface to a db ...
final PreparedStatement ps = entry.connection.prepareStatement("show databases");
final ResultSet rs = ps.executeQuery();
// LOGGER.info("List all tables: equals? '{}'", name);
while (rs.next()) {
final String data = rs.getString(1);
// LOGGER.info(" - '{}'", data);
if (name.equals(data)) {
return true;
}
}
return false;
} catch (final SQLException ex) {
LOGGER.error("Can not check if the DB exist SQL-error !!! {}", ex.getMessage());
} finally {
try {
entry.close();
} catch (final IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
entry = null;
}
throw new InternalServerErrorException("Can Not manage the DB-access"); throw new InternalServerErrorException("Can Not manage the DB-access");
} }
public static boolean createDB(final String name) { public static boolean createDB(final String name) {
if ("sqlite".equals(ConfigBaseVariable.getDBType())) { throw new InternalServerErrorException("Can Not manage the DB-access");
// no base manage in sqLite ...
// TODO: check if the file exist or not ...
return true;
}
try {
return 1 == DataAccess.executeSimpleQuery("CREATE DATABASE `" + name + "`;", new DBInterfaceRoot(true));
} catch (final SQLException | IOException ex) {
ex.printStackTrace();
LOGGER.error("Can not check if the DB exist!!! {}", ex.getMessage());
return false;
}
} }
public static boolean isTableExist(final String name, final QueryOption... option) public static boolean isTableExist(final String name, final QueryOption... option)
throws InternalServerErrorException { throws InternalServerErrorException {
final QueryOptions options = new QueryOptions(option);
try {
String request = "";
if ("sqlite".equals(ConfigBaseVariable.getDBType())) {
request = """
SELECT COUNT(*) AS total
FROM sqlite_master
WHERE type = 'table'
AND name = ?;
""";
// PreparedStatement ps = entry.connection.prepareStatement("show tables");
final DBEntry entry = DBInterfaceOption.getAutoEntry(options);
final PreparedStatement ps = entry.connection.prepareStatement(request);
ps.setString(1, name);
final ResultSet ret = ps.executeQuery();
final int count = ret.getInt("total");
return count == 1;
} else {
final DBEntry entry = DBInterfaceOption.getAutoEntry(options);
// TODO : Maybe connect with a temporary not specified connection interface to a db ...
final PreparedStatement ps = entry.connection.prepareStatement("show tables");
final ResultSet rs = ps.executeQuery();
// LOGGER.info("List all tables: equals? '{}'", name);
while (rs.next()) {
final String data = rs.getString(1);
// LOGGER.info(" - '{}'", data);
if (name.equals(data)) {
return true;
}
}
return false;
}
} catch (final SQLException ex) {
LOGGER.error("Can not check if the table exist SQL-error !!! {}", ex.getMessage());
} catch (final IOException ex) {
LOGGER.error("Can not check if the table exist!!! {}", ex.getMessage());
}
throw new InternalServerErrorException("Can Not manage the DB-access"); throw new InternalServerErrorException("Can Not manage the DB-access");
} }
/** Extract a list of Long with "-" separated element from a SQL input data.
* @param rs Result Set of the BDD
* @param iii Id in the result set
* @return The list of Long value
* @throws SQLException if an error is generated in the SQL request. */
public static List<Long> getListOfIds(final ResultSet rs, final int iii, final String separator)
throws SQLException {
final String trackString = rs.getString(iii);
if (rs.wasNull()) {
return null;
}
final List<Long> out = new ArrayList<>();
final String[] elements = trackString.split(separator);
for (final String elem : elements) {
final Long tmp = Long.parseLong(elem);
out.add(tmp);
}
return out;
}
/** Extract a list of UUID with "-" separated element from a SQL input data.
* @param rs Result Set of the BDD
* @param iii Id in the result set
* @return The list of Long value
* @throws SQLException if an error is generated in the SQL request. */
public static List<UUID> getListOfUUIDs(final ResultSet rs, final int iii, final String separator)
throws SQLException {
final String trackString = rs.getString(iii);
if (rs.wasNull()) {
return null;
}
final List<UUID> out = new ArrayList<>();
final String[] elements = trackString.split(separator);
for (final String elem : elements) {
final UUID tmp = UUID.fromString(elem);
out.add(tmp);
}
return out;
}
public static byte[][] splitIntoGroupsOf16Bytes(final byte[] input) {
final int inputLength = input.length;
final int numOfGroups = (inputLength + 15) / 16; // Calculate the number of groups needed
final byte[][] groups = new byte[numOfGroups][16];
for (int i = 0; i < numOfGroups; i++) {
final int startIndex = i * 16;
final int endIndex = Math.min(startIndex + 16, inputLength);
groups[i] = Arrays.copyOfRange(input, startIndex, endIndex);
}
return groups;
}
public static List<UUID> getListOfRawUUIDs(final ResultSet rs, final int iii)
throws SQLException, DataAccessException {
final byte[] trackString = rs.getBytes(iii);
if (rs.wasNull()) {
return null;
}
final byte[][] elements = splitIntoGroupsOf16Bytes(trackString);
final List<UUID> out = new ArrayList<>();
for (final byte[] elem : elements) {
final UUID tmp = UuidUtils.asUuid(elem);
out.add(tmp);
}
return out;
}
public static UUID getListOfRawUUID(final ResultSet rs, final int iii) throws SQLException, DataAccessException {
final byte[] elem = rs.getBytes(iii);
if (rs.wasNull()) {
return null;
}
return UuidUtils.asUuid(elem);
}
protected static <T> void setValuedb(
final Class<?> type,
final T data,
final CountInOut iii,
final Field field,
final PreparedStatement ps) throws Exception {
if (type == UUID.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.BINARY);
} else {
final byte[] dataByte = UuidUtils.asBytes((UUID) tmp);
ps.setBytes(iii.value, dataByte);
}
} else if (type == Long.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.BIGINT);
} else {
ps.setLong(iii.value, (Long) tmp);
}
} else if (type == long.class) {
ps.setLong(iii.value, field.getLong(data));
} else if (type == Integer.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
ps.setInt(iii.value, (Integer) tmp);
}
} else if (type == int.class) {
ps.setInt(iii.value, field.getInt(data));
} else if (type == Float.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.FLOAT);
} else {
ps.setFloat(iii.value, (Float) tmp);
}
} else if (type == float.class) {
ps.setFloat(iii.value, field.getFloat(data));
} else if (type == Double.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.DOUBLE);
} else {
ps.setDouble(iii.value, (Double) tmp);
}
} else if (type == Double.class) {
ps.setDouble(iii.value, field.getDouble(data));
} else if (type == Boolean.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
ps.setBoolean(iii.value, (Boolean) tmp);
}
} else if (type == boolean.class) {
ps.setBoolean(iii.value, field.getBoolean(data));
} else if (type == Timestamp.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
ps.setTimestamp(iii.value, (Timestamp) tmp);
}
} else if (type == Date.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
final Timestamp sqlDate = java.sql.Timestamp.from(((Date) tmp).toInstant());
ps.setTimestamp(iii.value, sqlDate);
}
} else if (type == Instant.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
final String sqlDate = ((Instant) tmp).toString();
ps.setString(iii.value, sqlDate);
}
} else if (type == LocalDate.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
final java.sql.Date sqlDate = java.sql.Date.valueOf((LocalDate) tmp);
ps.setDate(iii.value, sqlDate);
}
} else if (type == LocalTime.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
final java.sql.Time sqlDate = java.sql.Time.valueOf((LocalTime) tmp);
ps.setTime(iii.value, sqlDate);
}
} else if (type == String.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.VARCHAR);
} else {
ps.setString(iii.value, (String) tmp);
}
} else if (type.isEnum()) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.VARCHAR);
} else {
ps.setString(iii.value, tmp.toString());
}
} else {
throw new DataAccessException("Unknown Field Type");
}
iii.inc();
}
protected static <T> void setValueFromDb(
final Class<?> type,
final Object data,
final CountInOut count,
final Field field,
final ResultSet rs,
final CountInOut countNotNull) throws Exception {
if (type == UUID.class) {
final byte[] tmp = rs.getBytes(count.value);
// final UUID tmp = rs.getObject(count.value, UUID.class);
if (rs.wasNull()) {
field.set(data, null);
} else {
// field.set(data, tmp);
final UUID uuid = UuidUtils.asUuid(tmp);
field.set(data, uuid);
countNotNull.inc();
}
} else if (type == Long.class) {
final Long tmp = rs.getLong(count.value);
if (rs.wasNull()) {
field.set(data, null);
} else {
field.set(data, tmp);
countNotNull.inc();
}
} else if (type == long.class) {
final Long tmp = rs.getLong(count.value);
if (rs.wasNull()) {
// field.set(data, null);
} else {
field.setLong(data, tmp);
countNotNull.inc();
}
} else if (type == Integer.class) {
final Integer tmp = rs.getInt(count.value);
if (rs.wasNull()) {
field.set(data, null);
} else {
field.set(data, tmp);
countNotNull.inc();
}
} else if (type == int.class) {
final Integer tmp = rs.getInt(count.value);
if (rs.wasNull()) {
// field.set(data, null);
} else {
field.setInt(data, tmp);
countNotNull.inc();
}
} else if (type == Float.class) {
final Float tmp = rs.getFloat(count.value);
if (rs.wasNull()) {
field.set(data, null);
} else {
field.set(data, tmp);
countNotNull.inc();
}
} else if (type == float.class) {
final Float tmp = rs.getFloat(count.value);
if (rs.wasNull()) {
// field.set(data, null);
} else {
field.setFloat(data, tmp);
countNotNull.inc();
}
} else if (type == Double.class) {
final Double tmp = rs.getDouble(count.value);
if (rs.wasNull()) {
field.set(data, null);
} else {
field.set(data, tmp);
countNotNull.inc();
}
} else if (type == double.class) {
final Double tmp = rs.getDouble(count.value);
if (rs.wasNull()) {
// field.set(data, null);
} else {
field.setDouble(data, tmp);
countNotNull.inc();
}
} else if (type == Boolean.class) {
final Boolean tmp = rs.getBoolean(count.value);
if (rs.wasNull()) {
field.set(data, null);
} else {
field.set(data, tmp);
countNotNull.inc();
}
} else if (type == boolean.class) {
final Boolean tmp = rs.getBoolean(count.value);
if (rs.wasNull()) {
// field.set(data, null);
} else {
field.setBoolean(data, tmp);
countNotNull.inc();
}
} else if (type == Timestamp.class) {
final Timestamp tmp = rs.getTimestamp(count.value);
if (rs.wasNull()) {
field.set(data, null);
} else {
field.set(data, tmp);
countNotNull.inc();
}
} else if (type == Date.class) {
try {
final Timestamp tmp = rs.getTimestamp(count.value);
if (rs.wasNull()) {
field.set(data, null);
} else {
field.set(data, Date.from(tmp.toInstant()));
countNotNull.inc();
}
} catch (final SQLException ex) {
final String tmp = rs.getString(count.value);
LOGGER.error("Fail to parse the SQL time !!! {}", tmp);
if (rs.wasNull()) {
field.set(data, null);
} else {
final Date date = DateTools.parseDate(tmp);
LOGGER.error("Fail to parse the SQL time !!! {}", date);
field.set(data, date);
countNotNull.inc();
}
}
} else if (type == Instant.class) {
final String tmp = rs.getString(count.value);
if (rs.wasNull()) {
field.set(data, null);
} else {
field.set(data, Instant.parse(tmp));
countNotNull.inc();
}
} else if (type == LocalDate.class) {
final java.sql.Date tmp = rs.getDate(count.value);
if (rs.wasNull()) {
field.set(data, null);
} else {
field.set(data, tmp.toLocalDate());
countNotNull.inc();
}
} else if (type == LocalTime.class) {
final java.sql.Time tmp = rs.getTime(count.value);
if (rs.wasNull()) {
field.set(data, null);
} else {
field.set(data, tmp.toLocalTime());
countNotNull.inc();
}
} else if (type == String.class) {
final String tmp = rs.getString(count.value);
if (rs.wasNull()) {
field.set(data, null);
} else {
field.set(data, tmp);
countNotNull.inc();
}
} else if (type.isEnum()) {
final String tmp = rs.getString(count.value);
if (rs.wasNull()) {
field.set(data, null);
} else {
boolean find = false;
final Object[] arr = type.getEnumConstants();
for (final Object elem : arr) {
if (elem.toString().equals(tmp)) {
field.set(data, elem);
countNotNull.inc();
find = true;
break;
}
}
if (!find) {
throw new DataAccessException("Enum value does not exist in the Model: '" + tmp + "'");
}
}
} else {
throw new DataAccessException("Unknown Field Type");
}
count.inc();
}
// TODO: this function will replace the previous one !!!
protected static RetreiveFromDB createSetValueFromDbCallback(final int count, final Field field) throws Exception {
final Class<?> type = field.getType();
if (type == UUID.class) {
return (final ResultSet rs, final Object obj) -> {
final byte[] tmp = rs.getBytes(count);
// final UUID tmp = rs.getObject(count, UUID.class);
if (rs.wasNull()) {
field.set(obj, null);
} else {
// field.set(obj, tmp);
final UUID uuid = UuidUtils.asUuid(tmp);
field.set(obj, uuid);
}
};
}
if (type == Long.class) {
return (final ResultSet rs, final Object obj) -> {
final Long tmp = rs.getLong(count);
if (rs.wasNull()) {
field.set(obj, null);
} else {
field.set(obj, tmp);
}
};
}
if (type == long.class) {
return (final ResultSet rs, final Object obj) -> {
final Long tmp = rs.getLong(count);
if (rs.wasNull()) {
// field.set(data, null);
} else {
field.setLong(obj, tmp);
}
};
}
if (type == Integer.class) {
return (final ResultSet rs, final Object obj) -> {
final Integer tmp = rs.getInt(count);
if (rs.wasNull()) {
field.set(obj, null);
} else {
field.set(obj, tmp);
}
};
}
if (type == int.class) {
return (final ResultSet rs, final Object obj) -> {
final Integer tmp = rs.getInt(count);
if (rs.wasNull()) {
// field.set(obj, null);
} else {
field.setInt(obj, tmp);
}
};
}
if (type == Float.class) {
return (final ResultSet rs, final Object obj) -> {
final Float tmp = rs.getFloat(count);
if (rs.wasNull()) {
field.set(obj, null);
} else {
field.set(obj, tmp);
}
};
}
if (type == float.class) {
return (final ResultSet rs, final Object obj) -> {
final Float tmp = rs.getFloat(count);
if (rs.wasNull()) {
// field.set(obj, null);
} else {
field.setFloat(obj, tmp);
}
};
}
if (type == Double.class) {
return (final ResultSet rs, final Object obj) -> {
final Double tmp = rs.getDouble(count);
if (rs.wasNull()) {
field.set(obj, null);
} else {
field.set(obj, tmp);
}
};
}
if (type == double.class) {
return (final ResultSet rs, final Object obj) -> {
final Double tmp = rs.getDouble(count);
if (rs.wasNull()) {
// field.set(obj, null);
} else {
field.setDouble(obj, tmp);
}
};
}
if (type == Boolean.class) {
return (final ResultSet rs, final Object obj) -> {
final Boolean tmp = rs.getBoolean(count);
if (rs.wasNull()) {
field.set(obj, null);
} else {
field.set(obj, tmp);
}
};
}
if (type == boolean.class) {
return (final ResultSet rs, final Object obj) -> {
final Boolean tmp = rs.getBoolean(count);
if (rs.wasNull()) {
// field.set(obj, null);
} else {
field.setBoolean(obj, tmp);
}
};
}
if (type == Timestamp.class) {
return (final ResultSet rs, final Object obj) -> {
final Timestamp tmp = rs.getTimestamp(count);
if (rs.wasNull()) {
field.set(obj, null);
} else {
field.set(obj, tmp);
}
};
}
if (type == Date.class) {
return (final ResultSet rs, final Object obj) -> {
try {
final Timestamp tmp = rs.getTimestamp(count);
if (rs.wasNull()) {
field.set(obj, null);
} else {
field.set(obj, Date.from(tmp.toInstant()));
}
} catch (final SQLException ex) {
final String tmp = rs.getString(count);
LOGGER.error("Fail to parse the SQL time !!! {}", tmp);
if (rs.wasNull()) {
field.set(obj, null);
} else {
final Date date = DateTools.parseDate(tmp);
LOGGER.error("Fail to parse the SQL time !!! {}", date);
field.set(obj, date);
}
}
};
}
if (type == Instant.class) {
return (final ResultSet rs, final Object obj) -> {
final String tmp = rs.getString(count);
if (rs.wasNull()) {
field.set(obj, null);
} else {
field.set(obj, Instant.parse(tmp));
}
};
}
if (type == LocalDate.class) {
return (final ResultSet rs, final Object obj) -> {
final java.sql.Date tmp = rs.getDate(count);
if (rs.wasNull()) {
field.set(obj, null);
} else {
field.set(obj, tmp.toLocalDate());
}
};
}
if (type == LocalTime.class) {
return (final ResultSet rs, final Object obj) -> {
final java.sql.Time tmp = rs.getTime(count);
if (rs.wasNull()) {
field.set(obj, null);
} else {
field.set(obj, tmp.toLocalTime());
}
};
}
if (type == String.class) {
return (final ResultSet rs, final Object obj) -> {
final String tmp = rs.getString(count);
if (rs.wasNull()) {
field.set(obj, null);
} else {
field.set(obj, tmp);
}
};
}
if (type.isEnum()) {
return (final ResultSet rs, final Object obj) -> {
final String tmp = rs.getString(count);
if (rs.wasNull()) {
field.set(obj, null);
} else {
boolean find = false;
final Object[] arr = type.getEnumConstants();
for (final Object elem : arr) {
if (elem.toString().equals(tmp)) {
field.set(obj, elem);
find = true;
break;
}
}
if (!find) {
throw new DataAccessException("Enum value does not exist in the Model: '" + tmp + "'");
}
}
};
}
throw new DataAccessException("Unknown Field Type");
}
public static boolean isAddOnField(final Field field) {
return findAddOnforField(field) != null;
}
public static DataAccessAddOn findAddOnforField(final Field field) {
for (final DataAccessAddOn elem : addOn) {
if (elem.isCompatibleField(field)) {
return elem;
}
}
return null;
}
// TODO: manage insert batch... // TODO: manage insert batch...
public static <T> List<T> insertMultiple(final List<T> data, final QueryOption... options) throws Exception { public static <T> List<T> insertMultiple(final List<T> data, final QueryOption... options) throws Exception {
final List<T> out = new ArrayList<>(); final List<T> out = new ArrayList<>();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,10 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.bson.conversions.Bson;
import com.mongodb.client.model.Filters;
public class QueryAnd implements QueryItem { public class QueryAnd implements QueryItem {
protected final List<QueryItem> childs; protected final List<QueryItem> childs;
@ -51,4 +55,13 @@ public class QueryAnd implements QueryItem {
public int size() { public int size() {
return this.childs.size(); return this.childs.size();
} }
@Override
public void generateFilter(final List<Bson> filters) {
final List<Bson> filtersLocal = new ArrayList<>();
for (final QueryItem elem : this.childs) {
elem.generateFilter(filtersLocal);
}
filters.add(Filters.and(filtersLocal.toArray(new Bson[0])));
}
} }

View File

@ -1,8 +1,16 @@
package org.kar.archidata.dataAccess; package org.kar.archidata.dataAccess;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.util.List;
import org.bson.conversions.Bson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mongodb.client.model.Filters;
public class QueryCondition implements QueryItem { public class QueryCondition implements QueryItem {
static final Logger LOGGER = LoggerFactory.getLogger(DataAccess.class);
private final String key; private final String key;
private final String comparator; private final String comparator;
private final Object value; private final Object value;
@ -36,4 +44,24 @@ public class QueryCondition implements QueryItem {
DataAccess.addElement(ps, this.value, iii); DataAccess.addElement(ps, this.value, iii);
iii.inc(); iii.inc();
} }
@Override
public void generateFilter(final List<Bson> filters) {
if ("=".equals(this.comparator)) {
filters.add(Filters.eq(this.key, this.value));
} else if ("!=".equals(this.comparator)) {
filters.add(Filters.ne(this.key, this.value));
} else if (">".equals(this.comparator)) {
filters.add(Filters.gt(this.key, this.value));
} else if (">=".equals(this.comparator)) {
filters.add(Filters.gte(this.key, this.value));
} else if ("<".equals(this.comparator)) {
filters.add(Filters.lt(this.key, this.value));
} else if ("<=".equals(this.comparator)) {
filters.add(Filters.lte(this.key, this.value));
} else {
LOGGER.error("Not manage comparison: '{}'", this.key);
}
}
} }

View File

@ -3,6 +3,10 @@ package org.kar.archidata.dataAccess;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.util.List; import java.util.List;
import org.bson.conversions.Bson;
import com.mongodb.client.model.Filters;
public class QueryInList<T> implements QueryItem { public class QueryInList<T> implements QueryItem {
protected final String key; protected final String key;
protected final String comparator; protected final String comparator;
@ -50,4 +54,9 @@ public class QueryInList<T> implements QueryItem {
iii.inc(); iii.inc();
} }
} }
@Override
public void generateFilter(final List<Bson> filters) {
filters.add(Filters.in(this.key, this.value));
}
} }

View File

@ -1,9 +1,17 @@
package org.kar.archidata.dataAccess; package org.kar.archidata.dataAccess;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.util.List;
import org.bson.conversions.Bson;
public interface QueryItem { public interface QueryItem {
// For SQL mode query construction
void generateQuery(StringBuilder query, String tableName); void generateQuery(StringBuilder query, String tableName);
// For SQL mode query injection
void injectQuery(PreparedStatement ps, CountInOut iii) throws Exception; void injectQuery(PreparedStatement ps, CountInOut iii) throws Exception;
// For No-SQL mode filter creation
void generateFilter(List<Bson> filters);
} }

View File

@ -1,6 +1,11 @@
package org.kar.archidata.dataAccess; package org.kar.archidata.dataAccess;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.util.List;
import org.bson.conversions.Bson;
import com.mongodb.client.model.Filters;
public class QueryNotNull implements QueryItem { public class QueryNotNull implements QueryItem {
private final String key; private final String key;
@ -21,4 +26,9 @@ public class QueryNotNull implements QueryItem {
@Override @Override
public void injectQuery(final PreparedStatement ps, final CountInOut iii) throws Exception {} public void injectQuery(final PreparedStatement ps, final CountInOut iii) throws Exception {}
@Override
public void generateFilter(final List<Bson> filters) {
filters.add(Filters.exists(this.key));
}
} }

View File

@ -1,6 +1,11 @@
package org.kar.archidata.dataAccess; package org.kar.archidata.dataAccess;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.util.List;
import org.bson.conversions.Bson;
import com.mongodb.client.model.Filters;
public class QueryNull implements QueryItem { public class QueryNull implements QueryItem {
private final String key; private final String key;
@ -21,4 +26,10 @@ public class QueryNull implements QueryItem {
@Override @Override
public void injectQuery(final PreparedStatement ps, final CountInOut iii) throws Exception {} public void injectQuery(final PreparedStatement ps, final CountInOut iii) throws Exception {}
@Override
public void generateFilter(final List<Bson> filters) {
// Not sure of the result ... maybe check it ...
filters.add(Filters.eq(this.key, null));
}
} }

View File

@ -1,8 +1,13 @@
package org.kar.archidata.dataAccess; package org.kar.archidata.dataAccess;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.bson.conversions.Bson;
import com.mongodb.client.model.Filters;
public class QueryOr implements QueryItem { public class QueryOr implements QueryItem {
protected final List<QueryItem> childs; protected final List<QueryItem> childs;
@ -39,4 +44,13 @@ public class QueryOr implements QueryItem {
elem.injectQuery(ps, iii); elem.injectQuery(ps, iii);
} }
} }
@Override
public void generateFilter(final List<Bson> filters) {
final List<Bson> filtersLocal = new ArrayList<>();
for (final QueryItem elem : this.childs) {
elem.generateFilter(filtersLocal);
}
filters.add(Filters.or(filtersLocal.toArray(new Bson[0])));
}
} }

View File

@ -345,6 +345,14 @@ public class CheckJPA<T> implements CheckFunctionInterface {
} }
} }
public void check(final String baseName, final Object data) throws Exception {
check(baseName, data, null, null);
}
public void check(final String baseName, final Object data, final List<String> filterValue) throws Exception {
check(baseName, data, filterValue, null);
}
@Override @Override
public void check( public void check(
final String baseName, final String baseName,

View File

@ -1,11 +1,16 @@
package org.kar.archidata.dataAccess.options; package org.kar.archidata.dataAccess.options;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.List;
import org.bson.conversions.Bson;
import org.kar.archidata.dataAccess.CountInOut; import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.QueryItem; import org.kar.archidata.dataAccess.QueryItem;
import org.kar.archidata.dataAccess.QueryOptions; import org.kar.archidata.dataAccess.QueryOptions;
import com.mongodb.client.model.Filters;
/** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */ /** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */
public class Condition extends QueryOption { public class Condition extends QueryOption {
public final QueryItem condition; public final QueryItem condition;
@ -62,4 +67,26 @@ public class Condition extends QueryOption {
} }
query.append("\n"); query.append("\n");
} }
public Bson getFilter(final String collectionName, final QueryOptions options, final String deletedFieldName) {
boolean exclude_deleted = true;
if (options != null) {
exclude_deleted = !options.exist(AccessDeletedItems.class);
}
final List<Bson> filter = new ArrayList<>();
if (exclude_deleted && deletedFieldName != null) {
filter.add(Filters.ne(deletedFieldName, false));
}
// Check if we have a condition to generate
if (this.condition != null) {
this.condition.generateFilter(filter);
}
if (filter.size() == 0) {
return null;
}
if (filter.size() == 1) {
return filter.get(0);
}
return Filters.and(filter.toArray(new Bson[0]));
}
} }

View File

@ -20,4 +20,8 @@ public class Limit extends QueryOption {
DataAccess.addElement(ps, this.limit, iii); DataAccess.addElement(ps, this.limit, iii);
iii.inc(); iii.inc();
} }
public long getValue() {
return this.limit;
}
} }

View File

@ -3,7 +3,9 @@ package org.kar.archidata.dataAccess.options;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.util.List; import java.util.List;
import org.bson.Document;
import org.kar.archidata.dataAccess.CountInOut; import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.options.OrderItem.Order;
public class OrderBy extends QueryOption { public class OrderBy extends QueryOption {
protected final List<OrderItem> childs; protected final List<OrderItem> childs;
@ -40,4 +42,10 @@ public class OrderBy extends QueryOption {
public void injectQuery(final PreparedStatement ps, final CountInOut iii) throws Exception { public void injectQuery(final PreparedStatement ps, final CountInOut iii) throws Exception {
// nothing to add. // nothing to add.
} }
public void generateSort(final Document data) {
for (final OrderItem elem : this.childs) {
data.append(elem.value, elem.order == Order.ASC ? 1 : -1);
}
}
} }

View File

@ -1,6 +1,7 @@
package org.kar.archidata.db; package org.kar.archidata.db;
import org.kar.archidata.dataAccess.DataAccess; import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.exception.DataAccessException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -15,10 +16,13 @@ public class DBConfig {
private final boolean keepConnected; private final boolean keepConnected;
public DBConfig(final String type, final String hostname, final Integer port, final String login, public DBConfig(final String type, final String hostname, final Integer port, final String login,
final String password, final String dbName, final boolean keepConnected) { final String password, final String dbName, final boolean keepConnected) throws DataAccessException {
if (type == null) { if (type == null) {
this.type = "mysql"; this.type = "mysql";
} else { } else {
if (!"mysql".equals(type) && !"sqlite".equals(type) && !"mongo".equals(type)) {
throw new DataAccessException("unexpected DB type: '" + type + "'");
}
this.type = type; this.type = type;
} }
if (hostname == null) { if (hostname == null) {
@ -27,7 +31,11 @@ public class DBConfig {
this.hostname = hostname; this.hostname = hostname;
} }
if (port == null) { if (port == null) {
this.port = 3306; if ("mysql".equals(this.type)) {
this.port = 3306;
} else {
this.port = 27017;
}
} else { } else {
this.port = port; this.port = port;
} }
@ -35,6 +43,7 @@ public class DBConfig {
this.password = password; this.password = password;
this.dbName = dbName; this.dbName = dbName;
this.keepConnected = keepConnected; this.keepConnected = keepConnected;
} }
@Override @Override
@ -82,11 +91,17 @@ public class DBConfig {
} }
return "jdbc:sqlite:" + this.hostname + ".db"; return "jdbc:sqlite:" + this.hostname + ".db";
} }
if (isRoot) { if ("mongo".equals(this.type)) {
return "jdbc:" + this.type + "://" + this.hostname + ":" + this.port return "mongodb:" + getLogin() + ":" + getPassword() + "//" + this.hostname + ":" + this.port;
+ "/?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC";
} }
return "jdbc:" + this.type + "://" + this.hostname + ":" + this.port + "/" + this.dbName if ("mysql".equals(this.type)) {
+ "?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC"; if (isRoot) {
return "jdbc:" + this.type + "://" + this.hostname + ":" + this.port
+ "/?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC";
}
return "jdbc:" + this.type + "://" + this.hostname + ":" + this.port + "/" + this.dbName
+ "?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC";
}
return "dead_code";
} }
} }

View File

@ -1,95 +0,0 @@
package org.kar.archidata.db;
import java.io.Closeable;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DBEntry implements Closeable {
final static Logger LOGGER = LoggerFactory.getLogger(DBEntry.class);
public DBConfig config;
public Connection connection;
private static List<DBEntry> stored = new ArrayList<>();
private DBEntry(final DBConfig config, final boolean root) throws IOException {
this.config = config;
if (root) {
connectRoot();
} else {
connect();
}
}
public static DBEntry createInterface(final DBConfig config) throws IOException {
return createInterface(config, false);
}
public static DBEntry createInterface(final DBConfig config, final boolean root) throws IOException {
if (config.getKeepConnected()) {
for (final DBEntry elem : stored) {
if (elem == null) {
continue;
}
if (elem.config.getUrl().equals(config.getUrl())) {
return elem;
}
}
final DBEntry tmp = new DBEntry(config, root);
stored.add(tmp);
return tmp;
} else {
return new DBEntry(config, root);
}
}
public void connectRoot() throws IOException {
try {
this.connection = DriverManager.getConnection(this.config.getUrl(true), this.config.getLogin(),
this.config.getPassword());
} catch (final SQLException ex) {
throw new IOException("Connection db fail: " + ex.getMessage() + " On URL: " + this.config.getUrl(true));
}
}
public void connect() throws IOException {
try {
this.connection = DriverManager.getConnection(this.config.getUrl(), this.config.getLogin(),
this.config.getPassword());
} catch (final SQLException ex) {
LOGGER.error("Connection db fail: " + ex.getMessage() + " On URL: " + this.config.getUrl(true));
throw new IOException("Connection db fail: " + ex.getMessage() + " On URL: " + this.config.getUrl(true));
}
}
@Override
public void close() throws IOException {
if (this.config.getKeepConnected()) {
return;
}
closeForce();
}
public void closeForce() throws IOException {
try {
// connection.commit();
this.connection.close();
} catch (final SQLException ex) {
throw new IOException("Dis-connection db fail: " + ex.getMessage());
}
}
public static void closeAllForceMode() throws IOException {
for (final DBEntry entry : stored) {
entry.closeForce();
}
stored = new ArrayList<>();
}
}

View File

@ -0,0 +1,3 @@
package org.kar.archidata.db;
public class DbInterface {}

View File

@ -0,0 +1,68 @@
package org.kar.archidata.db;
import java.io.Closeable;
import java.io.IOException;
import org.bson.UuidRepresentation;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.pojo.PojoCodecProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import dev.morphia.Datastore;
import dev.morphia.Morphia;
public class DbInterfaceMorphia extends DbInterface implements Closeable {
final static Logger LOGGER = LoggerFactory.getLogger(DbInterfaceMorphia.class);
private final MongoClient mongoClient;
private final Datastore datastore;
public DbInterfaceMorphia(final String dbUrl, final String dbName, final Class<?>... classes) {
// Connect to MongoDB (simple form):
// final MongoClient mongoClient = MongoClients.create(dbUrl);
// Connect to MongoDB (complex form):
final ConnectionString connectionString = new ConnectionString(dbUrl);
// Créer un CodecRegistry pour UUID
//final CodecRegistry uuidCodecRegistry = CodecRegistries.fromCodecs(new UUIDCodec());
// Créer un CodecRegistry pour POJOs
final CodecRegistry pojoCodecRegistry = CodecRegistries
.fromProviders(PojoCodecProvider.builder().automatic(true).build());
// Ajouter le CodecRegistry par défaut, le codec UUID et celui pour POJOs
//final CodecRegistry codecRegistry = CodecRegistries.fromRegistries(
// MongoClientSettings.getDefaultCodecRegistry(), /*uuidCodecRegistry, */ pojoCodecRegistry);
final CodecRegistry codecRegistry = CodecRegistries.fromRegistries(
MongoClientSettings.getDefaultCodecRegistry(),
CodecRegistries.fromCodecs(new org.bson.codecs.UuidCodec(UuidRepresentation.STANDARD)),
pojoCodecRegistry);
// Configurer MongoClientSettings
final MongoClientSettings clientSettings = MongoClientSettings.builder() //
.applyConnectionString(connectionString)//
.codecRegistry(codecRegistry) //
.uuidRepresentation(UuidRepresentation.STANDARD)//
.build();
this.mongoClient = MongoClients.create(clientSettings);
this.datastore = Morphia.createDatastore(this.mongoClient, "karusic");
// Map entities
this.datastore.getMapper().map(classes);
// Ensure indexes
this.datastore.ensureIndexes();
}
public Datastore getDatastore() {
return this.datastore;
}
@Override
public void close() throws IOException {
this.mongoClient.close();
}
}

View File

@ -0,0 +1,42 @@
package org.kar.archidata.db;
import java.io.Closeable;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DbInterfaceSQL extends DbInterface implements Closeable {
final static Logger LOGGER = LoggerFactory.getLogger(DbInterfaceSQL.class);
private final Connection connection;
public DbInterfaceSQL(final DBConfig config, final String dbName, final Class<?>... classes) throws IOException {
this(config.getUrl(), config.getLogin(), config.getPassword());
}
public DbInterfaceSQL(final String dbUrl, final String login, final String password) throws IOException {
try {
this.connection = DriverManager.getConnection(dbUrl, login, password);
} catch (final SQLException ex) {
LOGGER.error("Connection db fail: " + ex.getMessage() + " On URL: " + dbUrl);
throw new IOException("Connection db fail: " + ex.getMessage() + " On URL: " + dbUrl);
}
}
public Connection getConnection() {
return this.connection;
}
@Override
public void close() throws IOException {
try {
this.connection.close();
} catch (final SQLException ex) {
throw new IOException("Dis-connection db fail: " + ex.getMessage());
}
}
}

View File

@ -10,6 +10,7 @@ import jakarta.ws.rs.DefaultValue;
public class UUIDGenericData extends GenericTiming { public class UUIDGenericData extends GenericTiming {
@Id @Id
@DefaultValue("(UUID_TO_BIN(UUID(), TRUE))") @DefaultValue("(UUID_TO_BIN(UUID(), TRUE))")
@Column(nullable = false, unique = true) @Column(nullable = false, unique = true)
@Schema(description = "Unique UUID of the object", required = false, readOnly = true, example = "e6b33c1c-d24d-11ee-b616-02420a030102") @Schema(description = "Unique UUID of the object", required = false, readOnly = true, example = "e6b33c1c-d24d-11ee-b616-02420a030102")