From d61e099bef313a65e42e8beba6baf77be06523ad Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Sun, 14 Dec 2025 14:27:34 +0000 Subject: [PATCH 1/3] add deadline --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d20aaf9..7071577 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/339Lr3BJ) ### How the tests work (and Docker requirement) This project ships with an end‑to‑end CLI integration test suite that uses Testcontainers to spin up a temporary MySQL database. From 6a7ab7dfea7c81a9b0f3580e8fb8f0373272f00f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alice=20Wers=C3=A9n?= Date: Sun, 14 Dec 2025 17:32:45 +0100 Subject: [PATCH 2/3] Initial commit: added Main.java with login logic --- src/main/java/com/example/Main.java | 73 +++++++++++++++++++---------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 6dc6fbd..8a0c776 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -2,12 +2,15 @@ import java.sql.Connection; import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; import java.util.Arrays; +import java.util.Scanner; public class Main { - static void main(String[] args) { + public static void main(String[] args) { if (isDevMode(args)) { DevDatabaseInitializer.start(); } @@ -15,48 +18,68 @@ static void main(String[] args) { } public void run() { - // Resolve DB settings with precedence: System properties -> Environment variables + // Hämtar databasinställningar från system properties/miljövariabler. String jdbcUrl = resolveConfig("APP_JDBC_URL", "APP_JDBC_URL"); String dbUser = resolveConfig("APP_DB_USER", "APP_DB_USER"); String dbPass = resolveConfig("APP_DB_PASS", "APP_DB_PASS"); + // Kontrollerar att alla inställningar finns, annars kasta ett tydligt fel. if (jdbcUrl == null || dbUser == null || dbPass == null) { throw new IllegalStateException( "Missing DB configuration. Provide APP_JDBC_URL, APP_DB_USER, APP_DB_PASS " + - "as system properties (-Dkey=value) or environment variables."); + "as system properties (-Dkey=value) or environment variables." + ); } - + // Skapar en anslutning till databasen. try (Connection connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass)) { - } catch (SQLException e) { + + // Skapar en Scanner för att läsa input från användaren. + Scanner scanner = new Scanner(System.in); + + System.out.print("Username: "); // Ber användaren att skriva in användernamnet. + String username = scanner.nextLine(); + + System.out.print("Password: "); // Ber användaren att skriva in lösenordet. + String password = scanner.nextLine(); + + // En SQL-fråga för att kontrollera användarnamn och lösenord i databasen. + String loginSql = "SELECT * FROM account WHERE name = ? AND password = ?"; + try (PreparedStatement stmt = connection.prepareStatement(loginSql)) { + stmt.setString(1, username); + stmt.setString(2, password); + + try (ResultSet rs = stmt.executeQuery()) { // Om inget konto hittas, så misslyckas inlogget. + if (!rs.next()) { + System.out.println("Invalid login."); + return; // Avslutar run() om inloggningen misslyckas. + } + } + } + + System.out.println("Login successful!"); //Om inlogg lyckas. + // TODO: Här kan du börja skriva meny-loop + + } catch (SQLException e) { // Om något går fel med databasen, så kastas ett undantag. throw new RuntimeException(e); } - //Todo: Starting point for your code } - - /** - * Determines if the application is running in development mode based on system properties, - * environment variables, or command-line arguments. - * - * @param args an array of command-line arguments - * @return {@code true} if the application is in development mode; {@code false} otherwise - */ + // Kollar om programmet körs i DevMode (ett utvecklingsläge). private static boolean isDevMode(String[] args) { - if (Boolean.getBoolean("devMode")) //Add VM option -DdevMode=true - return true; - if ("true".equalsIgnoreCase(System.getenv("DEV_MODE"))) //Environment variable DEV_MODE=true - return true; - return Arrays.asList(args).contains("--dev"); //Argument --dev + // Kollar om VM option -DdevMode=true är tillsatt. + if (Boolean.getBoolean("devMode")) return true; + // Kollar om environment variabeln DEV_MODE=true är tillsatt. + if ("true".equalsIgnoreCase(System.getenv("DEV_MODE"))) return true; + return Arrays.asList(args).contains("--dev"); } - /** - * Reads configuration with precedence: Java system property first, then environment variable. - * Returns trimmed value or null if neither source provides a non-empty value. - */ + // Läser konfigurationen med system property först, sen environment variabeln. private static String resolveConfig(String propertyKey, String envKey) { - String v = System.getProperty(propertyKey); + String v = System.getProperty(propertyKey); // Kollar först system property. if (v == null || v.trim().isEmpty()) { - v = System.getenv(envKey); + v = System.getenv(envKey); // Sedan miljövariabeln. } + + // Returnerar värdet eller null. return (v == null || v.trim().isEmpty()) ? null : v.trim(); } } From 819a2e5a762030c80e41cefdea14941bea38542a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alice=20Wers=C3=A9n?= Date: Sun, 14 Dec 2025 23:12:14 +0100 Subject: [PATCH 3/3] =?UTF-8?q?F=C3=A4rdig=20uppgift=20med=20kommentarer?= =?UTF-8?q?=20och=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 35 ++++++ pom.xml | 3 + src/main/java/com/example/Main.java | 174 +++++++++++++++++++++++----- 3 files changed, 186 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 7071577..5719434 100644 --- a/README.md +++ b/README.md @@ -112,3 +112,38 @@ VG (extra credit) - Create a `DataSource` once at startup (using the connection settings above) and inject it into your repositories by constructor injection. For a minimal setup, you can implement a small `SimpleDriverManagerDataSource` that delegates to `DriverManager.getConnection(...)`. This keeps repositories independent of configuration and lets you upgrade to a connection pool (e.g., HikariCP) later without changing repository code. - Define `AccountRepository` and `MoonMissionRepository` and provide JDBC implementations. - In `Main`, resolve configuration, construct the `DataSource`, instantiate repositories. + + +# Databas-JDBC Laboration (3) + +Detta projekt är en Java-applikation som använder JDBC för att interagera med en MySQL-databas. Denna applikation hanterar konton och rymduppdrag. + +## Funktioner + +- Logga in med användarnamn och lösenord. +- Lista alla rymduppdrag. +- Hämta ett specifikt uppdrag baserat på mission_id. +- Räkna antal uppdrag för ett specifikt år. +- Skapa nytt konto. +- Uppdatera lösenord för ett konto. +- Ta bort ett konto. + +## Användning + +1. Starta programmet via IntelliJ eller kommandoraden. +2. Logga in med ett existerande konto (eller skapa ett nytt via menyn). +3. Följ menyn för att utföra olika operationer på databasen. + +## Olika teknologier + +- Java 25 +- JDBC +- MySQL +- Maven + +## Testning + +I detta projekt används Maven för att köra integrationstester. Kör tester med: + +```bash +./mvnw clean verify diff --git a/pom.xml b/pom.xml index 002ff66..4e39704 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,7 @@ 5.20.0 + com.mysql mysql-connector-j @@ -46,11 +47,13 @@ 1.21.3 test + org.testcontainers mysql 1.21.3 + diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 8a0c776..597a044 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -11,75 +11,197 @@ public class Main { public static void main(String[] args) { + // Om vi kör i DevMode: Starta testdatabasen, via Docker/Testcontainers. if (isDevMode(args)) { DevDatabaseInitializer.start(); } + // Startar programlogiken. new Main().run(); } public void run() { - // Hämtar databasinställningar från system properties/miljövariabler. + // Hämtar databasinställningar. String jdbcUrl = resolveConfig("APP_JDBC_URL", "APP_JDBC_URL"); String dbUser = resolveConfig("APP_DB_USER", "APP_DB_USER"); String dbPass = resolveConfig("APP_DB_PASS", "APP_DB_PASS"); - // Kontrollerar att alla inställningar finns, annars kasta ett tydligt fel. + // Om någon databasinställning skulle saknas, så avbryts programmet. if (jdbcUrl == null || dbUser == null || dbPass == null) { throw new IllegalStateException( - "Missing DB configuration. Provide APP_JDBC_URL, APP_DB_USER, APP_DB_PASS " + - "as system properties (-Dkey=value) or environment variables." + "Missing DB configuration. Provide APP_JDBC_URL, APP_DB_USER, APP_DB_PASS as system properties (-Dkey=value) or environment variables." ); } - // Skapar en anslutning till databasen. - try (Connection connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass)) { - // Skapar en Scanner för att läsa input från användaren. + // Skapar anslutning till databasen. + try (Connection connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass)) { + // För att läsa input av användaren i terminalen, så används Scanner. Scanner scanner = new Scanner(System.in); - System.out.print("Username: "); // Ber användaren att skriva in användernamnet. + // Loggar in med användarnamn och lösenord. + System.out.print("Username: "); String username = scanner.nextLine(); - - System.out.print("Password: "); // Ber användaren att skriva in lösenordet. + System.out.print("Password: "); String password = scanner.nextLine(); - // En SQL-fråga för att kontrollera användarnamn och lösenord i databasen. + // En SQL-fråga för att kontrollera användarnamn och lösenord. String loginSql = "SELECT * FROM account WHERE name = ? AND password = ?"; try (PreparedStatement stmt = connection.prepareStatement(loginSql)) { stmt.setString(1, username); stmt.setString(2, password); - - try (ResultSet rs = stmt.executeQuery()) { // Om inget konto hittas, så misslyckas inlogget. + try (ResultSet rs = stmt.executeQuery()) { + // Om ingen rad hittas, så misslyckas inloggningen. if (!rs.next()) { - System.out.println("Invalid login."); + System.out.println("Invalid username or password"); return; // Avslutar run() om inloggningen misslyckas. } } } + // Fortsätter programmet efter lyckad inloggning. + System.out.println("Login successful!"); + + // En meny-loop. + boolean running = true; + while (running) { + System.out.println("\nMenu:"); + System.out.println("1) List moon missions"); + System.out.println("2) Get moon mission by ID"); + System.out.println("3) Count missions for a year"); + System.out.println("4) Create account"); + System.out.println("5) Update account password"); + System.out.println("6) Delete account"); + System.out.println("0) Exit"); + System.out.print("Choose an option: "); - System.out.println("Login successful!"); //Om inlogg lyckas. - // TODO: Här kan du börja skriva meny-loop + // Läser in användarens menyval. + switch (scanner.nextLine()) { + case "1" -> listMissions(connection); + case "2" -> getMissionById(connection, scanner); + case "3" -> countMissionsByYear(connection, scanner); + case "4" -> createAccount(connection, scanner); + case "5" -> updatePassword(connection, scanner); + case "6" -> deleteAccount(connection, scanner); + case "0" -> { + running = false; + System.out.println("Exiting program..."); + } + default -> System.out.println("Invalid option."); + } + } - } catch (SQLException e) { // Om något går fel med databasen, så kastas ett undantag. + } catch (SQLException e) { + // Om något skulle gå fel med databasen. throw new RuntimeException(e); } } - // Kollar om programmet körs i DevMode (ett utvecklingsläge). + + // Listar alla rymduppdrag. + private void listMissions(Connection conn) throws SQLException { + try (PreparedStatement stmt = conn.prepareStatement("SELECT spacecraft FROM moon_mission"); + ResultSet rs = stmt.executeQuery()) { + System.out.println("Moon missions:"); + while (rs.next()) { + System.out.println(rs.getString("spacecraft")); + } + } + } + + // Hämtar ett specifikt uppdrag baserat på mission_id. + private void getMissionById(Connection conn, Scanner scanner) throws SQLException { + System.out.print("Enter mission_id: "); + String id = scanner.nextLine(); + try (PreparedStatement stmt = conn.prepareStatement( + "SELECT spacecraft, launch_date FROM moon_mission WHERE mission_id = ?")) { + stmt.setString(1, id); + try (ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + + // Hämtar årtal från launch_date. + int year = rs.getDate("launch_date").toLocalDate().getYear(); + System.out.println("Mission: " + rs.getString("spacecraft") + ", Year: " + year); + } else { + System.out.println("Mission not found."); + } + } + } + } + + // Räknar hur många uppdrag som skedde ett specifikt år. + private void countMissionsByYear(Connection conn, Scanner scanner) throws SQLException { + System.out.print("Enter year: "); + String year = scanner.nextLine(); + try (PreparedStatement stmt = conn.prepareStatement( + "SELECT COUNT(*) AS count FROM moon_mission WHERE YEAR(launch_date) = ?")) { + stmt.setString(1, year); + try (ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + System.out.println("Year: " + year + ", Number of missions: " + rs.getInt("count")); + } + } + } + } + + // Skapar ett nytt konto. + private void createAccount(Connection conn, Scanner scanner) throws SQLException { + System.out.print("First name: "); + String firstName = scanner.nextLine(); + System.out.print("Last name: "); + String lastName = scanner.nextLine(); + System.out.print("SSN: "); + String ssn = scanner.nextLine(); + System.out.print("Password: "); + String password = scanner.nextLine(); + + try (PreparedStatement stmt = conn.prepareStatement( + "INSERT INTO account (first_name, last_name, ssn, password) VALUES (?, ?, ?, ?)")) { + stmt.setString(1, firstName); + stmt.setString(2, lastName); + stmt.setString(3, ssn); + stmt.setString(4, password); + stmt.executeUpdate(); + System.out.println("Account created!"); + } + } + + // Uppdaterar ett lösenord för ett konto. + private void updatePassword(Connection conn, Scanner scanner) throws SQLException { + System.out.print("User ID: "); + String userId = scanner.nextLine(); + System.out.print("New password: "); + String newPassword = scanner.nextLine(); + + try (PreparedStatement stmt = conn.prepareStatement("UPDATE account SET password = ? WHERE user_id = ?")) { + stmt.setString(1, newPassword); + stmt.setString(2, userId); + int rows = stmt.executeUpdate(); + System.out.println(rows > 0 ? "Password updated!" : "User not found."); + } + } + + // Tar bort ett konto. + private void deleteAccount(Connection conn, Scanner scanner) throws SQLException { + System.out.print("User ID: "); + String userId = scanner.nextLine(); + + try (PreparedStatement stmt = conn.prepareStatement("DELETE FROM account WHERE user_id = ?")) { + stmt.setString(1, userId); + int rows = stmt.executeUpdate(); + System.out.println(rows > 0 ? "Account deleted!" : "User not found."); + } + } + + // Avgör om programmet körs i DevMode. private static boolean isDevMode(String[] args) { - // Kollar om VM option -DdevMode=true är tillsatt. if (Boolean.getBoolean("devMode")) return true; - // Kollar om environment variabeln DEV_MODE=true är tillsatt. if ("true".equalsIgnoreCase(System.getenv("DEV_MODE"))) return true; return Arrays.asList(args).contains("--dev"); } - // Läser konfigurationen med system property först, sen environment variabeln. + // Läser värden från system properties eller environment variabler. private static String resolveConfig(String propertyKey, String envKey) { - String v = System.getProperty(propertyKey); // Kollar först system property. + String v = System.getProperty(propertyKey); if (v == null || v.trim().isEmpty()) { - v = System.getenv(envKey); // Sedan miljövariabeln. + v = System.getenv(envKey); } - - // Returnerar värdet eller null. return (v == null || v.trim().isEmpty()) ? null : v.trim(); } -} +} \ No newline at end of file