diff --git a/src/main/java/database/DaoException.java b/src/main/java/database/DaoException.java new file mode 100644 index 0000000000..3190d233ce --- /dev/null +++ b/src/main/java/database/DaoException.java @@ -0,0 +1,10 @@ +package database; + +import org.jdbi.v3.core.JdbiException; + +public class DaoException extends JdbiException { + + public DaoException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/database/NoteDao.java b/src/main/java/database/NoteDao.java new file mode 100644 index 0000000000..d945505cdf --- /dev/null +++ b/src/main/java/database/NoteDao.java @@ -0,0 +1,88 @@ +package database; + +import model.Note; +import org.jdbi.v3.core.Handle; +import org.jdbi.v3.core.JdbiException; +import tools.DatabaseConnection; + +import java.util.List; +import java.util.Optional; + +public class NoteDao { + + public static void save(Note note) { + try (Handle handle = DatabaseConnection.getHandle()) { + handle.createScript(""" + INSERT INTO notes (`message`, `from`, `to`, `timestamp`, `fame`, `deleted`) + VALUES (?, ?, ?, ?, ?, ?)""") + .bind(0, note.message()) + .bind(1, note.from()) + .bind(2, note.to()) + .bind(3, note.timestamp()) + .bind(4, note.fame()) + .bind(5, 0) + .execute(); + } catch (JdbiException e) { + throw new DaoException("Failed to save note: %s".formatted(note.toString()), e); + } + } + + public static List findAllByTo(String to) { + try (Handle handle = DatabaseConnection.getHandle()) { + return handle.createQuery(""" + SELECT * + FROM notes + WHERE `deleted` = 0 + AND `to` = ?""") + .bind(0, to) + .mapTo(Note.class) + .list(); + } catch (JdbiException e) { + throw new DaoException("Failed to find notes with \"to\": %s".formatted(to), e); + } + } + + public static Optional delete(int id) { + try (Handle handle = DatabaseConnection.getHandle()) { + Optional note = findById(handle, id); + if (note.isEmpty()) { + return Optional.empty(); + } + deleteById(handle, id); + + return note; + } catch (JdbiException e) { + throw new DaoException("Failed to delete note with id: %d".formatted(id), e); + } + } + + private static Optional findById(Handle handle, int id) { + final Optional note; + try { + note = handle.createQuery(""" + SELECT * + FROM notes + WHERE `deleted` = 0 + AND `id` = ?""") + .bind(0, id) + .mapTo(Note.class) + .findOne(); + } catch (JdbiException e) { + throw new DaoException("Failed find note with id %s".formatted(id), e); + } + return note; + } + + private static void deleteById(Handle handle, int id) { + try { + handle.createUpdate(""" + UPDATE notes + SET `deleted` = 1 + WHERE `id` = ?""") + .bind(0, id) + .execute(); + } catch (JdbiException e) { + throw new DaoException("Failed to delete note with id %d".formatted(id), e); + } + } +} diff --git a/src/main/java/database/NoteRowMapper.java b/src/main/java/database/NoteRowMapper.java new file mode 100644 index 0000000000..b035741859 --- /dev/null +++ b/src/main/java/database/NoteRowMapper.java @@ -0,0 +1,22 @@ +package database; + +import model.Note; +import org.jdbi.v3.core.mapper.RowMapper; +import org.jdbi.v3.core.statement.StatementContext; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class NoteRowMapper implements RowMapper { + + @Override + public Note map(ResultSet rs, StatementContext ctx) throws SQLException { + int id = rs.getInt("id"); + String message = rs.getString("message"); + String from = rs.getString("from"); + String to = rs.getString("to"); + long timestamp = rs.getLong("timestamp"); + int fame = rs.getInt("fame"); + return new Note(id, message, from, to, timestamp, fame); + } +} diff --git a/src/main/java/model/Note.java b/src/main/java/model/Note.java index 677f096c5c..9b85643d8d 100644 --- a/src/main/java/model/Note.java +++ b/src/main/java/model/Note.java @@ -11,11 +11,11 @@ public record Note(int id, String message, String from, String to, long timestam Objects.requireNonNull(to); } - public Note createNormal(String message, String from, String to, long timestamp) { + public static Note createNormal(String message, String from, String to, long timestamp) { return new Note(PLACEHOLDER_ID, message, from, to, timestamp, 0); } - public Note createGift(String message, String from, String to, long timestamp) { + public static Note createGift(String message, String from, String to, long timestamp) { return new Note(PLACEHOLDER_ID, message, from, to, timestamp, 1); } } diff --git a/src/main/java/net/server/channel/handlers/NoteActionHandler.java b/src/main/java/net/server/channel/handlers/NoteActionHandler.java index 1918820e7e..7c963691d6 100644 --- a/src/main/java/net/server/channel/handlers/NoteActionHandler.java +++ b/src/main/java/net/server/channel/handlers/NoteActionHandler.java @@ -22,19 +22,23 @@ package net.server.channel.handlers; import client.Client; +import database.DaoException; +import database.NoteDao; +import model.Note; import net.AbstractPacketHandler; import net.packet.InPacket; -import tools.DatabaseConnection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import tools.PacketCreator; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Optional; public final class NoteActionHandler extends AbstractPacketHandler { + private static final Logger log = LoggerFactory.getLogger(NoteActionHandler.class); + @Override - public final void handlePacket(InPacket p, Client c) { + public void handlePacket(InPacket p, Client c) { int action = p.readByte(); if (action == 0 && c.getPlayer().getCashShop().getAvailableNotes() > 0) { String charname = p.readString(); @@ -58,24 +62,20 @@ public final class NoteActionHandler extends AbstractPacketHandler { int id = p.readInt(); p.readByte(); //Fame, but we read it from the database :) - try (Connection con = DatabaseConnection.getConnection()) { - try (PreparedStatement ps = con.prepareStatement("SELECT `fame` FROM notes WHERE id=? AND deleted=0")) { - ps.setInt(1, id); - try (ResultSet rs = ps.executeQuery()) { - if (rs.next()) { - fame += rs.getInt("fame"); - } - - } - } - - try (PreparedStatement ps = con.prepareStatement("UPDATE notes SET `deleted` = 1 WHERE id = ?")) { - ps.setInt(1, id); - ps.executeUpdate(); - } - } catch (SQLException e) { - e.printStackTrace(); + final Optional note; + try { + note = NoteDao.delete(id); + } catch (DaoException e) { + log.error("Failed to delete note {}", id, e); + continue; } + + if (note.isEmpty()) { + log.warn("Note with id {} not able to be deleted. Already deleted?", id); + continue; + } + + fame += note.get().fame(); } if (fame > 0) { c.getPlayer().gainFame(fame); diff --git a/src/main/java/tools/DatabaseConnection.java b/src/main/java/tools/DatabaseConnection.java index a5607477a6..433f90ae19 100644 --- a/src/main/java/tools/DatabaseConnection.java +++ b/src/main/java/tools/DatabaseConnection.java @@ -3,6 +3,7 @@ package tools; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import config.YamlConfig; +import database.NoteRowMapper; import org.jdbi.v3.core.Handle; import org.jdbi.v3.core.Jdbi; import org.slf4j.Logger; @@ -99,7 +100,7 @@ public class DatabaseConnection { } private static void initializeJdbi(DataSource dataSource) { - // TODO: configure row mappers - jdbi = Jdbi.create(dataSource); + jdbi = Jdbi.create(dataSource) + .registerRowMapper(new NoteRowMapper()); } }