[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.LoggerFactory;
import dev.morphia.annotations.Entity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column;
@ -31,6 +32,7 @@ import jakarta.ws.rs.DefaultValue;
public class AnnotationTools {
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 {
if (options != null) {
final List<OverrideTableName> data = options.get(OverrideTableName.class);
@ -41,16 +43,13 @@ public class AnnotationTools {
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);
if (annotation.length == 0) {
// when no annotation is detected, then the table name is the class name
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();
if (tmp == null) {
return element.getSimpleName();
@ -58,6 +57,31 @@ public class AnnotationTools {
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 {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class);
if (annotation.length == 0) {

View File

@ -9,12 +9,9 @@ import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@ -22,24 +19,19 @@ import java.util.UUID;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.CreationTimestamp;
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.Condition;
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.GroupBy;
import org.kar.archidata.dataAccess.options.Limit;
import org.kar.archidata.dataAccess.options.OrderBy;
import org.kar.archidata.dataAccess.options.QueryOption;
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.tools.ConfigBaseVariable;
import org.kar.archidata.tools.DateTools;
import org.kar.archidata.tools.UuidUtils;
import org.slf4j.Logger;
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
* back-end. */
public class DataAccess {
public abstract 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 {
addOn.add(new AddOnManyToMany());
addOn.add(new AddOnManyToOne());
addOn.add(new AddOnOneToMany());
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() {
static public final DataAccess greateInterface(final DbInterface io) {
if (io instanceof final DbInterfaceMorphia ioMorphia) {
return DataAccessMorphia(ioMorphia);
} else if (io instanceof final DbInterfaceSQL ioSQL) {
}
}
public static boolean isDBExist(final String name, final QueryOption... option)
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");
}
public static boolean createDB(final String name) {
if ("sqlite".equals(ConfigBaseVariable.getDBType())) {
// 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;
}
throw new InternalServerErrorException("Can Not manage the DB-access");
}
public static boolean isTableExist(final String name, final QueryOption... option)
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");
}
/** 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...
public static <T> List<T> insertMultiple(final List<T> data, final QueryOption... options) throws Exception {
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.List;
import org.bson.conversions.Bson;
import com.mongodb.client.model.Filters;
public class QueryAnd implements QueryItem {
protected final List<QueryItem> childs;
@ -51,4 +55,13 @@ public class QueryAnd implements QueryItem {
public int 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;
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 {
static final Logger LOGGER = LoggerFactory.getLogger(DataAccess.class);
private final String key;
private final String comparator;
private final Object value;
@ -36,4 +44,24 @@ public class QueryCondition implements QueryItem {
DataAccess.addElement(ps, this.value, iii);
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.util.List;
import org.bson.conversions.Bson;
import com.mongodb.client.model.Filters;
public class QueryInList<T> implements QueryItem {
protected final String key;
protected final String comparator;
@ -50,4 +54,9 @@ public class QueryInList<T> implements QueryItem {
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;
import java.sql.PreparedStatement;
import java.util.List;
import org.bson.conversions.Bson;
public interface QueryItem {
// For SQL mode query construction
void generateQuery(StringBuilder query, String tableName);
// For SQL mode query injection
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;
import java.sql.PreparedStatement;
import java.util.List;
import org.bson.conversions.Bson;
import com.mongodb.client.model.Filters;
public class QueryNotNull implements QueryItem {
private final String key;
@ -21,4 +26,9 @@ public class QueryNotNull implements QueryItem {
@Override
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;
import java.sql.PreparedStatement;
import java.util.List;
import org.bson.conversions.Bson;
import com.mongodb.client.model.Filters;
public class QueryNull implements QueryItem {
private final String key;
@ -21,4 +26,10 @@ public class QueryNull implements QueryItem {
@Override
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;
import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.List;
import org.bson.conversions.Bson;
import com.mongodb.client.model.Filters;
public class QueryOr implements QueryItem {
protected final List<QueryItem> childs;
@ -39,4 +44,13 @@ public class QueryOr implements QueryItem {
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
public void check(
final String baseName,

View File

@ -1,11 +1,16 @@
package org.kar.archidata.dataAccess.options;
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.QueryItem;
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. */
public class Condition extends QueryOption {
public final QueryItem condition;
@ -62,4 +67,26 @@ public class Condition extends QueryOption {
}
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);
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.util.List;
import org.bson.Document;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.options.OrderItem.Order;
public class OrderBy extends QueryOption {
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 {
// 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;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.exception.DataAccessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -15,10 +16,13 @@ public class DBConfig {
private final boolean keepConnected;
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) {
this.type = "mysql";
} else {
if (!"mysql".equals(type) && !"sqlite".equals(type) && !"mongo".equals(type)) {
throw new DataAccessException("unexpected DB type: '" + type + "'");
}
this.type = type;
}
if (hostname == null) {
@ -27,7 +31,11 @@ public class DBConfig {
this.hostname = hostname;
}
if (port == null) {
this.port = 3306;
if ("mysql".equals(this.type)) {
this.port = 3306;
} else {
this.port = 27017;
}
} else {
this.port = port;
}
@ -35,6 +43,7 @@ public class DBConfig {
this.password = password;
this.dbName = dbName;
this.keepConnected = keepConnected;
}
@Override
@ -82,11 +91,17 @@ public class DBConfig {
}
return "jdbc:sqlite:" + this.hostname + ".db";
}
if (isRoot) {
return "jdbc:" + this.type + "://" + this.hostname + ":" + this.port
+ "/?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC";
if ("mongo".equals(this.type)) {
return "mongodb:" + getLogin() + ":" + getPassword() + "//" + this.hostname + ":" + this.port;
}
return "jdbc:" + this.type + "://" + this.hostname + ":" + this.port + "/" + this.dbName
+ "?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC";
if ("mysql".equals(this.type)) {
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 {
@Id
@DefaultValue("(UUID_TO_BIN(UUID(), TRUE))")
@Column(nullable = false, unique = true)
@Schema(description = "Unique UUID of the object", required = false, readOnly = true, example = "e6b33c1c-d24d-11ee-b616-02420a030102")