working on calendar application
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -2,7 +2,9 @@
|
||||
package de.srsoftware.cal.db;
|
||||
|
||||
import de.srsoftware.cal.api.Appointment;
|
||||
import de.srsoftware.tools.Result;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -22,9 +24,19 @@ public interface Database {
|
||||
* @param count the maximum number of appointments to return
|
||||
* @param offset the number of appointments to skip
|
||||
* @return the list of appointments fetched from the db
|
||||
* @throws SQLException if the appointments cannot be fetched from the DB
|
||||
*/
|
||||
public List<Appointment> list(Integer count, Integer offset) throws SQLException;
|
||||
|
||||
/**
|
||||
* list appointments unfiltered
|
||||
* @param from restrict appointments to times after this date time
|
||||
* @param till restrict appointments to times before this date time
|
||||
* @return list of appointments in this time span
|
||||
* @throws SQLException if the appointments cannot be fetched from the DB
|
||||
*/
|
||||
public List<Appointment> list(LocalDateTime from, LocalDateTime till) throws SQLException;
|
||||
|
||||
/**
|
||||
* list appointments
|
||||
* @param tags only list appointments which have matching tags
|
||||
@@ -33,4 +45,6 @@ public interface Database {
|
||||
* @return the list of appointments fetched from the db
|
||||
*/
|
||||
public List<Appointment> listByTags(Set<String> tags, Integer count, Integer offset);
|
||||
|
||||
Result<Appointment> loadEvent(long id);
|
||||
}
|
||||
|
||||
@@ -1,31 +1,30 @@
|
||||
/* © SRSoftware 2024 */
|
||||
package de.srsoftware.cal.db;
|
||||
|
||||
import static de.srsoftware.tools.Optionals.allEmpty;
|
||||
import static de.srsoftware.tools.Optionals.nullable;
|
||||
import static de.srsoftware.tools.Optionals.*;
|
||||
import static de.srsoftware.tools.Strings.camelCase;
|
||||
import static de.srsoftware.tools.jdbc.Condition.equal;
|
||||
import static de.srsoftware.tools.jdbc.Condition.moreThan;
|
||||
import static de.srsoftware.tools.jdbc.Query.MARK;
|
||||
import static java.lang.System.Logger.Level.*;
|
||||
|
||||
import de.srsoftware.cal.BaseAppointment;
|
||||
import de.srsoftware.cal.api.Appointment;
|
||||
import de.srsoftware.tools.Calc;
|
||||
import de.srsoftware.tools.Error;
|
||||
import de.srsoftware.tools.Payload;
|
||||
import de.srsoftware.tools.Result;
|
||||
import de.srsoftware.tools.jdbc.Query;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.sql.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
public class MariaDB implements Database {
|
||||
private static final String SELECT_APPOINTMENTS = "SELECT * FROM appointments";
|
||||
private static final String SELECT_VERSION = "SELECT value FROM config WHERE keyname = 'dbversion'";
|
||||
private static final String ADD_SLUG = "ALTER TABLE appointments ADD slug VARCHAR(255) UNIQUE";
|
||||
private static final String INSERT_HASH = "INSERT INTO appointment_hashes (aid, hash) values (?, ?) ON DUPLICATE KEY UPDATE hash=hash;";
|
||||
private static final String UPDATE_DB_VERSION = "UPDATE config SET value = ? WHERE keyname = 'dbversion'";
|
||||
private static final String INSERT_SLUG = "UPDATE IGNORE appointments SET slug = ? WHERE aid = ?";
|
||||
private static Connection connection;
|
||||
private static final System.Logger LOG = System.getLogger(MariaDB.class.getSimpleName());
|
||||
private static final String ADD_SLUG = "ALTER TABLE appointments ADD slug VARCHAR(255) UNIQUE";
|
||||
private static final String APPOINTMENTS = "appointments";
|
||||
private static final String AID = "aid";
|
||||
private static Connection connection;
|
||||
|
||||
private MariaDB(Connection conn) throws SQLException {
|
||||
connection = conn;
|
||||
@@ -33,7 +32,8 @@ public class MariaDB implements Database {
|
||||
}
|
||||
|
||||
private void applyUpdates() throws SQLException {
|
||||
var rs = Query.of(SELECT_VERSION).execute(connection);
|
||||
LOG.log(INFO, "Checking for updates…");
|
||||
var rs = Query.select("value").from("config").where("keyname", equal("dbversion")).exec(connection);
|
||||
var version = 0;
|
||||
if (rs.next()) {
|
||||
version = rs.getInt("value");
|
||||
@@ -48,35 +48,39 @@ public class MariaDB implements Database {
|
||||
}
|
||||
|
||||
private void update1() throws SQLException {
|
||||
LOG.log(INFO, "Updating db scheme from version 1 to 2…");
|
||||
var list = new ArrayList<Appointment>();
|
||||
|
||||
connection.setAutoCommit(false);
|
||||
|
||||
Query.of(ADD_SLUG).statement(connection).execute();
|
||||
LOG.log(DEBUG, "Adding slug column…");
|
||||
connection.prepareStatement(ADD_SLUG).execute();
|
||||
var slugMap = new HashMap<Long, String>();
|
||||
var rs = Query.of(SELECT_APPOINTMENTS).execute(connection);
|
||||
LOG.log(DEBUG, "Reading existing appointments…");
|
||||
var rs = Query.select("*").from("appointments").exec(connection);
|
||||
while (rs.next()) {
|
||||
var id = rs.getLong("aid");
|
||||
var location = nullable(rs.getString("location"));
|
||||
var id = rs.getLong(AID);
|
||||
var location = nullable(nullIfEmpty(rs.getString("location")));
|
||||
if (location.isEmpty()) continue;
|
||||
var title = rs.getString("title");
|
||||
LOG.log(TRACE, () -> "%s: %s".formatted(id, title));
|
||||
var descr = rs.getString("description");
|
||||
if (allEmpty(title, descr)) continue;
|
||||
var start = nullable(rs.getTimestamp("start"));
|
||||
if (start.isEmpty()) continue;
|
||||
var slug = "%s@%s".formatted(start.get().toLocalDateTime(), camelCase(location.get()));
|
||||
var slug = "%s@%s".formatted(start.get().toLocalDateTime(), camelCase(location.get().replace(",", "")));
|
||||
if (slug.length() > 250) slug = slug.substring(0, 250);
|
||||
slugMap.put(id, slug);
|
||||
}
|
||||
rs.close();
|
||||
var stmt = Query.of(INSERT_SLUG).statement(connection);
|
||||
LOG.log(DEBUG, "Creating slugs…");
|
||||
var query = Query.updateIgnore("appointments").set("slug").where(AID, equal(MARK)).prepare(connection);
|
||||
for (var entry : slugMap.entrySet()) {
|
||||
stmt.setString(1, entry.getValue());
|
||||
stmt.setLong(2, entry.getKey());
|
||||
stmt.execute();
|
||||
query.apply(entry.getValue(), entry.getKey());
|
||||
}
|
||||
stmt = Query.of(UPDATE_DB_VERSION).statement(connection);
|
||||
stmt.setLong(1, 2);
|
||||
stmt.execute();
|
||||
|
||||
LOG.log(DEBUG, "Writing new db version marker…");
|
||||
Query.update("config").set("value").where("keyname", equal("dbversion")).prepare(connection).apply(2);
|
||||
connection.setAutoCommit(true);
|
||||
}
|
||||
|
||||
@@ -94,23 +98,61 @@ public class MariaDB implements Database {
|
||||
return new MariaDB(DriverManager.getConnection(jdbc, user, pass));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<Appointment> list(Integer count, Integer offset) throws SQLException {
|
||||
var list = new ArrayList<Appointment>();
|
||||
var results = Query.of(SELECT_APPOINTMENTS).orderBy("start").execute(connection);
|
||||
while (results.next()) {
|
||||
var id = results.getInt("aid");
|
||||
var title = results.getString("title");
|
||||
var description = results.getString("description");
|
||||
if (allEmpty(title, description)) continue;
|
||||
var start = results.getTimestamp("start").toLocalDateTime();
|
||||
var end = nullable(results.getTimestamp("end")).map(Timestamp::toLocalDateTime).orElse(null);
|
||||
var location = results.getString("location");
|
||||
var slug = results.getString("slug");
|
||||
if (slug == null) slug = Calc.hash(start + "@" + location).orElse(null);
|
||||
list.add(new BaseAppointment(id, title, description, start, end, location, slug));
|
||||
var results = Query.select("*").from(APPOINTMENTS).sort("start").exec(connection);
|
||||
while (results.next()) createAppointmentOf(results).optional().ifPresent(list::add);
|
||||
results.close();
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Appointment> loadEvent(long id) {
|
||||
try {
|
||||
var rs = Query //
|
||||
.select("%s.*".formatted(APPOINTMENTS), "GROUP_CONCAT(keyword) AS tags")
|
||||
.from(APPOINTMENTS)
|
||||
.leftJoin(AID, "appointment_tags", AID)
|
||||
.leftJoin("tid", "tags", "tid")
|
||||
.groupBy(AID)
|
||||
.where("%s.%s".formatted(APPOINTMENTS, AID), equal(id))
|
||||
.exec(connection);
|
||||
return rs.next() ? createAppointmentOf(rs) : Error.format("Failed to find appointment with id %s", id);
|
||||
} catch (SQLException e) {
|
||||
return Error.of("Failed to load appointment with id = %s".formatted(id), e);
|
||||
}
|
||||
}
|
||||
|
||||
private Result<Appointment> createAppointmentOf(ResultSet results) throws SQLException {
|
||||
var id = results.getInt(AID);
|
||||
var title = results.getString("title");
|
||||
var description = results.getString("description");
|
||||
if (allEmpty(title, description)) return Error.format("Title and Description of appointment %s are empty", id);
|
||||
var start = results.getTimestamp("start").toLocalDateTime();
|
||||
var end = nullable(results.getTimestamp("end")).map(Timestamp::toLocalDateTime).orElse(null);
|
||||
var location = results.getString("location");
|
||||
var slug = results.getString("slug");
|
||||
var tags = nullIfEmpty(results.getString("tags"));
|
||||
if (slug == null) slug = Calc.hash(start + "@" + location).orElse(null);
|
||||
var appointment = new BaseAppointment(id, title, description, start, end, location, slug);
|
||||
if (tags != null) appointment.tags(tags.split(","));
|
||||
return Payload.of(appointment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Appointment> list(LocalDateTime from, LocalDateTime till) throws SQLException {
|
||||
var list = new ArrayList<Appointment>();
|
||||
var results = Query //
|
||||
.select("appointments.*", "GROUP_CONCAT(keyword) AS tags")
|
||||
.from(APPOINTMENTS)
|
||||
.leftJoin(AID, "appointment_tags", AID)
|
||||
.leftJoin("tid", "tags", "tid")
|
||||
.groupBy(AID)
|
||||
.sort("start")
|
||||
.where("start", moreThan(from))
|
||||
.exec(connection);
|
||||
while (results.next()) createAppointmentOf(results).optional().ifPresent(list::add);
|
||||
results.close();
|
||||
return list;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user