added db handler

~6h work
This commit is contained in:
Benedikt Galbavy 2024-01-02 17:34:35 +01:00
parent fad46df96c
commit ad335a545a
12 changed files with 164 additions and 38 deletions

View File

@ -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;

View File

@ -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) {
}

View File

@ -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) {
}

View File

@ -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) {
}

View File

@ -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) {
}

View File

@ -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) {
}

View File

@ -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<String> columns;
@Singular
private SortedMap<String, Object> parameters;
@Singular
private SortedMap<String, Object> 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<String, Object> 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();
}
}
}
}

View File

@ -0,0 +1,8 @@
package at.nanopenguin.mtcg.db;
public enum SqlCommand {
INSERT,
SELECT,
UPDATE,
DELETE,
}

View File

@ -0,0 +1,10 @@
package at.nanopenguin.mtcg.db;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum Table {
USERS("users");
public final String table;
}

View File

@ -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;
}
}

View File

@ -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 {

View File

@ -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)) {