PandaLib/DB/src/main/java/fr/pandacube/lib/db/SQLElementList.java

188 lines
5.7 KiB
Java

package fr.pandacube.lib.db;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import fr.pandacube.lib.util.Log;
/**
*
* @param <E>
*/
public class SQLElementList<E extends SQLElement<E>> extends ArrayList<E> {
private final Map<SQLField<E, ?>, Object> modifiedValues = new LinkedHashMap<>();
@Override
public synchronized boolean add(E e) {
if (e == null || !e.isStored()) return false;
return super.add(e);
}
/**
* Défini une valeur à un champ qui sera appliquée dans la base de données à
* tous les
* entrées présente dans cette liste lors de l'appel à {@link #saveCommon()}
* .
* Les valeurs stockés dans chaque élément de cette liste ne seront affectés
* que lors de
* l'appel à {@link #saveCommon()}
*
* @param field le champs à modifier
* @param value la valeur à lui appliquer
*/
public synchronized <T> void setCommon(SQLField<E, T> field, T value) {
if (field == null)
throw new IllegalArgumentException("field can't be null");
if (Objects.equals(field.getName(), "id"))
throw new IllegalArgumentException("Can't modify id field in a SQLElementList");
Class<E> elemClass = field.getSQLElementType();
try {
E emptyElement = elemClass.getConstructor().newInstance();
emptyElement.set(field, value, false);
} catch (Exception e) {
throw new IllegalArgumentException("Illegal field or value or can't instanciante an empty instance of "
+ elemClass.getName() + ". (the instance is only created to test validity of field and value)", e);
}
// ici, la valeur est bonne
modifiedValues.put(field, value);
}
/**
* Applique toutes les valeurs défini avec
* {@link #setCommon(SQLField, Object)} à toutes
* les entrées dans la base de données correspondants aux entrées de cette
* liste. Les nouvelles
* valeurs sont aussi mises à jour dans les objets contenus dans cette
* liste, si la valeur n'a pas été modifiée individuellement avec
* {@link SQLElement#set(SQLField, Object)}.<br/>
* Les objets de cette liste qui n'ont pas leur données en base de données
* sont ignorées.
*/
public synchronized int saveCommon() throws DBException {
List<E> storedEl = getStoredEl();
if (storedEl.isEmpty()) return 0;
@SuppressWarnings("unchecked")
Class<E> classEl = (Class<E>)storedEl.get(0).getClass();
int ret = DB.update(classEl,
storedEl.get(0).getFieldId().in(storedEl.stream().map(SQLElement::getId).collect(Collectors.toList())
),
modifiedValues);
applyNewValuesToElements(storedEl);
return ret;
}
@SuppressWarnings("unchecked")
private void applyNewValuesToElements(List<E> storedEl) {
// applique les valeurs dans chaques objets de la liste
for (E el : storedEl)
for (@SuppressWarnings("rawtypes")
SQLField entry : modifiedValues.keySet())
if (!el.isModified(entry)) el.set(entry, modifiedValues.get(entry), false);
}
private List<E> getStoredEl() {
return stream().filter(SQLElement::isStored).collect(Collectors.toCollection(ArrayList::new));
}
/**
* @deprecated please use {@link DB#delete(Class, SQLWhere)} instead,
* except if you really want to fetch the data before removing them from database.
*/
@Deprecated
public synchronized void removeFromDB() {
List<E> storedEl = getStoredEl();
if (storedEl.isEmpty()) return;
try {
@SuppressWarnings("unchecked")
Class<E> classEl = (Class<E>)storedEl.get(0).getClass();
DB.delete(classEl,
storedEl.get(0).getFieldId().in(storedEl.stream().map(SQLElement::getId).collect(Collectors.toList()))
);
for (E el : storedEl)
el.markAsNotStored();
} catch (DBException e) {
Log.severe(e);
}
}
public <T, P extends SQLElement<P>> SQLElementList<P> getReferencedEntries(SQLFKField<E, T, P> foreignKey, SQLOrderBy<P> orderBy) throws DBException {
Set<T> values = new HashSet<>();
forEach(v -> {
T val = v.get(foreignKey);
if (val != null)
values.add(val);
});
if (values.isEmpty()) {
return new SQLElementList<>();
}
return DB.getAll(foreignKey.getForeignElementClass(), foreignKey.getPrimaryField().in(values), orderBy, null, null);
}
public <T, P extends SQLElement<P>> Map<T, P> getReferencedEntriesInGroups(SQLFKField<E, T, P> foreignKey) throws DBException {
SQLElementList<P> foreignElemts = getReferencedEntries(foreignKey, null);
Map<T, P> ret = new HashMap<>();
foreignElemts.forEach(foreignVal -> ret.put(foreignVal.get(foreignKey.getPrimaryField()), foreignVal));
return ret;
}
public <T, F extends SQLElement<F>> SQLElementList<F> getReferencingForeignEntries(SQLFKField<F, T, E> foreignKey, SQLOrderBy<F> orderBy, Integer limit, Integer offset) throws DBException {
Set<T> values = new HashSet<>();
forEach(v -> {
T val = v.get(foreignKey.getPrimaryField());
if (val != null)
values.add(val);
});
if (values.isEmpty()) {
return new SQLElementList<>();
}
return DB.getAll(foreignKey.getSQLElementType(), foreignKey.in(values), orderBy, limit, offset);
}
public <T, F extends SQLElement<F>> Map<T, SQLElementList<F>> getReferencingForeignEntriesInGroups(SQLFKField<F, T, E> foreignKey, SQLOrderBy<F> orderBy, Integer limit, Integer offset) throws DBException {
SQLElementList<F> foreignElements = getReferencingForeignEntries(foreignKey, orderBy, limit, offset);
Map<T, SQLElementList<F>> map = new HashMap<>();
foreignElements.forEach(foreignVal -> {
SQLElementList<F> subList = map.getOrDefault(foreignVal.get(foreignKey), new SQLElementList<>());
subList.add(foreignVal);
map.put(foreignVal.get(foreignKey), subList);
});
return map;
}
}