2023-09-17 22:13:42 +03:00
|
|
|
|
package Common.Database.SQLITE;
|
2024-10-07 00:58:29 +03:00
|
|
|
|
import Common.Database.Objects.DBObject;
|
|
|
|
|
|
import Common.Database.Tables.DBTable;
|
|
|
|
|
|
import Common.Database.Tables.DBTableColumn;
|
2023-09-17 22:13:42 +03:00
|
|
|
|
import Common.Database.Database;
|
2024-10-07 14:22:52 +03:00
|
|
|
|
import Common.Utils.CommonUtils;
|
2024-10-07 00:58:29 +03:00
|
|
|
|
import Common_old.UI.UI;
|
|
|
|
|
|
import Common_old.Utils.Utils;
|
2023-11-19 02:12:44 +03:00
|
|
|
|
import Visual_DVM_2021.Passes.PassException;
|
2023-09-17 22:13:42 +03:00
|
|
|
|
import javafx.util.Pair;
|
|
|
|
|
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
|
|
import java.sql.*;
|
|
|
|
|
|
import java.util.LinkedHashMap;
|
|
|
|
|
|
import java.util.Vector;
|
|
|
|
|
|
|
2024-10-07 00:58:29 +03:00
|
|
|
|
import static Common_old.Utils.Utils.requireNonNullElse;
|
2023-09-17 22:13:42 +03:00
|
|
|
|
public abstract class SQLiteDatabase extends Database {
|
|
|
|
|
|
protected Connection conn = null;
|
|
|
|
|
|
protected Statement statement = null;
|
|
|
|
|
|
protected ResultSet resSet = null;
|
|
|
|
|
|
public LinkedHashMap<Class<? extends DBObject>, PreparedStatement> insertStatements = new LinkedHashMap<>();
|
|
|
|
|
|
public LinkedHashMap<Class<? extends DBObject>, PreparedStatement> updateStatements = new LinkedHashMap<>();
|
|
|
|
|
|
public LinkedHashMap<Class<? extends DBObject>, 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();
|
|
|
|
|
|
Utils.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<String> existing_columns = new Vector<>();
|
|
|
|
|
|
Vector<String> 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<String> columns_names = new Vector<>();
|
|
|
|
|
|
for (DBTableColumn column : table.columns.values())
|
|
|
|
|
|
columns_names.add(column.toString());
|
2024-10-07 14:22:52 +03:00
|
|
|
|
cmd += CommonUtils.RBrackets(String.join(",", columns_names));
|
2023-09-17 22:13:42 +03:00
|
|
|
|
statement.execute(cmd);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public void prepareTablesStatements() throws Exception {
|
|
|
|
|
|
for (DBTable table : tables.values()) {
|
|
|
|
|
|
//---
|
|
|
|
|
|
Vector<String> column_names = new Vector<>();
|
|
|
|
|
|
Vector<String> 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() + " " +
|
2024-10-07 14:22:52 +03:00
|
|
|
|
CommonUtils.RBrackets(String.join(",", column_names)) + " " + "VALUES " +
|
|
|
|
|
|
CommonUtils.RBrackets(String.join(",", column_values))));
|
2023-09-17 22:13:42 +03:00
|
|
|
|
//------------------------------------------------------------------------------->>
|
|
|
|
|
|
Vector<String> 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 <K, D extends DBObject> Pair<K, D> readRecord(DBTable<K, D> 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
|
2024-10-07 14:22:52 +03:00
|
|
|
|
throw new PassException("Ошибка при загрузке поля " + CommonUtils.Brackets(column.Name) + " класса " + CommonUtils.Brackets(table.d.getSimpleName()));
|
2023-09-17 22:13:42 +03:00
|
|
|
|
}
|
|
|
|
|
|
return new Pair<>((K) o.getPK(), o);
|
|
|
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
|
|
|
protected <K, D extends DBObject> void loadAll(DBTable<K, D> table) throws Exception {
|
|
|
|
|
|
resSet = statement.executeQuery("SELECT * FROM " + table.QName());
|
|
|
|
|
|
while (resSet.next()) {
|
|
|
|
|
|
Pair<K, D> 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)
|
|
|
|
|
|
UI.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)
|
|
|
|
|
|
UI.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
|
|
|
|
|
|
|
|
|
|
|
|
}
|