package Common.Database.SQLITE; import Common.Database.Objects.DBObject; import Common.Database.Tables.DBTable; import Common.Database.Tables.DBTableColumn; import Common.Database.Database; import Common.Utils.CommonUtils; import Common.Visual.CommonUI; import Visual_DVM_2021.Passes.PassException; import javafx.util.Pair; import java.io.File; import java.sql.*; import java.util.LinkedHashMap; import java.util.Vector; import static Common.Utils.CommonUtils.requireNonNullElse; public abstract class SQLiteDatabase extends Database { protected Connection conn = null; protected Statement statement = null; protected ResultSet resSet = null; public LinkedHashMap, PreparedStatement> insertStatements = new LinkedHashMap<>(); public LinkedHashMap, PreparedStatement> updateStatements = new LinkedHashMap<>(); public LinkedHashMap, PreparedStatement> deleteStatements = new LinkedHashMap<>(); //->> public SQLiteDatabase(File file_in) { super(file_in); } @Override protected void connect() throws Exception { Class.forName("org.sqlite.JDBC"); for (int i = 0; i < 5; ++i) { try { conn = DriverManager.getConnection("jdbc:sqlite:" + file.getAbsolutePath()); break; } catch (Exception ex) { ex.printStackTrace(); conn = null; System.gc(); CommonUtils.sleep(2000); } } if (conn == null) throw new PassException("Внутренняя ошибка sqlite. Не удалось установить соединение за 5 попыток."); statement = conn.createStatement(); } @Override protected void disconnect() throws Exception { if (conn != null) { conn.close(); conn = null; } if (statement != null) { statement.close(); statement = null; } if (resSet != null) { resSet.close(); resSet = null; } for (PreparedStatement preparedStatement : insertStatements.values()) { if (preparedStatement != null) preparedStatement.close(); } for (PreparedStatement preparedStatement : updateStatements.values()) { if (preparedStatement != null) preparedStatement.close(); } for (PreparedStatement preparedStatement : deleteStatements.values()) { if (preparedStatement != null) preparedStatement.close(); } insertStatements.clear(); updateStatements.clear(); deleteStatements.clear(); //-->> System.gc(); //->> } @Override protected void beginTransaction() throws Exception { conn.setAutoCommit(false); } @Override protected void commit() throws Exception { conn.commit(); conn.setAutoCommit(true); } @Override protected boolean TableExists(DBTable table) throws Exception { int count = 0; resSet = statement.executeQuery("SELECT count(*) FROM 'sqlite_master' WHERE type=\"table\" AND name=" + table.QName()); if (resSet.next()) count = resSet.getInt(1); return count > 0; } @Override public void CreateTable(DBTable table) throws Exception { if (table.columns.size() > 0) { if (TableExists(table)) { Vector existing_columns = new Vector<>(); Vector columns_to_create = new Vector<>(); //предполагаем что первичный ключ и атрибуты не изменятся. //и что не будет столбов с одинаковыми именами но разными типами. resSet = statement.executeQuery("pragma table_info(" + table.QName() + ")"); while (resSet.next()) existing_columns.add(resSet.getString(2)); //- for (String target_column : table.columns.keySet()) { if (!existing_columns.contains(target_column)) columns_to_create.add(target_column); } for (String cn : columns_to_create) statement.execute("ALTER TABLE " + table.QName() + " ADD COLUMN " + table.columns.get(cn)); } else { String cmd = "CREATE TABLE if not exists " + table.QName() + " "; Vector columns_names = new Vector<>(); for (DBTableColumn column : table.columns.values()) columns_names.add(column.toString()); cmd += CommonUtils.RBrackets(String.join(",", columns_names)); statement.execute(cmd); } } } @Override public void prepareTablesStatements() throws Exception { for (DBTable table : tables.values()) { //--- Vector column_names = new Vector<>(); Vector column_values = new Vector<>(); for (DBTableColumn column : table.columns.values()) { if (!column.AutoIncrement) { column_names.add(column.QName()); column_values.add("?"); } } insertStatements.put(table.d, conn.prepareStatement( "INSERT OR REPLACE INTO " + table.QName() + " " + CommonUtils.RBrackets(String.join(",", column_names)) + " " + "VALUES " + CommonUtils.RBrackets(String.join(",", column_values)))); //------------------------------------------------------------------------------->> Vector new_values = new Vector(); for (DBTableColumn column : table.columns.values()) { if (!column.AutoIncrement) { new_values.add(column.QName() + "=?"); } } updateStatements.put(table.d, conn.prepareStatement( "UPDATE " + table.QName() + " " + "SET " + String.join(",", new_values) + " " + "WHERE (" + table.PK.QName() + "=?)" )); //---------------------------------------------------------------------------------->>> deleteStatements.put(table.d, conn.prepareStatement("DELETE FROM " + table.QName() + " WHERE " + table.PK.QName() + " = ?")); } } @Override protected void delete(DBTable table, DBObject o) throws Exception { PreparedStatement ps = deleteStatements.get(table.d); ps.setObject(1, o.getPK()); ps.executeUpdate(); } protected Pair readRecord(DBTable table) throws Exception { D o = table.d.newInstance(); for (DBTableColumn column : table.columns.values()) { Object field_value = null; switch (column.type) { case DOUBLE: field_value = requireNonNullElse(resSet.getDouble(column.Name), 0); break; case UNDEFINED: break; case INT: field_value = requireNonNullElse(resSet.getInt(column.Name), 0); break; case LONG: field_value = requireNonNullElse(resSet.getLong(column.Name), 0); break; case STRING: if (table.d.getField(column.Name).getType().isEnum()) { Class enum_class = Class.forName(table.d.getField(column.Name).getType().getName()); String string = resSet.getString(column.Name); Object[] enum_constants = enum_class.getEnumConstants(); if (string != null) { try { field_value = Enum.valueOf(enum_class, string); } catch (Exception ignore) { field_value = enum_constants[0]; } } else field_value = enum_constants[0]; } else field_value = requireNonNullElse(resSet.getString(column.Name), ""); break; } if (field_value != null) { table.d.getField(column.Name).set(o, field_value); } else throw new PassException("Ошибка при загрузке поля " + CommonUtils.Brackets(column.Name) + " класса " + CommonUtils.Brackets(table.d.getSimpleName())); } return new Pair<>((K) o.getPK(), o); } @Override protected void loadAll(DBTable table) throws Exception { resSet = statement.executeQuery("SELECT * FROM " + table.QName()); while (resSet.next()) { Pair record = readRecord(table); table.Data.put(record.getKey(), record.getValue()); } } @Override protected void insert(DBTable table, DBObject o) throws Exception { PreparedStatement ps = insertStatements.get(table.d); if (ps == null) CommonUI.Info("INSERT NULL"); int i = 1; for (DBTableColumn column : table.columns.values()) { if (!column.AutoIncrement) { ps.setObject(i, o.getClass().getField(column.Name).get(o)); ++i; } } ps.execute(); //--> for (DBTableColumn column : table.columns.values()) { if (column.AutoIncrement) { resSet = statement.executeQuery("SELECT MAX(" + column.QName() + ") AS LAST FROM " + table.QName()); String maxId = resSet.getString("LAST"); int intMaxId = Integer.parseInt(maxId); o.getClass().getField(column.Name).set(o, intMaxId); break; } } } @Override protected void update(DBTable table, DBObject o) throws Exception { PreparedStatement ps = updateStatements.get(table.d); if (ps == null) CommonUI.Info("UPDATE NULL"); int i = 1; for (DBTableColumn column : table.columns.values()) { if (!column.AutoIncrement) { ps.setObject(i, o.getClass().getField(column.Name).get(o)); ++i; } } ps.setObject(i, o.getPK()); ps.executeUpdate(); } @Override protected void deleteAll(DBTable table) throws Exception { statement.executeUpdate("DELETE FROM " + table.QName()); } @Override protected void resetAI(DBTable table) throws Exception { statement.executeUpdate("UPDATE SQLITE_SEQUENCE SET SEQ = 0 WHERE NAME =" + table.QName()); } //-- //https://stackoverflow.com/questions/8558099/sqlite-query-with-byte-where-clause }