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);
}
}
}