Debug, Correction de bug, Classe Log, affichage des distances amélioré

- Ajout d'un mode debuggage utilisé par l'ORM pour afficher les requêtes envoyés au serveur BDD
- Corrections de nombreux bugs présent dans le nouvel ORM et dans son utilisation
- Meilleure affichage des distances : unité m et km choisi selon distance
- Ajout d'une classe Log qui permet de simplifier l'utilisation du logger
This commit is contained in:
Marc Baloup 2016-07-14 01:46:44 +02:00
parent b2a19e09c1
commit c5af1bd213
11 changed files with 228 additions and 56 deletions

View File

@ -50,6 +50,25 @@ public class EnumUtil {
return null; return null;
} }
/**
* Permet de rechercher l'existance d'un élément dans un enum, de façon insensible à la casse
* La validité de la classe passé en premier paramètre est vérifiée dynamiquement et non
* statiquement. Préférez l'utilisation de {@link #searchEnum(Class, String)} quand c'est possible.
* @param enumType la classe correpondant à l'enum à lister
* @param search l'élément à rechercher, insensible à la casse
* @return l'élément de l'énumération, si elle a été trouvée et si la classe passée en paramètre est un enum, null dans les autres cas
*/
public static Enum<?> searchUncheckedEnum(Class<?> enumType, String search) {
if (!enumType.isEnum())
return null;
Enum<?>[] elements = (Enum<?>[]) enumType.getEnumConstants();
for (Enum<?> el : elements)
if (el.name().equalsIgnoreCase(search))
return el;
return null;
}

View File

@ -1,11 +1,22 @@
package fr.pandacube.java.util; package fr.pandacube.java.util;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
public class Log { public class Log {
private static Logger logger; private static Logger logger;
private static AtomicBoolean logDebug = new AtomicBoolean(false);
public static void setDebugState(boolean newVal) {
logDebug.set(newVal);
}
public static boolean getDebugState() {
return logDebug.get();
}
public static Logger getLogger() { public static Logger getLogger() {
return logger; return logger;
@ -42,5 +53,20 @@ public class Log {
public static void severe(String message) { public static void severe(String message) {
logger.severe(message); logger.severe(message);
} }
public static void debug(String message, Throwable t) {
if (!logDebug.get()) return;
logger.log(Level.INFO, message, t);
}
public static void debug(Throwable t) {
if (!logDebug.get()) return;
logger.log(Level.INFO, "", t);
}
public static void debug(String message) {
if (!logDebug.get()) return;
logger.info(message);
}
} }

View File

@ -1,25 +0,0 @@
package fr.pandacube.java.util;
import java.text.DecimalFormat;
public class MemoryUtil {
public static String humanReadableSize(long octet)
{
DecimalFormat format = new DecimalFormat("#####0.00");
double size = octet;
if (size < 1024)
return size+"o";
size /= 1024;
if (size < 1024)
return format.format(size)+"kio";
size /= 1024;
if (size < 1024)
return format.format(size)+"Mio";
size /= 1024;
if (size < 1024)
return format.format(size)+"Gio";
size /= 1024;
return format.format(size)+"Tio";
}
}

View File

@ -4,6 +4,7 @@ import java.sql.Date;
import java.util.UUID; import java.util.UUID;
import fr.pandacube.java.util.db2.sql_tools.ORM; import fr.pandacube.java.util.db2.sql_tools.ORM;
import fr.pandacube.java.util.db2.sql_tools.ORMException;
import fr.pandacube.java.util.db2.sql_tools.SQLElement; import fr.pandacube.java.util.db2.sql_tools.SQLElement;
import fr.pandacube.java.util.db2.sql_tools.SQLField; import fr.pandacube.java.util.db2.sql_tools.SQLField;
import fr.pandacube.java.util.db2.sql_tools.SQLType; import fr.pandacube.java.util.db2.sql_tools.SQLType;
@ -90,7 +91,7 @@ public class SQLPlayer extends SQLElement {
public static SQLPlayer getPlayerFromUUID(UUID playerId) throws Exception { public static SQLPlayer getPlayerFromUUID(UUID playerId) throws ORMException {
return ORM.getFirst(SQLPlayer.class, return ORM.getFirst(SQLPlayer.class,
new SQLWhereComp(SQLPlayer.playerId, SQLComparator.EQ, playerId.toString()), new SQLWhereComp(SQLPlayer.playerId, SQLComparator.EQ, playerId.toString()),
null); null);

View File

@ -8,6 +8,7 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import fr.pandacube.java.util.EnumUtil;
import fr.pandacube.java.util.Log; import fr.pandacube.java.util.Log;
import fr.pandacube.java.util.db2.SQLContact; import fr.pandacube.java.util.db2.SQLContact;
import fr.pandacube.java.util.db2.SQLForumCategorie; import fr.pandacube.java.util.db2.SQLForumCategorie;
@ -127,7 +128,7 @@ public final class ORM {
ps.setObject(i++, val); ps.setObject(i++, val);
} }
try { try {
System.out.println(ps.toString()); Log.info("Creating table "+elem.tableName()+":\n"+ps.toString());
ps.executeUpdate(); ps.executeUpdate();
} finally { } finally {
ps.close(); ps.close();
@ -222,11 +223,11 @@ public final class ORM {
int i = 1; int i = 1;
for (Object val : params) { for (Object val : params) {
if (val instanceof Enum<?>)
val = ((Enum<?>)val).name();
ps.setObject(i++, val); ps.setObject(i++, val);
} }
Log.debug(ps.toString());
System.out.println(ps.toString());
ResultSet set = ps.executeQuery(); ResultSet set = ps.executeQuery();
try { try {
@ -259,7 +260,7 @@ public final class ORM {
private static <T extends SQLElement> T getElementInstance(ResultSet set, Class<T> elemClass) throws ReflectiveOperationException, SQLException { private static <T extends SQLElement> T getElementInstance(ResultSet set, Class<T> elemClass) throws ORMException {
try { try {
T instance = elemClass.getConstructor(int.class).newInstance(set.getInt("id")); T instance = elemClass.getConstructor(int.class).newInstance(set.getInt("id"));
@ -271,12 +272,37 @@ public final class ORM {
continue; // ignore when field is present in database but not handled by SQLElement instance continue; // ignore when field is present in database but not handled by SQLElement instance
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
SQLField<Object> sqlField = (SQLField<Object>) instance.getFields().get(fieldName); SQLField<Object> sqlField = (SQLField<Object>) instance.getFields().get(fieldName);
instance.set(sqlField, set.getObject(c), false); if (sqlField.type.getJavaType().isEnum()) {
// JDBC ne supporte pas les enums
String enumStrValue = set.getString(c);
if (enumStrValue == null || set.wasNull())
instance.set(sqlField, null, false);
else {
Enum<?> enumValue = EnumUtil.searchUncheckedEnum(sqlField.type.getJavaType(), enumStrValue);
if (enumValue == null)
throw new ORMException("The enum constant '"+enumStrValue+"' is not found in enum class "+sqlField.type.getJavaType().getName());
instance.set(sqlField, enumValue, false);
}
}
else {
Object val = set.getObject(c, sqlField.type.getJavaType());
if (val == null || set.wasNull())
instance.set(sqlField, null, false);
else
instance.set(sqlField, val, false);
}
// la valeur venant de la BDD est marqué comme "non modifié" dans l'instance
// car le constructeur de l'instance met tout les champs comme modifiés
instance.modifiedSinceLastSave.remove(sqlField.name);
} }
if (!instance.isValidForSave())
throw new ORMException("This SQLElement representing a database entry is not valid for save : "+instance.toString());
return instance; return instance;
} catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) { } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException | SQLException e) {
throw new ReflectiveOperationException("Can't instanciate " + elemClass.getName(), e); throw new ORMException("Can't instanciate " + elemClass.getName(), e);
} }
} }
@ -293,9 +319,6 @@ public final class ORM {
private ORM() { } // rend la classe non instanciable private ORM() { } // rend la classe non instanciable
/* /*

View File

@ -37,7 +37,7 @@ public abstract class SQLElement {
private final SQLFieldMap fields; private final SQLFieldMap fields;
private final Map<SQLField<?>, Object> values; private final Map<SQLField<?>, Object> values;
private final Set<String> modifiedSinceLastSave; /* package */ final Set<String> modifiedSinceLastSave;
public SQLElement() { public SQLElement() {
@ -206,7 +206,7 @@ public abstract class SQLElement {
public boolean isValidForSave() { public boolean isValidForSave() {
return values.keySet().containsAll(fields.keySet()); return values.keySet().containsAll(fields.values());
} }
@ -214,7 +214,7 @@ public abstract class SQLElement {
private Map<SQLField<?>, Object> getOnlyModifiedValues() { private Map<SQLField<?>, Object> getOnlyModifiedValues() {
Map<SQLField<?>, Object> modifiedValues = new LinkedHashMap<>(); Map<SQLField<?>, Object> modifiedValues = new LinkedHashMap<>();
values.forEach((k, v) -> { values.forEach((k, v) -> {
if (modifiedSinceLastSave.contains(k)) if (modifiedSinceLastSave.contains(k.name))
modifiedValues.put(k, v); modifiedValues.put(k, v);
}); });
return modifiedValues; return modifiedValues;
@ -230,10 +230,10 @@ public abstract class SQLElement {
public void save() throws ORMException { public void save() throws ORMException {
if (!isValidForSave()) if (!isValidForSave())
throw new IllegalStateException("this instance of " + getClass().getName() + " has at least one undefined value and can't be saved."); throw new IllegalStateException(toString() + " has at least one undefined value and can't be saved.");
ORM.initTable(getClass()); ORM.initTable(getClass());
String toStringStatement = "";
try { try {
Connection conn = db.getNativeConnection(); Connection conn = db.getNativeConnection();
@ -249,14 +249,20 @@ public abstract class SQLElement {
modifiedSinceLastSave.remove("id"); modifiedSinceLastSave.remove("id");
Map<SQLField<?>, Object> modifiedValues = getOnlyModifiedValues(); Map<SQLField<?>, Object> modifiedValues = getOnlyModifiedValues();
if (modifiedValues.isEmpty())
return;
String sql = ""; String sql = "";
List<Object> psValues = new ArrayList<>(); List<Object> psValues = new ArrayList<>();
for(Map.Entry<SQLField<?>, Object> entry : modifiedValues.entrySet()) { for(Map.Entry<SQLField<?>, Object> entry : modifiedValues.entrySet()) {
sql += entry.getKey() + " = ? ,"; sql += entry.getKey().name + " = ? ,";
psValues.add(entry.getValue()); if (entry.getKey().type.getJavaType().isEnum()) {
// prise en charge enum (non prise en charge par JDBC)
psValues.add(((Enum<?>)entry.getValue()).name());
}
else
psValues.add(entry.getValue());
} }
if (sql.length() > 0) if (sql.length() > 0)
@ -270,7 +276,8 @@ public abstract class SQLElement {
for (Object val : psValues) { for (Object val : psValues) {
ps.setObject(i++, val); ps.setObject(i++, val);
} }
toStringStatement = ps.toString();
ps.executeUpdate(); ps.executeUpdate();
} finally { } finally {
ps.close(); ps.close();
@ -295,8 +302,13 @@ public abstract class SQLElement {
} }
first = false; first = false;
concat_vals += " ? "; concat_vals += " ? ";
concat_fields += entry.getKey(); concat_fields += entry.getKey().name;
psValues.add(entry.getValue()); if (entry.getKey().type.getJavaType().isEnum()) {
// prise en charge enum (non prise en charge par JDBC)
psValues.add(((Enum<?>)entry.getValue()).name());
}
else
psValues.add(entry.getValue());
} }
@ -307,7 +319,8 @@ public abstract class SQLElement {
for (Object val : psValues) { for (Object val : psValues) {
ps.setObject(i++, val); ps.setObject(i++, val);
} }
toStringStatement = ps.toString();
ps.executeUpdate(); ps.executeUpdate();
ResultSet rs = ps.getGeneratedKeys(); ResultSet rs = ps.getGeneratedKeys();
@ -329,8 +342,9 @@ public abstract class SQLElement {
modifiedSinceLastSave.clear(); modifiedSinceLastSave.clear();
} catch(SQLException e) { } catch(SQLException e) {
throw new ORMException("Error while saving data in table "+tableName(), e); throw new ORMException("Error while executing SQL statement "+toStringStatement, e);
} }
Log.debug(toStringStatement);
} }
@ -356,6 +370,7 @@ public abstract class SQLElement {
{ // supprimer la ligne de la base { // supprimer la ligne de la base
PreparedStatement st = db.getNativeConnection().prepareStatement("DELETE FROM "+tableName+" WHERE id="+id); PreparedStatement st = db.getNativeConnection().prepareStatement("DELETE FROM "+tableName+" WHERE id="+id);
try { try {
Log.debug(st.toString());
st.executeUpdate(); st.executeUpdate();
markAsNotStored(); markAsNotStored();
} finally { } finally {
@ -411,7 +426,7 @@ public abstract class SQLElement {
try { try {
b.append(f.name, get(f)); b.append(f.name, get(f));
} catch(IllegalArgumentException e) { } catch(IllegalArgumentException e) {
b.append(f.name, "(Must be defined before saving)"); b.append(f.name, "(Undefined)");
} }
} }

View File

@ -7,6 +7,8 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import fr.pandacube.java.util.Log;
/** /**
* *
* @param <E> * @param <E>
@ -32,11 +34,11 @@ public class SQLElementList<E extends SQLElement> extends ArrayList<E> {
* @param value la valeur à lui appliquer * @param value la valeur à lui appliquer
*/ */
public synchronized <T> void setCommon(SQLField<T> field, T value) { public synchronized <T> void setCommon(SQLField<T> field, T value) {
if (isEmpty()) throw new IllegalStateException("This SQLElementList is empty");
if (field != null && field.name == "id") if (field != null && field.name == "id")
throw new IllegalArgumentException("Can't modify id field in a SQLElementList"); throw new IllegalArgumentException("Can't modify id field in a SQLElementList");
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Class<E> elemClass = (Class<E>) get(0).getClass();
Class<E> elemClass = (Class<E>) field.getSQLElementType();
try { try {
E emptyElement = elemClass.newInstance(); E emptyElement = elemClass.newInstance();
emptyElement.set(field, value, false); emptyElement.set(field, value, false);
@ -71,7 +73,12 @@ public class SQLElementList<E extends SQLElement> extends ArrayList<E> {
for(Map.Entry<SQLField<?>, Object> entry : modifiedValues.entrySet()) { for(Map.Entry<SQLField<?>, Object> entry : modifiedValues.entrySet()) {
sqlSet += entry.getKey().name + " = ? ,"; sqlSet += entry.getKey().name + " = ? ,";
psValues.add(entry.getValue()); if (entry.getKey().type.getJavaType().isEnum()) {
// prise en charge enum (non prise en charge par JDBC)
psValues.add(((Enum<?>)entry.getValue()).name());
}
else
psValues.add(entry.getValue());
} }
if (sqlSet.length() > 0) if (sqlSet.length() > 0)
@ -93,6 +100,7 @@ public class SQLElementList<E extends SQLElement> extends ArrayList<E> {
ps.setObject(i++, val); ps.setObject(i++, val);
} }
Log.debug(ps.toString());
ps.executeUpdate(); ps.executeUpdate();
applyNewValuesToElements(storedEl); applyNewValuesToElements(storedEl);
@ -140,6 +148,7 @@ public class SQLElementList<E extends SQLElement> extends ArrayList<E> {
PreparedStatement st = ORM.getConnection().getNativeConnection().prepareStatement("DELETE FROM "+storedEl.get(0).tableName()+" WHERE "+sqlWhere); PreparedStatement st = ORM.getConnection().getNativeConnection().prepareStatement("DELETE FROM "+storedEl.get(0).tableName()+" WHERE "+sqlWhere);
try { try {
Log.debug(st.toString());
st.executeUpdate(); st.executeUpdate();
for (E el : storedEl) { for (E el : storedEl) {

View File

@ -6,7 +6,7 @@ public class SQLType<T> {
private final String sqlType; private final String sqlType;
private final String sqlTypeParam; private final String sqlTypeParam;
private final Class<?> javaTypes; private final Class<T> javaTypes;
public SQLType(String sqlT, String sqlP, Class<T> javaT) { public SQLType(String sqlT, String sqlP, Class<T> javaT) {
sqlType = sqlT; sqlType = sqlT;
@ -37,6 +37,10 @@ public class SQLType<T> {
return toString().equals(((SQLType<?>)obj).toString()); return toString().equals(((SQLType<?>)obj).toString());
} }
public Class<T> getJavaType() {
return javaTypes;
}

View File

@ -0,0 +1,63 @@
package fr.pandacube.java.util.measurement;
import java.text.DecimalFormat;
import java.util.Arrays;
public class DistanceUtil {
public static String distanceToString(double meterDist, int precision, DistanceUnit... desiredUnits) {
Arrays.sort(desiredUnits);
DistanceUnit choosenUnit = desiredUnits[0]; // la plus petite unitée
for (DistanceUnit unit : desiredUnits) {
if (meterDist / unit.multiplicator < 1)
continue;
choosenUnit = unit;
}
if (choosenUnit != desiredUnits[0] && precision <= 2)
precision = 2;
String precisionFormat = "##0";
if (precision > 0)
precisionFormat += ".";
for (int i=0;i<precision; i++)
precisionFormat += "0";
DecimalFormat df = new DecimalFormat(precisionFormat);
double dist = meterDist / choosenUnit.multiplicator;
return df.format(dist)+choosenUnit.unitStr;
}
public static String distanceToString(double meterDist, int precision) {
return distanceToString(meterDist, precision, DistanceUnit.M, DistanceUnit.KM);
}
public enum DistanceUnit implements Comparable<DistanceUnit> {
NM(0.000000001, "nm"),
µM(0.000001, "µm"),
MM(0.001, "mm"),
CM(0.01, "cm"),
M(1, "m"),
KM(1000, "km");
private final double multiplicator;
private final String unitStr;
private DistanceUnit(double mult, String s) {
multiplicator = mult;
unitStr = s;
}
}
}

View File

@ -0,0 +1,37 @@
package fr.pandacube.java.util.measurement;
import java.text.DecimalFormat;
public class MemoryUtil {
private static final DecimalFormat format = new DecimalFormat("#####0.00");
public static String humanReadableSize(long octet, boolean si)
{
double size = octet;
int diveBy = si ? 1000 : 1024;
if (size < diveBy)
return size+"o";
size /= diveBy;
if (size < diveBy)
return format.format(size) + (si ? "ko" : "kio");
size /= diveBy;
if (size < diveBy)
return format.format(size) + (si ? "Mo" : "Mio");
size /= diveBy;
if (size < diveBy)
return format.format(size) + (si ? "Go" : "Gio");
size /= diveBy;
return format.format(size) + (si ? "To" : "Tio");
}
public static String humanReadableSize(long octet) {
return humanReadableSize(octet, false);
}
}

View File

@ -1,4 +1,4 @@
package fr.pandacube.java.util; package fr.pandacube.java.util.measurement;
public class TimeUtil { public class TimeUtil {
public static String durationToString (long msec_time, boolean dec_seconde) public static String durationToString (long msec_time, boolean dec_seconde)