177 lines
6.2 KiB
Java
177 lines
6.2 KiB
Java
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<UserParamEntry> 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<UserParamEntry> 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<UserParamEntry> 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;
|
||
}
|
||
} |