96 lines
3.3 KiB
Java
96 lines
3.3 KiB
Java
package squirrel;
|
|
|
|
import java.sql.*;
|
|
import java.util.*;
|
|
|
|
public class LocalStorage {
|
|
private final Connection conn;
|
|
|
|
public LocalStorage() {
|
|
this.conn = Database.getConnection();
|
|
}
|
|
|
|
// Save: serialize constructor arguments
|
|
public <T> void save(String key, T value) {
|
|
try {
|
|
var ctor = value.getClass().getDeclaredConstructors()[0];
|
|
ctor.setAccessible(true);
|
|
|
|
var fields = value.getClass().getDeclaredFields();
|
|
List<String> pairs = new ArrayList<>();
|
|
|
|
for (var f : fields) {
|
|
f.setAccessible(true);
|
|
Object v = f.get(value);
|
|
pairs.add(f.getName() + "=" + (v == null ? "null" : v.toString()));
|
|
}
|
|
|
|
try (PreparedStatement ps = conn.prepareStatement(
|
|
"INSERT INTO storage(key, data) VALUES(?, ?) " +
|
|
"ON CONFLICT(key) DO UPDATE SET data=excluded.data"
|
|
)) {
|
|
ps.setString(1, key);
|
|
ps.setString(2, String.join(";", pairs));
|
|
ps.executeUpdate();
|
|
}
|
|
} catch (Exception e) {
|
|
throw new RuntimeException("Save failed", e);
|
|
}
|
|
}
|
|
|
|
// Load: reconstruct using the constructor of clazz
|
|
@SuppressWarnings("unchecked")
|
|
public <T> T load(String key, Class<T> clazz) {
|
|
try (PreparedStatement ps = conn.prepareStatement(
|
|
"SELECT data FROM storage WHERE key=?"
|
|
)) {
|
|
ps.setString(1, key);
|
|
ResultSet rs = ps.executeQuery();
|
|
if (!rs.next()) return null;
|
|
|
|
String data = rs.getString("data");
|
|
Map<String, String> map = new LinkedHashMap<>();
|
|
for (String pair : data.split(";")) {
|
|
if (pair.isBlank()) continue;
|
|
int idx = pair.indexOf('=');
|
|
if (idx == -1) continue;
|
|
map.put(pair.substring(0, idx), pair.substring(idx + 1));
|
|
}
|
|
|
|
var ctor = clazz.getDeclaredConstructors()[0];
|
|
ctor.setAccessible(true);
|
|
Class<?>[] paramTypes = ctor.getParameterTypes();
|
|
Object[] converted = new Object[paramTypes.length];
|
|
|
|
int i = 0;
|
|
for (Class<?> pt : paramTypes) {
|
|
String raw = map.values().toArray(new String[0])[i];
|
|
if (pt == int.class || pt == Integer.class) {
|
|
converted[i] = Integer.parseInt(raw);
|
|
} else if (pt == long.class || pt == Long.class) {
|
|
converted[i] = Long.parseLong(raw);
|
|
} else if (pt == double.class || pt == Double.class) {
|
|
converted[i] = Double.parseDouble(raw);
|
|
} else if (pt == String.class) {
|
|
converted[i] = raw;
|
|
} else {
|
|
throw new IllegalArgumentException("Unsupported param type: " + pt);
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return (T) ctor.newInstance(converted);
|
|
} catch (Exception e) {
|
|
throw new RuntimeException("Load failed", e);
|
|
}
|
|
}
|
|
|
|
public void flush() {
|
|
try (Statement stmt = conn.createStatement()) {
|
|
stmt.executeUpdate("DELETE FROM storage");
|
|
} catch (Exception e) {
|
|
throw new RuntimeException("Flush failed", e);
|
|
}
|
|
}
|
|
}
|