From ea9bfd127020e3f3a5e11aefe454ade0c394c7e4 Mon Sep 17 00:00:00 2001 From: Marc Baloup Date: Mon, 2 Dec 2019 01:25:40 +0100 Subject: [PATCH] Some ORM refactoring --- src/main/java/fr/pandacube/util/orm/ORM.java | 192 +++++++++--------- .../fr/pandacube/util/orm/SQLElement.java | 36 +--- .../fr/pandacube/util/orm/SQLElementList.java | 48 ++--- .../java/fr/pandacube/util/orm/SQLUpdate.java | 70 +++++++ 4 files changed, 181 insertions(+), 165 deletions(-) create mode 100644 src/main/java/fr/pandacube/util/orm/SQLUpdate.java diff --git a/src/main/java/fr/pandacube/util/orm/ORM.java b/src/main/java/fr/pandacube/util/orm/ORM.java index 04d9cdb..88f2fd0 100644 --- a/src/main/java/fr/pandacube/util/orm/ORM.java +++ b/src/main/java/fr/pandacube/util/orm/ORM.java @@ -6,7 +6,9 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.function.Consumer; import org.javatuples.Pair; @@ -24,6 +26,7 @@ import fr.pandacube.util.orm.SQLWhereComp.SQLComparator; public final class ORM { private static List>> tables = new ArrayList<>(); + private static Map>, String> tableNames = new HashMap<>(); private static DBConnection connection; @@ -45,7 +48,8 @@ public final class ORM { Log.debug("[ORM] Start Init SQL table "+elemClass.getSimpleName()); E instance = elemClass.newInstance(); String tableName = instance.tableName(); - if (!tableExist(tableName)) createTable(instance); + tableNames.put(elemClass, tableName); + if (!tableExistInDB(tableName)) createTable(instance); Log.debug("[ORM] End init SQL table "+elemClass.getSimpleName()); } catch (Exception|ExceptionInInitializerError e) { throw new ORMInitTableException(elemClass, e); @@ -78,8 +82,13 @@ public final class ORM { ps.executeUpdate(); } } + + public static > String getTableName(Class elemClass) throws ORMException { + initTable(elemClass); + return tableNames.get(elemClass); + } - private static boolean tableExist(String tableName) throws SQLException { + private static boolean tableExistInDB(String tableName) throws SQLException { boolean exist = false; try (ResultSet set = connection.getNativeConnection().getMetaData().getTables(null, null, tableName, null)) { exist = set.next(); @@ -176,7 +185,7 @@ public final class ORM { initTable(elemClass); try { - String sql = "SELECT * FROM " + elemClass.newInstance().tableName(); + String sql = "SELECT * FROM " + getTableName(elemClass); List params = new ArrayList<>(); @@ -189,67 +198,20 @@ public final class ORM { if (limit != null) sql += " LIMIT " + limit; if (offset != null) sql += " OFFSET " + offset; sql += ";"; - - try (PreparedStatement ps = connection.getNativeConnection().prepareStatement(sql)) { - - int i = 1; - for (Object val : params) { - if (val instanceof Enum) val = ((Enum) val).name(); - ps.setObject(i++, val); - } - Log.debug(ps.toString()); - - try (ResultSet set = ps.executeQuery()) { - while (set.next()) { - E elm = getElementInstance(set, elemClass); - action.accept(elm); - } + + try (ResultSet set = customQueryStatement(sql, params)) { + while (set.next()) { + E elm = getElementInstance(set, elemClass); + action.accept(elm); } } - } catch (ReflectiveOperationException | SQLException e) { + } catch (SQLException e) { throw new ORMException(e); } } - /** - * Delete the elements of the table represented by {@code elemClass} which meet the condition {@code where}. - * @param elemClass the SQLElement representing the table. - * @param where the condition to meet for an element to be deleted from the table. If null, the table is truncated using {@link #truncateTable(Class)}. - * @return The return value of {@link PreparedStatement#executeUpdate()}, for an SQL query {@code DELETE}. - * @throws ORMException - */ - public static > int delete(Class elemClass, SQLWhere where) throws ORMException { - initTable(elemClass); - - if (where == null) { - return truncateTable(elemClass); - } - - try { - Pair> whereData = where.toSQL(); - - String sql = "DELETE FROM " + elemClass.newInstance().tableName() - + " WHERE " + whereData.getValue0() - + ";"; - List params = new ArrayList<>(whereData.getValue1()); - - try (PreparedStatement ps = connection.getNativeConnection().prepareStatement(sql)) { - - int i = 1; - for (Object val : params) { - if (val instanceof Enum) val = ((Enum) val).name(); - ps.setObject(i++, val); - } - Log.debug(ps.toString()); - - return ps.executeUpdate(); - } - } catch (ReflectiveOperationException | SQLException e) { - throw new ORMException(e); - } - - } + public static > long count(Class elemClass) throws ORMException { return count(elemClass, null); @@ -259,7 +221,7 @@ public final class ORM { initTable(elemClass); try { - String sql = "SELECT COUNT(*) as count FROM " + elemClass.newInstance().tableName(); + String sql = "SELECT COUNT(*) as count FROM " + getTableName(elemClass); List params = new ArrayList<>(); @@ -269,23 +231,13 @@ public final class ORM { params.addAll(ret.getValue1()); } sql += ";"; - - try (PreparedStatement ps = connection.getNativeConnection().prepareStatement(sql)) { - - int i = 1; - for (Object val : params) { - if (val instanceof Enum) val = ((Enum) val).name(); - ps.setObject(i++, val); - } - Log.debug(ps.toString()); - - try (ResultSet set = ps.executeQuery()) { - while (set.next()) { - return set.getLong(1); - } + + try (ResultSet set = customQueryStatement(sql, params)) { + if (set.next()) { + return set.getLong(1); } } - } catch (ReflectiveOperationException | SQLException e) { + } catch (SQLException e) { throw new ORMException(e); } @@ -295,16 +247,8 @@ public final class ORM { - public static > int truncateTable(Class elemClass) throws ORMException { - try (Statement stmt = connection.getNativeConnection().createStatement()) { - return stmt.executeUpdate("TRUNCATE `" + elemClass.newInstance().tableName() + "`"); - } catch(SQLException | ReflectiveOperationException e) { - throw new ORMException(e); - } - } - - public static ResultSet getCustomResult(String sql, List params) throws ORMException { + public static ResultSet customQueryStatement(String sql, List params) throws ORMException { try { PreparedStatement ps = connection.getNativeConnection().prepareStatement(sql); int i = 1; @@ -325,6 +269,71 @@ public final class ORM { } + + + + public static > SQLUpdate update(Class elemClass, SQLWhere where) throws ORMException { + return new SQLUpdate<>(elemClass, where); + } + + /* package */ static > int update(Class elemClass, SQLWhere where, Map, Object> values) throws ORMException { + return new SQLUpdate<>(elemClass, where, values).execute(); + } + + + /** + * Delete the elements of the table represented by {@code elemClass} which meet the condition {@code where}. + * @param elemClass the SQLElement representing the table. + * @param where the condition to meet for an element to be deleted from the table. If null, the table is truncated using {@link #truncateTable(Class)}. + * @return The return value of {@link PreparedStatement#executeUpdate()}, for an SQL query {@code DELETE}. + * @throws ORMException + */ + public static > int delete(Class elemClass, SQLWhere where) throws ORMException { + initTable(elemClass); + + if (where == null) { + return truncateTable(elemClass); + } + + Pair> whereData = where.toSQL(); + + String sql = "DELETE FROM " + getTableName(elemClass) + + " WHERE " + whereData.getValue0() + + ";"; + List params = new ArrayList<>(whereData.getValue1()); + + return customUpdateStatement(sql, params); + + } + + + + public static int customUpdateStatement(String sql, List params) throws ORMException { + try (PreparedStatement ps = connection.getNativeConnection().prepareStatement(sql)) { + + int i = 1; + for (Object val : params) { + if (val instanceof Enum) val = ((Enum) val).name(); + ps.setObject(i++, val); + } + Log.debug(ps.toString()); + + return ps.executeUpdate(); + } catch (SQLException e) { + throw new ORMException(e); + } + } + + + + public static > int truncateTable(Class elemClass) throws ORMException { + try (Statement stmt = connection.getNativeConnection().createStatement()) { + return stmt.executeUpdate("TRUNCATE `" + getTableName(elemClass) + "`"); + } catch(SQLException e) { + throw new ORMException(e); + } + } + @SuppressWarnings("unchecked") private static > E getElementInstance(ResultSet set, Class elemClass) throws ORMException { try { @@ -379,23 +388,4 @@ public final class ORM { private ORM() {} // rend la classe non instanciable - /* - * public static void main(String[] args) throws Throwable { - * ORM.init(new DBConnection("localhost", 3306, "pandacube", "pandacube", - * "pandacube")); - * List players = ORM.getAll(SQLPlayer.class, - * new SQLWhereChain(SQLBoolOp.AND) - * .add(new SQLWhereNull(SQLPlayer.banTimeout, true)) - * .add(new SQLWhereChain(SQLBoolOp.OR) - * .add(new SQLWhereComp(SQLPlayer.bambou, SQLComparator.EQ, 0L)) - * .add(new SQLWhereComp(SQLPlayer.grade, SQLComparator.EQ, "default")) - * ), - * new SQLOrderBy().addField(SQLPlayer.playerDisplayName), null, null); - * for(SQLPlayer p : players) { - * System.out.println(p.get(SQLPlayer.playerDisplayName)); - * } - * // TODO mise à jour relative d'un champ (incrément / décrément) - * } - */ - } diff --git a/src/main/java/fr/pandacube/util/orm/SQLElement.java b/src/main/java/fr/pandacube/util/orm/SQLElement.java index e7b1802..bb54bbb 100644 --- a/src/main/java/fr/pandacube/util/orm/SQLElement.java +++ b/src/main/java/fr/pandacube/util/orm/SQLElement.java @@ -32,7 +32,6 @@ public abstract class SQLElement> { private boolean stored = false; private int id; - private final String tableName; private final SQLFieldMap fields; private final Map, Object> values; @@ -40,7 +39,6 @@ public abstract class SQLElement> { @SuppressWarnings("unchecked") public SQLElement() { - tableName = tableName(); try { ORM.initTable((Class)getClass()); @@ -230,7 +228,6 @@ public abstract class SQLElement> { throw new IllegalStateException(toString() + " has at least one undefined value and can't be saved."); ORM.initTable((Class)getClass()); - String toStringStatement = ""; try { if (stored) { // mettre à jour les valeurs dans la base @@ -243,27 +240,8 @@ public abstract class SQLElement> { Map, Object> modifiedValues = getOnlyModifiedValues(); if (modifiedValues.isEmpty()) return; - - String sql = ""; - List psValues = new ArrayList<>(); - - for (Map.Entry, Object> entry : modifiedValues.entrySet()) { - sql += "`" + entry.getKey().getName() + "` = ? ,"; - addValueToSQLObjectList(psValues, entry.getKey(), entry.getValue()); - } - - if (sql.length() > 0) sql = sql.substring(0, sql.length() - 1); - - try (PreparedStatement ps = db.getNativeConnection() - .prepareStatement("UPDATE " + tableName + " SET " + sql + " WHERE id=" + id)) { - - int i = 1; - for (Object val : psValues) - ps.setObject(i++, val); - - toStringStatement = ps.toString(); - ps.executeUpdate(); - } + + ORM.update((Class)getClass(), new SQLWhereComp(getFieldId(), SQLComparator.EQ, getId()), modifiedValues); } else { // ajouter dans la base @@ -286,16 +264,15 @@ public abstract class SQLElement> { concat_fields += "`" + entry.getKey().getName() + "`"; addValueToSQLObjectList(psValues, entry.getKey(), entry.getValue()); } - + try (PreparedStatement ps = db.getNativeConnection().prepareStatement( - "INSERT INTO " + tableName + " (" + concat_fields + ") VALUES (" + concat_vals + ")", + "INSERT INTO " + tableName() + " (" + concat_fields + ") VALUES (" + concat_vals + ")", Statement.RETURN_GENERATED_KEYS)) { int i = 1; for (Object val : psValues) ps.setObject(i++, val); - toStringStatement = ps.toString(); ps.executeUpdate(); try (ResultSet rs = ps.getGeneratedKeys()) { @@ -308,9 +285,8 @@ public abstract class SQLElement> { modifiedSinceLastSave.clear(); } catch (SQLException e) { - throw new ORMException("Error while executing SQL statement " + toStringStatement, e); + throw new ORMException("Error while saving data", e); } - Log.debug(toStringStatement); } @@ -344,7 +320,7 @@ public abstract class SQLElement> { if (stored) { // supprimer la ligne de la base try (PreparedStatement st = db.getNativeConnection() - .prepareStatement("DELETE FROM " + tableName + " WHERE id=" + id)) { + .prepareStatement("DELETE FROM " + tableName() + " WHERE id=" + id)) { Log.debug(st.toString()); st.executeUpdate(); markAsNotStored(); diff --git a/src/main/java/fr/pandacube/util/orm/SQLElementList.java b/src/main/java/fr/pandacube/util/orm/SQLElementList.java index 4bccb45..da3a3cd 100644 --- a/src/main/java/fr/pandacube/util/orm/SQLElementList.java +++ b/src/main/java/fr/pandacube/util/orm/SQLElementList.java @@ -78,42 +78,22 @@ public class SQLElementList> extends ArrayList { * * @throws SQLException */ - public synchronized void saveCommon() throws ORMException { + public synchronized int saveCommon() throws ORMException { List storedEl = getStoredEl(); - if (storedEl.isEmpty()) return; + if (storedEl.isEmpty()) return 0; + + @SuppressWarnings("unchecked") + Class classEl = (Class)storedEl.get(0).getClass(); + + int ret = ORM.update(classEl, + new SQLWhereIn(storedEl.get(0).getFieldId(), + storedEl.stream().map(SQLElement::getId).collect(Collectors.toList()) + ), + modifiedValues); - String sqlSet = ""; - List psValues = new ArrayList<>(); - - for (Map.Entry, Object> entry : modifiedValues.entrySet()) { - sqlSet += "`" + entry.getKey().getName() + "` = ? ,"; - SQLElement.addValueToSQLObjectList(psValues, entry.getKey(), entry.getValue()); - } - - if (sqlSet.length() > 0) sqlSet = sqlSet.substring(0, sqlSet.length() - 1); - - String sqlWhere = ""; - boolean first = true; - for (E el : storedEl) { - if (!first) sqlWhere += " OR "; - first = false; - sqlWhere += "id = " + el.getId(); - } - - try(PreparedStatement ps = ORM.getConnection().getNativeConnection() - .prepareStatement("UPDATE " + storedEl.get(0).tableName() + " SET " + sqlSet + " WHERE " + sqlWhere)) { - - int i = 1; - for (Object val : psValues) - ps.setObject(i++, val); - - Log.debug(ps.toString()); - ps.executeUpdate(); - - applyNewValuesToElements(storedEl); - } catch (SQLException e) { - throw new ORMException(e); - } + applyNewValuesToElements(storedEl); + + return ret; } @SuppressWarnings("unchecked") diff --git a/src/main/java/fr/pandacube/util/orm/SQLUpdate.java b/src/main/java/fr/pandacube/util/orm/SQLUpdate.java new file mode 100644 index 0000000..295ab2f --- /dev/null +++ b/src/main/java/fr/pandacube/util/orm/SQLUpdate.java @@ -0,0 +1,70 @@ +package fr.pandacube.util.orm; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.javatuples.Pair; + +import fr.pandacube.util.Log; + +public class SQLUpdate> { + + private final Class elemClass; + private final SQLWhere where; + private final Map, Object> values; + + /* package */ SQLUpdate(Class el, SQLWhere w) { + elemClass = el; + where = w; + values = new HashMap<>(); + } + + /* package */ SQLUpdate(Class el, SQLWhere w, Map, Object> v) { + elemClass = el; + where = w; + values = v; + } + + public SQLUpdate set(SQLField field, T value) { + values.put(field, value); + return this; + } + + public SQLUpdate setUnsafe(SQLField field, Object value) { + values.put(field, value); + return this; + } + + public int execute() throws ORMException { + + if (values.isEmpty()) { + Log.warning(new ORMException("Trying to do an UPDATE with no values to SET. Query aborted.")); + return 0; + } + + String sql = "UPDATE " + ORM.getTableName(elemClass) + " SET "; + List params = new ArrayList<>(); + + boolean first = true; + for (Map.Entry, Object> entry : values.entrySet()) { + if (!first) + sql += ", "; + sql += "`" + entry.getKey().getName() + "` = ? "; + SQLElement.addValueToSQLObjectList(params, entry.getKey(), entry.getValue()); + first = false; + } + + if (where != null) { + Pair> ret = where.toSQL(); + sql += " WHERE " + ret.getValue0(); + params.addAll(ret.getValue1()); + } + + sql += ";"; + + return ORM.customUpdateStatement(sql, params); + } + +}