diff --git a/README.md b/README.md index d20aaf9..5719434 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. @@ -111,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 6dc6fbd..597a044 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -2,56 +2,201 @@ 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) { + // Om vi kör i DevMode: Starta testdatabasen, via Docker/Testcontainers. if (isDevMode(args)) { DevDatabaseInitializer.start(); } + // Startar programlogiken. new Main().run(); } public void run() { - // Resolve DB settings with precedence: System properties -> Environment variables + // 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"); + // 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 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); + + // Loggar in med användarnamn och lösenord. + System.out.print("Username: "); + String username = scanner.nextLine(); + System.out.print("Password: "); + String password = scanner.nextLine(); + + // 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 ingen rad hittas, så misslyckas inloggningen. + if (!rs.next()) { + 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: "); + + // 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 skulle gå fel med databasen. 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 - */ + // 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) { - 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 + if (Boolean.getBoolean("devMode")) return true; + 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 värden från system properties eller environment variabler. private static String resolveConfig(String propertyKey, String envKey) { String v = System.getProperty(propertyKey); if (v == null || v.trim().isEmpty()) { @@ -59,4 +204,4 @@ private static String resolveConfig(String propertyKey, String envKey) { } return (v == null || v.trim().isEmpty()) ? null : v.trim(); } -} +} \ No newline at end of file