Conversation
Validering av användare - klar
WalkthroughAdds connection pooling, repository interfaces and implementations, refactors Main to a menu-driven, authenticated UI using the repositories, and updates README and pom with a test/docker note and three new dependencies. Changes
Sequence DiagramsequenceDiagram
participant User
participant Main
participant AccountRepo
participant MoonMissionRepo
participant DataSource
participant Database
User->>Main: Start app
Main->>User: Prompt credentials
User->>Main: Submit username/password
Main->>AccountRepo: validateLogIn(username, password)
AccountRepo->>DataSource: getConnection()
DataSource->>Database: provide pooled connection
AccountRepo->>Database: SELECT ... FROM account WHERE ...
Database-->>AccountRepo: result (row/no row)
AccountRepo-->>Main: boolean (authenticated?)
alt authenticated
Main->>User: Show menu
User->>Main: Choose operation
opt List missions
Main->>MoonMissionRepo: displayColumn("*") / displayAll
MoonMissionRepo->>DataSource: getConnection()
DataSource->>Database: provide connection
MoonMissionRepo->>Database: SELECT * FROM moon_mission
Database-->>MoonMissionRepo: result set
MoonMissionRepo-->>Main: formatted mission list
Main-->>User: display missions
end
opt Create account
Main->>User: collect name, ssn, password
Main->>AccountRepo: createAccount(...)
AccountRepo->>DataSource: getConnection()
AccountRepo->>Database: INSERT INTO account (...)
Database-->>AccountRepo: update count
AccountRepo-->>Main: success/failure
Main-->>User: show result
end
else not authenticated
Main->>User: Show error, retry
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (2)
src/main/java/repositories/MoonMissionInter.java (1)
1-23: Interface contract vs implementation: document*semantics precisely (and match behavior)
JavaDoc says*means “all columns”; make sure the repo implementation actually supports that (currently it doesn’t).src/main/java/repositories/AccountInter.java (1)
1-42: Consider returning results/status instead of printing inside repo methods
Even for a CLI app, returningboolean/Optional/row counts (and lettingMainprint) usually makes the code easier to test and evolve.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
README.md(1 hunks)pom.xml(1 hunks)src/main/java/com/example/DataSource.java(1 hunks)src/main/java/com/example/Main.java(2 hunks)src/main/java/repositories/AccountInter.java(1 hunks)src/main/java/repositories/AccountRepo.java(1 hunks)src/main/java/repositories/MoonMissionInter.java(1 hunks)src/main/java/repositories/MoonMissionRepo.java(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/main/java/repositories/MoonMissionRepo.java (1)
src/main/java/com/example/DataSource.java (1)
DataSource(7-50)
src/main/java/com/example/Main.java (3)
src/main/java/repositories/AccountRepo.java (1)
AccountRepo(10-96)src/main/java/com/example/DevDatabaseInitializer.java (1)
DevDatabaseInitializer(6-24)src/main/java/com/example/DataSource.java (1)
DataSource(7-50)
src/main/java/repositories/AccountRepo.java (1)
src/main/java/com/example/DataSource.java (1)
DataSource(7-50)
🔇 Additional comments (3)
README.md (1)
1-83: Docs update is clear; Docker prerequisite is correctly called out
Nice addition, especially the IntelliJ + precedence notes.pom.xml (1)
54-68:andsrc/main/java/com/example/DataSource.java (1)
7-20: The filesrc/main/java/com/example/DataSource.javacould not be located in the repository, so the specific code issues mentioned in this review cannot be verified. Without access to the actual code, I cannot confirm:
- Whether
resolveConfig()returnsnullvalues- Whether Hibernate is used in this project
- The actual content at lines 22-49
- Whether the suggested fixes are applicable to the actual codebase
Please verify that the file path is correct and the file exists in the repository before applying the suggested changes.
| static void main(String[] args) throws SQLException { | ||
| if (isDevMode(args)) { | ||
| DevDatabaseInitializer.start(); | ||
| } | ||
| new Main().run(); | ||
|
|
||
| } |
There was a problem hiding this comment.
Critical: main must be public static void main(String[] args)
Package-private main won’t be a valid entrypoint when running the jar/class normally.
- static void main(String[] args) throws SQLException {
+ public static void main(String[] args) throws SQLException {
if (isDevMode(args)) {
DevDatabaseInitializer.start();
}
new Main().run();
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| static void main(String[] args) throws SQLException { | |
| if (isDevMode(args)) { | |
| DevDatabaseInitializer.start(); | |
| } | |
| new Main().run(); | |
| } | |
| public static void main(String[] args) throws SQLException { | |
| if (isDevMode(args)) { | |
| DevDatabaseInitializer.start(); | |
| } | |
| new Main().run(); | |
| } |
🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 14 to 20, the main method is
declared package-private as "static void main(String[] args) throws
SQLException" which prevents the JVM from finding it as an entry point; change
the signature to the standard public static void main(String[] args) (you can
keep or handle the throws SQLException inside the method or catch it), ensure
visibility is public and retain existing logic (call
DevDatabaseInitializer.start() when isDevMode(args) and then new Main().run())
so the class is runnable as a jar.
| private void deleteAccount(AccountRepo accountRepo) { | ||
| System.out.print("Enter userId for the account that you would like to delete: "); | ||
| int userId = Integer.parseInt(scanner.nextLine().trim()); | ||
|
|
||
| accountRepo.deleteAccount(userId); | ||
| } |
There was a problem hiding this comment.
Unvalidated Integer.parseInt(...) can crash the CLI
deleteAccount, updatePassword, and getMissionFromID will throw NumberFormatException on bad input and terminate the app; consider a small “readInt” helper with retry/abort behavior.
Also applies to: 99-110, 123-129
🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 84-89 (and similarly for
99-110 and 123-129), user input is parsed directly with Integer.parseInt which
throws NumberFormatException on bad input and will crash the CLI; implement a
small reusable readInt helper that loops prompting the user, trims input,
catches NumberFormatException, shows a friendly error and reprompts, and accepts
a special abort token (e.g., "q" or "cancel") to return an OptionalInt or throw
a controlled exception so callers can abort gracefully; replace direct
Integer.parseInt calls in deleteAccount, updatePassword, and getMissionFromID
with this helper and handle the abort/empty result to avoid terminating the app.
| private boolean validPassword(String password){ | ||
| if(password.isBlank() || password.length() < 6){ | ||
| System.out.println("Error. Password must be at least 8 characters long."); | ||
| return false; | ||
| } | ||
| return true; | ||
| } |
There was a problem hiding this comment.
Password validation: condition and message disagree
You check < 6 but print “at least 8 characters”. Pick one (tests often care about exact behavior).
private boolean validPassword(String password){
- if(password.isBlank() || password.length() < 6){
+ if(password.isBlank() || password.length() < 8){
System.out.println("Error. Password must be at least 8 characters long.");
return false;
}
return true;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private boolean validPassword(String password){ | |
| if(password.isBlank() || password.length() < 6){ | |
| System.out.println("Error. Password must be at least 8 characters long."); | |
| return false; | |
| } | |
| return true; | |
| } | |
| private boolean validPassword(String password){ | |
| if(password.isBlank() || password.length() < 8){ | |
| System.out.println("Error. Password must be at least 8 characters long."); | |
| return false; | |
| } | |
| return true; | |
| } |
🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 91-97 the password check and
error message disagree: the code checks for password.length() < 6 but prints "at
least 8 characters long"; update the validation to enforce the intended minimum
length (choose 8) by changing the length check to password.length() < 8 and keep
the error message as "Error. Password must be at least 8 characters long." so
behavior and message match.
| public class MoonMissionRepo implements MoonMissionInter{ | ||
| @Override | ||
| public void displayColumn(String columnName) { | ||
| String query = "select * from moon_mission"; | ||
|
|
||
| try (Connection con = DataSource.getConnection(); | ||
| PreparedStatement pS = con.prepareStatement(query)) { | ||
|
|
||
| ResultSet result = pS.executeQuery(); | ||
| while (result.next()) { | ||
| String name = result.getString(columnName); | ||
| System.out.println(name); | ||
| } | ||
|
|
||
| } catch (SQLException e) { | ||
| throw new RuntimeException(e); | ||
| } | ||
| } |
There was a problem hiding this comment.
displayColumn("*") isn’t implemented; invalid column names will crash the command
If you want to support “all columns”, you need to iterate ResultSetMetaData rather than call getString("*"). Also consider validating columnName against a small allowlist (e.g., spacecraft) if it’s user-provided.
public void displayColumn(String columnName) {
String query = "select * from moon_mission";
try (Connection con = DataSource.getConnection();
PreparedStatement pS = con.prepareStatement(query)) {
- ResultSet result = pS.executeQuery();
- while (result.next()) {
- String name = result.getString(columnName);
- System.out.println(name);
- }
+ try (ResultSet result = pS.executeQuery()) {
+ while (result.next()) {
+ if ("*".equals(columnName)) {
+ ResultSetMetaData meta = result.getMetaData();
+ int columns = meta.getColumnCount();
+ for (int i = 1; i <= columns; i++) {
+ System.out.print(meta.getColumnName(i) + "=" + result.getString(i));
+ System.out.print(i == columns ? "\n" : ", ");
+ }
+ } else {
+ System.out.println(result.getString(columnName));
+ }
+ }
+ }
} catch (SQLException e) {
throw new RuntimeException(e);
}
}🤖 Prompt for AI Agents
In src/main/java/repositories/MoonMissionRepo.java around lines 9 to 26, the
method currently calls result.getString(columnName) which will fail for
columnName == "*" and for invalid user-supplied names; change the logic to first
validate columnName against a small allowlist (e.g., "spacecraft",
"mission_date", etc.) and if columnName equals "*" iterate ResultSetMetaData to
read and print every column by name for each row, otherwise call
getString(validatedColumnName); ensure invalid names are handled gracefully
(throw IllegalArgumentException or log and return) rather than letting a
SQLException occur.
| @Override | ||
| public void allMissionsConductedInYear(String year) { | ||
| String query = "select count(*) from moon_mission where launch_date like ?"; | ||
|
|
||
| try (Connection con = DataSource.getConnection(); | ||
| PreparedStatement pS = con.prepareStatement(query)) { | ||
|
|
||
| int numMissions = 0; | ||
| pS.setString(1, year.trim() + "%"); | ||
| ResultSet result = pS.executeQuery(); | ||
| if (result.next()){ | ||
| numMissions = result.getInt(1); | ||
| } | ||
|
|
||
| System.out.println("There were " + numMissions + " moon missions registered during " + year + ".\n"); | ||
|
|
||
| } catch (SQLException e) { | ||
| e.printStackTrace(); | ||
| throw new RuntimeException(); | ||
| } |
There was a problem hiding this comment.
Year filtering via LIKE is brittle; keep the exception cause
launch_date like 'YYYY%' depends on how launch_date is stored/converted. If it’s a DATE/TIMESTAMP, prefer YEAR(launch_date) = ? (MySQL) or a range filter. Also throw new RuntimeException() drops the original cause.
public void allMissionsConductedInYear(String year) {
- String query = "select count(*) from moon_mission where launch_date like ?";
+ String query = "select count(*) from moon_mission where YEAR(launch_date) = ?";
try (Connection con = DataSource.getConnection();
PreparedStatement pS = con.prepareStatement(query)) {
int numMissions = 0;
- pS.setString(1, year.trim() + "%");
- ResultSet result = pS.executeQuery();
- if (result.next()){
- numMissions = result.getInt(1);
- }
+ pS.setInt(1, Integer.parseInt(year.trim()));
+ try (ResultSet result = pS.executeQuery()) {
+ if (result.next()) numMissions = result.getInt(1);
+ }
System.out.println("There were " + numMissions + " moon missions registered during " + year + ".\n");
} catch (SQLException e) {
e.printStackTrace();
- throw new RuntimeException();
+ throw new RuntimeException(e);
}
}🤖 Prompt for AI Agents
In src/main/java/repositories/MoonMissionRepo.java around lines 57 to 76, the
query uses "launch_date LIKE 'YYYY%'" which is brittle for DATE/TIMESTAMP
columns and the catch block throws a RuntimeException without preserving the
original SQLException; change the SQL to use a year-based filter (e.g.,
YEAR(launch_date) = ? for MySQL or a BETWEEN range for DBs without YEAR()) and
bind an integer year parameter, or construct start/end dates and use
"launch_date BETWEEN ? AND ?"; in the catch block rethrow while preserving the
cause (for example throw new RuntimeException(e) or rethrow the SQLException
wrapped) so the original error details are not lost.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
src/main/java/com/example/Main.java (2)
14-14: Critical:mainmust bepublicto be a valid entry point.The main method is declared package-private (
static void main), which prevents the JVM from recognizing it as an application entry point when running the jar.Apply this diff:
- static void main(String[] args) throws SQLException { + public static void main(String[] args) throws SQLException {
86-86: UnvalidatedInteger.parseIntwill crash on invalid input.At lines 86, 101, and 125, user input is parsed directly with
Integer.parseInt, which throwsNumberFormatExceptionon bad input and terminates the app. This degrades the user experience in a menu-driven CLI.Consider implementing a reusable
readInt()helper that catches the exception, prompts the user again, and allows them to abort gracefully (e.g., by entering "q" or "cancel").Also applies to: 101-101, 125-125
🧹 Nitpick comments (4)
src/main/java/repositories/AccountRepo.java (1)
37-48: Username collision handling missing.The generated username (first 3 chars of first name + first 3 chars of last name) can collide when multiple users share the same name pattern. The insert will fail with a constraint violation if
namehas a unique constraint, but the current code prints a generic error message.Consider adding a suffix (e.g., sequential number or random digits) to handle collisions, or catch the specific constraint violation and provide a clearer message.
} catch (SQLException e) { + if (e.getMessage() != null && e.getMessage().contains("Duplicate entry")) { + System.out.println("Error. Username already exists. Please try a different name combination."); + return; + } throw new RuntimeException(e); }src/main/java/com/example/Main.java (3)
12-12: Scanner resource leak: field-level Scanner never closed.The
Scannerinstance created at line 12 wrapsSystem.inand is never closed. While this won't cause issues in a short-running CLI app, it's a resource leak and can trigger static analysis warnings.Consider adding a shutdown hook or closing the scanner in a finally block when the program exits. For a simple CLI, you might suppress the warning with a comment explaining
System.inis managed by the JVM.private final Scanner scanner = new Scanner(System.in); // Not closed: System.in lifecycle managed by JVM
166-166: Redundant null check on Scanner input.At line 166,
choice != nullis unnecessary becausescanner.nextLine()never returnsnull(it returns an empty string if the input is empty).Simplify to:
- if (choice != null && choice.trim().equals("0")) + if (choice.trim().equals("0"))Or even simpler, since
choiceis already trimmed at line 164:- if (choice != null && choice.trim().equals("0")) + if (choice.equals("0"))
153-169: Sign-in allows unlimited retry attempts.The
validateSignInmethod loops indefinitely until valid credentials are entered or the user explicitly exits. While functional for a lab, this could be improved with a maximum retry limit to prevent brute-force attempts.For production systems, consider adding a retry limit (e.g., 3 attempts) and potentially a lockout mechanism or delay after failed attempts.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/main/java/com/example/Main.java(2 hunks)src/main/java/repositories/AccountRepo.java(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/main/java/repositories/AccountRepo.java (1)
src/main/java/com/example/DataSource.java (1)
DataSource(7-50)
src/main/java/com/example/Main.java (2)
src/main/java/com/example/DevDatabaseInitializer.java (1)
DevDatabaseInitializer(6-24)src/main/java/com/example/DataSource.java (1)
DataSource(7-50)
🔇 Additional comments (2)
src/main/java/com/example/Main.java (2)
92-93: Password validation now uses 6 characters minimum.The validation at line 92 checks
password.length() < 6and the message at line 93 says "at least 6 characters long", so they are now consistent. However, note that this differs from the earlier past review which suggested 8 characters as a best practice for security.If this is production code (not just a lab), consider increasing the minimum to 8 characters for better security.
115-115: LGTM! Year regex now correctly validates 1900s and 2000s.The regex
^(19|20)\\d{2}$properly matches four-digit years in the ranges 1900–1999 and 2000–2099, fixing the previous issue.
| public boolean validateLogIn(String username, String password) { | ||
| String userQuery = "select * from account where name = ? and password = ?"; | ||
|
|
||
| try (Connection con = DataSource.getConnection(); | ||
| PreparedStatement pS = con.prepareStatement(userQuery)) { | ||
|
|
||
| pS.setString(1, username); | ||
| pS.setString(2, password); | ||
| ResultSet result = pS.executeQuery(); | ||
| return result.next(); | ||
|
|
||
| } catch (SQLException e) { | ||
| throw new RuntimeException(e); | ||
| } | ||
| } |
There was a problem hiding this comment.
ResultSet resource leak in validateLogIn.
The ResultSet returned by pS.executeQuery() at line 21 is not explicitly closed. While the Connection and PreparedStatement are managed by try-with-resources, the ResultSet is not, creating a potential resource leak.
Apply this diff to ensure proper resource cleanup:
public boolean validateLogIn(String username, String password) {
String userQuery = "select * from account where name = ? and password = ?";
try (Connection con = DataSource.getConnection();
- PreparedStatement pS = con.prepareStatement(userQuery)) {
+ PreparedStatement pS = con.prepareStatement(userQuery);
+ ResultSet result = pS.executeQuery()) {
pS.setString(1, username);
pS.setString(2, password);
- ResultSet result = pS.executeQuery();
return result.next();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}Wait, that's incorrect because we set parameters after creating the ResultSet. Let me fix:
public boolean validateLogIn(String username, String password) {
String userQuery = "select * from account where name = ? and password = ?";
try (Connection con = DataSource.getConnection();
PreparedStatement pS = con.prepareStatement(userQuery)) {
pS.setString(1, username);
pS.setString(2, password);
- ResultSet result = pS.executeQuery();
- return result.next();
+ try (ResultSet result = pS.executeQuery()) {
+ return result.next();
+ }
} catch (SQLException e) {
throw new RuntimeException(e);
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public boolean validateLogIn(String username, String password) { | |
| String userQuery = "select * from account where name = ? and password = ?"; | |
| try (Connection con = DataSource.getConnection(); | |
| PreparedStatement pS = con.prepareStatement(userQuery)) { | |
| pS.setString(1, username); | |
| pS.setString(2, password); | |
| ResultSet result = pS.executeQuery(); | |
| return result.next(); | |
| } catch (SQLException e) { | |
| throw new RuntimeException(e); | |
| } | |
| } | |
| public boolean validateLogIn(String username, String password) { | |
| String userQuery = "select * from account where name = ? and password = ?"; | |
| try (Connection con = DataSource.getConnection(); | |
| PreparedStatement pS = con.prepareStatement(userQuery)) { | |
| pS.setString(1, username); | |
| pS.setString(2, password); | |
| try (ResultSet result = pS.executeQuery()) { | |
| return result.next(); | |
| } | |
| } catch (SQLException e) { | |
| throw new RuntimeException(e); | |
| } | |
| } |
🤖 Prompt for AI Agents
In src/main/java/repositories/AccountRepo.java around lines 13 to 27, the
ResultSet from pS.executeQuery() is not closed and executeQuery must be called
only after parameters are set; fix by setting the PreparedStatement parameters
first, then call executeQuery and wrap the returned ResultSet in its own
try-with-resources (try (ResultSet rs = pS.executeQuery()) { return rs.next();
}) so the ResultSet is always closed while keeping the Connection and
PreparedStatement in their existing try-with-resources.
| try (Connection con = DataSource.getConnection(); | ||
| PreparedStatement pS = con.prepareStatement(addUser)) { | ||
|
|
||
| String username = firstName.substring(0, 3) + lastName.substring(0, 3); |
There was a problem hiding this comment.
Potential StringIndexOutOfBoundsException if validation is bypassed.
The username generation at line 37 assumes firstName and lastName each have at least 3 characters. While Main.createAccount validates this (lines 56-66), calling AccountRepo.createAccount directly from elsewhere could cause a runtime exception.
Apply this diff to add defensive validation:
public void createAccount(String firstName, String lastName, String password, String ssn) {
String addUser = "insert into account (name, password, first_name, last_name, ssn) " +
"values (?, ?, ?, ?, ?)";
try (Connection con = DataSource.getConnection();
PreparedStatement pS = con.prepareStatement(addUser)) {
+ if (firstName.length() < 3 || lastName.length() < 3) {
+ throw new IllegalArgumentException("First name and last name must each be at least 3 characters");
+ }
String username = firstName.substring(0, 3) + lastName.substring(0, 3);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| String username = firstName.substring(0, 3) + lastName.substring(0, 3); | |
| public void createAccount(String firstName, String lastName, String password, String ssn) { | |
| String addUser = "insert into account (name, password, first_name, last_name, ssn) " + | |
| "values (?, ?, ?, ?, ?)"; | |
| try (Connection con = DataSource.getConnection(); | |
| PreparedStatement pS = con.prepareStatement(addUser)) { | |
| if (firstName.length() < 3 || lastName.length() < 3) { | |
| throw new IllegalArgumentException("First name and last name must each be at least 3 characters"); | |
| } | |
| String username = firstName.substring(0, 3) + lastName.substring(0, 3); |
🤖 Prompt for AI Agents
In src/main/java/repositories/AccountRepo.java around line 37, the username is
built by taking substring(0,3) of firstName and lastName which can throw
StringIndexOutOfBoundsException if either is null or shorter than 3 chars; add
defensive validation at the start of createAccount to check that firstName and
lastName are non-null and length >= 3 and throw an IllegalArgumentException with
a clear message if they are not, and then keep the existing substring logic (or
alternatively build the username using substring(0, Math.min(3, s.length())) if
you prefer to accept shorter names) so the method cannot crash when called
directly.
Summary by CodeRabbit
New Features
Documentation
Chores
✏️ Tip: You can customize this high-level summary in your review settings.