package shine.db.dao; import shine.db.SqliteDbController; import shine.db.entities.UserParamEntry; import java.sql.*; import java.util.ArrayList; import java.util.List; /** * UserParamsDAO — хранение сохранённых параметров пользователя. * * Правило: * - методы с Connection НЕ закрывают соединение * - методы без Connection сами открывают и закрывают соединение * * ЛОГИКА time_ms: * - БД принимает запись только если она "новее" (time_ms строго больше текущего). * - Реализовано атомарно одним SQL: UPSERT + WHERE users_params.time_ms < excluded.time_ms * * Возврат результата: * - upsertIfNewer(...) возвращает количество изменённых строк: * 1 = вставили/обновили * 0 = проигнорировали (запись уже новее или равная) */ public final class UserParamsDAO { private static volatile UserParamsDAO instance; private final SqliteDbController db = SqliteDbController.getInstance(); private UserParamsDAO() { } public static UserParamsDAO getInstance() { if (instance == null) { synchronized (UserParamsDAO.class) { if (instance == null) instance = new UserParamsDAO(); } } return instance; } // -------------------- UPSERT (IF NEWER) -------------------- /** * Атомарный UPSERT "только если новее". * * @return 1 если вставили/обновили; 0 если запись не тронули (existing.time_ms >= incoming.time_ms). */ public int upsertIfNewer(Connection c, UserParamEntry e) throws SQLException { String sql = """ INSERT INTO users_params ( login, param, time_ms, value, device_key, signature ) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT(login, param) DO UPDATE SET time_ms = excluded.time_ms, value = excluded.value, device_key = excluded.device_key, signature = excluded.signature WHERE users_params.time_ms < excluded.time_ms """; try (PreparedStatement ps = c.prepareStatement(sql)) { ps.setString(1, e.getLogin()); ps.setString(2, e.getParam()); ps.setLong(3, e.getTimeMs()); ps.setString(4, e.getValue()); if (e.getDeviceKey() != null) ps.setString(5, e.getDeviceKey()); else ps.setNull(5, Types.VARCHAR); if (e.getSignature() != null) ps.setString(6, e.getSignature()); else ps.setNull(6, Types.VARCHAR); return ps.executeUpdate(); // 1 или 0 } } /** То же самое, но сам открывает/закрывает соединение. */ public int upsertIfNewer(UserParamEntry e) throws SQLException { try (Connection c = db.getConnection()) { return upsertIfNewer(c, e); } } // -------------------- SELECT -------------------- /** Получить параметр по (login,param) с внешним соединением. Соединение НЕ закрывает. */ public UserParamEntry getByLoginAndParam(Connection c, String login, String param) throws SQLException { String sql = """ SELECT login, param, time_ms, value, device_key, signature FROM users_params WHERE login = ? AND param = ? LIMIT 1 """; try (PreparedStatement ps = c.prepareStatement(sql)) { ps.setString(1, login); ps.setString(2, param); try (ResultSet rs = ps.executeQuery()) { if (!rs.next()) return null; return mapRow(rs); } } } /** Получить параметр по (login,param) без внешнего соединения. Сам открывает/закрывает. */ public UserParamEntry getByLoginAndParam(String login, String param) throws SQLException { try (Connection c = db.getConnection()) { return getByLoginAndParam(c, login, param); } } /** Получить все параметры пользователя с внешним соединением. */ public List getByLogin(Connection c, String login) throws SQLException { String sql = """ SELECT login, param, time_ms, value, device_key, signature FROM users_params WHERE login = ? ORDER BY time_ms DESC """; List list = new ArrayList<>(); try (PreparedStatement ps = c.prepareStatement(sql)) { ps.setString(1, login); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) list.add(mapRow(rs)); } } return list; } /** Получить все параметры пользователя без внешнего соединения. */ public List getByLogin(String login) throws SQLException { try (Connection c = db.getConnection()) { return getByLogin(c, login); } } // -------------------- MAPPER -------------------- private static UserParamEntry mapRow(ResultSet rs) throws SQLException { UserParamEntry e = new UserParamEntry(); e.setLogin(rs.getString("login")); e.setParam(rs.getString("param")); e.setTimeMs(rs.getLong("time_ms")); e.setValue(rs.getString("value")); String dk = rs.getString("device_key"); if (rs.wasNull()) dk = null; e.setDeviceKey(dk); String sig = rs.getString("signature"); if (rs.wasNull()) sig = null; e.setSignature(sig); return e; } }