From ad335a545a0745355c0a02a24c476d18bf9ab535 Mon Sep 17 00:00:00 2001 From: Benedikt Galbavy Date: Tue, 2 Jan 2024 17:34:35 +0100 Subject: [PATCH] added db handler ~6h work --- .../mtcg/application/service/UserService.java | 1 - .../application/service/schemas/Card.java | 4 - .../service/schemas/TradingDeal.java | 4 - .../service/schemas/UserCredentials.java | 4 - .../application/service/schemas/UserData.java | 5 - .../service/schemas/UserStats.java | 4 - src/at/nanopenguin/mtcg/db/DbQuery.java | 138 ++++++++++++++++++ src/at/nanopenguin/mtcg/db/SqlCommand.java | 8 + src/at/nanopenguin/mtcg/db/Table.java | 10 ++ src/at/nanopenguin/mtcg/http/HttpStatus.java | 8 +- .../nanopenguin/mtcg/http/RequestHandler.java | 7 +- src/at/nanopenguin/mtcg/http/Server.java | 9 +- 12 files changed, 164 insertions(+), 38 deletions(-) create mode 100644 src/at/nanopenguin/mtcg/db/DbQuery.java create mode 100644 src/at/nanopenguin/mtcg/db/SqlCommand.java create mode 100644 src/at/nanopenguin/mtcg/db/Table.java diff --git a/src/at/nanopenguin/mtcg/application/service/UserService.java b/src/at/nanopenguin/mtcg/application/service/UserService.java index e145b7a..beb0142 100644 --- a/src/at/nanopenguin/mtcg/application/service/UserService.java +++ b/src/at/nanopenguin/mtcg/application/service/UserService.java @@ -1,6 +1,5 @@ package at.nanopenguin.mtcg.application.service; -import at.nanopenguin.mtcg.application.service.schemas.UserCredentials; import at.nanopenguin.mtcg.http.HttpMethod; import at.nanopenguin.mtcg.http.HttpRequest; import at.nanopenguin.mtcg.http.HttpStatus; diff --git a/src/at/nanopenguin/mtcg/application/service/schemas/Card.java b/src/at/nanopenguin/mtcg/application/service/schemas/Card.java index 91b0187..7c63ff4 100644 --- a/src/at/nanopenguin/mtcg/application/service/schemas/Card.java +++ b/src/at/nanopenguin/mtcg/application/service/schemas/Card.java @@ -1,11 +1,7 @@ package at.nanopenguin.mtcg.application.service.schemas; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -@JsonSerialize -@JsonDeserialize @JsonIgnoreProperties(ignoreUnknown = true) public record Card(String id, String name, Float damage) { } diff --git a/src/at/nanopenguin/mtcg/application/service/schemas/TradingDeal.java b/src/at/nanopenguin/mtcg/application/service/schemas/TradingDeal.java index 25ea177..3cc89fd 100644 --- a/src/at/nanopenguin/mtcg/application/service/schemas/TradingDeal.java +++ b/src/at/nanopenguin/mtcg/application/service/schemas/TradingDeal.java @@ -1,11 +1,7 @@ package at.nanopenguin.mtcg.application.service.schemas; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -@JsonSerialize -@JsonDeserialize @JsonIgnoreProperties(ignoreUnknown = true) public record TradingDeal(String id, String cardToTrade, String type, Float minimumDamage) { } diff --git a/src/at/nanopenguin/mtcg/application/service/schemas/UserCredentials.java b/src/at/nanopenguin/mtcg/application/service/schemas/UserCredentials.java index 9656ee9..dc1cb40 100644 --- a/src/at/nanopenguin/mtcg/application/service/schemas/UserCredentials.java +++ b/src/at/nanopenguin/mtcg/application/service/schemas/UserCredentials.java @@ -1,11 +1,7 @@ package at.nanopenguin.mtcg.application.service.schemas; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -@JsonSerialize -@JsonDeserialize @JsonIgnoreProperties(ignoreUnknown = true) public record UserCredentials(String username, String password) { } diff --git a/src/at/nanopenguin/mtcg/application/service/schemas/UserData.java b/src/at/nanopenguin/mtcg/application/service/schemas/UserData.java index 816d6c0..bd1d332 100644 --- a/src/at/nanopenguin/mtcg/application/service/schemas/UserData.java +++ b/src/at/nanopenguin/mtcg/application/service/schemas/UserData.java @@ -1,12 +1,7 @@ package at.nanopenguin.mtcg.application.service.schemas; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -@JsonSerialize -@JsonDeserialize @JsonIgnoreProperties(ignoreUnknown = true) public record UserData(String name, String bio, String image) { } diff --git a/src/at/nanopenguin/mtcg/application/service/schemas/UserStats.java b/src/at/nanopenguin/mtcg/application/service/schemas/UserStats.java index d50657a..5863e66 100644 --- a/src/at/nanopenguin/mtcg/application/service/schemas/UserStats.java +++ b/src/at/nanopenguin/mtcg/application/service/schemas/UserStats.java @@ -1,11 +1,7 @@ package at.nanopenguin.mtcg.application.service.schemas; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -@JsonSerialize -@JsonDeserialize @JsonIgnoreProperties(ignoreUnknown = true) public record UserStats(String name, Integer elo, Integer wins, Integer losses) { } diff --git a/src/at/nanopenguin/mtcg/db/DbQuery.java b/src/at/nanopenguin/mtcg/db/DbQuery.java new file mode 100644 index 0000000..b52384e --- /dev/null +++ b/src/at/nanopenguin/mtcg/db/DbQuery.java @@ -0,0 +1,138 @@ +package at.nanopenguin.mtcg.db; + +import lombok.Builder; +import lombok.NonNull; +import lombok.Singular; +import lombok.val; + +import java.sql.*; +import java.util.*; +import java.util.stream.Collectors; + +@Builder +public final class DbQuery { + private static final String connectionString = "jdbc:postgres://localhost:5432/mydb?user=postgres&password=postgres"; + + @NonNull + private final SqlCommand command; + @NonNull + private final Table table; + @Singular + private List columns; + @Singular + private SortedMap parameters; + @Singular + private SortedMap conditions; + + public static class DbQueryBuilder { + public ResultSet executeQuery() throws SQLException { + DbQuery dbQuery = this.build(); + if (dbQuery.command != SqlCommand.SELECT) throw new SQLException(); + return dbQuery.read(); + } + + public int executeUpdate() throws SQLException { + DbQuery dbQuery = this.build(); + return switch (dbQuery.command) { + case INSERT -> dbQuery.create(); + case UPDATE -> dbQuery.update(); + case DELETE -> dbQuery.delete(); + default -> throw new SQLException(); + }; + } + } + + private static Connection connect() throws SQLException { + return DriverManager.getConnection(connectionString); + } + + private String buildParameterizedString(@NonNull SortedMap parameters) { + if (!parameters.isEmpty()) { + return String.join(" = ?, ", parameters.keySet()) + " = ?"; + } + return ""; + } + + private int create() throws SQLException { + if (this.parameters.isEmpty()) throw new SQLException("No parameters provided for INSERT statement."); + + try (Connection connection = connect()) { + String columns = this.parameters.keySet().stream() + .filter(columnName -> columnName.matches("[a-zA-Z0-9_]+")) + .collect(Collectors.joining(", ")); + + String sql = String.format("INSERT INTO %s (%s) VALUES (%s);", table.table, columns, String.join(", ", Collections.nCopies(this.parameters.size(), "?"))); + + try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) { + int i = 1; + for (val entry : this.parameters.entrySet()) { + preparedStatement.setObject(i++, entry.getValue()); + } + + return preparedStatement.executeUpdate(); + } + } + } + + private ResultSet read() throws SQLException { + try (Connection connection = connect()) { + StringJoiner columnJoiner = new StringJoiner(", "); + if (this.columns.isEmpty()) { + columnJoiner.add("*"); + } + this.columns.forEach(columnJoiner::add); + + String sql = String.format("SELECT %s FROM %s%s;", columnJoiner, table.table, + this.conditions.isEmpty() ? "" : " WHERE (" + this.buildParameterizedString(this.conditions) + ")"); + + try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) { + int i = 1; + for (val entry : this.conditions.entrySet()) { + preparedStatement.setObject(i++, entry.getValue()); + } + + return preparedStatement.executeQuery(); + } + } + } + + private int update() throws SQLException { + if (this.parameters.isEmpty()) throw new SQLException(); + if (this.conditions.isEmpty()) throw new SQLException(); + + try (Connection connection = connect()) { + String sql = String.format("UPDATE %s SET %s WHERE (%s);", table.table, + this.buildParameterizedString(this.parameters), + this.buildParameterizedString(this.conditions)); + + PreparedStatement preparedStatement = connection.prepareStatement(sql); + + int i = 1; + for (val entry : this.parameters.entrySet()) { + preparedStatement.setObject(i++, entry.getValue()); + } + for (val entry : this.conditions.entrySet()) { + preparedStatement.setObject(i++, entry.getValue()); + } + + return preparedStatement.executeUpdate(); + } + } + + private int delete() throws SQLException { + if (this.conditions.isEmpty()) throw new SQLException(); + try (Connection connection = connect()) { + + String sql = String.format("DELETE FROM %s WHERE (%s);", table.table, this.buildParameterizedString(this.conditions)); + + try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) { + int i = 1; + for (val entry : this.conditions.entrySet()) { + preparedStatement.setObject(i++, entry.getValue()); + } + + return preparedStatement.executeUpdate(); + } + } + } +} diff --git a/src/at/nanopenguin/mtcg/db/SqlCommand.java b/src/at/nanopenguin/mtcg/db/SqlCommand.java new file mode 100644 index 0000000..515927b --- /dev/null +++ b/src/at/nanopenguin/mtcg/db/SqlCommand.java @@ -0,0 +1,8 @@ +package at.nanopenguin.mtcg.db; + +public enum SqlCommand { + INSERT, + SELECT, + UPDATE, + DELETE, +} diff --git a/src/at/nanopenguin/mtcg/db/Table.java b/src/at/nanopenguin/mtcg/db/Table.java new file mode 100644 index 0000000..8217aeb --- /dev/null +++ b/src/at/nanopenguin/mtcg/db/Table.java @@ -0,0 +1,10 @@ +package at.nanopenguin.mtcg.db; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public enum Table { + USERS("users"); + + public final String table; +} diff --git a/src/at/nanopenguin/mtcg/http/HttpStatus.java b/src/at/nanopenguin/mtcg/http/HttpStatus.java index 032d13f..7ab3d0f 100644 --- a/src/at/nanopenguin/mtcg/http/HttpStatus.java +++ b/src/at/nanopenguin/mtcg/http/HttpStatus.java @@ -1,5 +1,8 @@ package at.nanopenguin.mtcg.http; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor public enum HttpStatus { OK(200, "OK"), CREATED(201, "Created"), @@ -15,9 +18,4 @@ public enum HttpStatus { public final int statusCode; public final String statusMessage; - - HttpStatus(int code, String message) { - this.statusCode = code; - this.statusMessage = message; - } } diff --git a/src/at/nanopenguin/mtcg/http/RequestHandler.java b/src/at/nanopenguin/mtcg/http/RequestHandler.java index 2935241..d45bbbe 100644 --- a/src/at/nanopenguin/mtcg/http/RequestHandler.java +++ b/src/at/nanopenguin/mtcg/http/RequestHandler.java @@ -2,6 +2,7 @@ package at.nanopenguin.mtcg.http; import at.nanopenguin.mtcg.application.service.Service; import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.RequiredArgsConstructor; import java.io.BufferedReader; import java.io.IOException; @@ -9,15 +10,11 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; +@RequiredArgsConstructor public class RequestHandler implements Runnable { private final Socket serviceSocket; private final Router router; - public RequestHandler(Socket serviceSocket, Router router) { - this.serviceSocket = serviceSocket; - this.router = router; - } - @Override public void run() { try { diff --git a/src/at/nanopenguin/mtcg/http/Server.java b/src/at/nanopenguin/mtcg/http/Server.java index 38ecb62..f9075d5 100644 --- a/src/at/nanopenguin/mtcg/http/Server.java +++ b/src/at/nanopenguin/mtcg/http/Server.java @@ -1,22 +1,19 @@ package at.nanopenguin.mtcg.http; +import lombok.RequiredArgsConstructor; + import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +@RequiredArgsConstructor public class Server { private final int port; // 16-bit unsigned, not enforced private final int threads; private final Router router; - public Server(int port, int threads, Router router) { - this.port = port; - this.threads = threads; - this.router = router; - } - public void start() throws IOException { try (ServerSocket listener = new ServerSocket(this.port)) {