Skip to content

Mats branch 2#7

Closed
bamsemats wants to merge 50 commits intomainfrom
mats_branch_2
Closed

Mats branch 2#7
bamsemats wants to merge 50 commits intomainfrom
mats_branch_2

Conversation

@bamsemats
Copy link

@bamsemats bamsemats commented Dec 19, 2025

Update to implement GUI through JavaFX. Temporary structure API => DTO => GUI; will modify when ready to implement database structure.

Summary by CodeRabbit

  • New Features

    • Launched a JavaFX-based movie database application with a graphical user interface.
    • Integrated with TMDB API to display top-rated movies in a responsive card layout.
    • Added Docker Compose configuration for simplified MySQL database setup.
  • Documentation

    • Updated project README with group formation guidelines, GitHub Classroom notes, and database setup instructions.
  • Chores

    • Added project build dependencies and configuration for Java application development.

✏️ Tip: You can customize this high-level summary in your review settings.

github-classroom bot and others added 30 commits December 15, 2025 10:08
Update pom.xml with dependencies.
…n branch

Feature/entity tatjana merge into main branch.
setup httpClient and gson
movieId and Credits with movieId
TatjanaTrajkovic and others added 20 commits December 17, 2025 21:47
Adds initial TMDB integration:
- TMDB API client with timeout and error handling
- DTOs for top rated movies, movie details and credits
- Gson mapping with @SerializedName
- Package naming aligned with Java conventions (api, dto)
This PR updates the entity model with a combination of bug fixes and refactoring.

- Release year is now stored as a numeric year for improved consistency
- Added a validated TMDB identifier to Movie for reliable external references
- Simplified Person by removing birth date and image URL fields

These changes improve data consistency and simplify the domain model.
This PR refactors the database setup by moving EntityManagerFactory initialization
and transaction management into a dedicated JPAUtil class.

- Centralizes database connection and transaction handling for reuse
- Simplifies application startup logic
- Initializes the database schema on startup and logs a confirmation message

This improves code organization, maintainability, and consistency.
…directly for now) to create cards for each movie shown at start page.
@coderabbitai
Copy link

coderabbitai bot commented Dec 19, 2025

Walkthrough

This PR introduces a complete JavaFX-based movie database application stack with JPA entities (Movie, Person, Role), TMDB API integration via a client and DTO layer, a data source abstraction for the UI, and a JavaFX interface that fetches and displays top-rated movies from TMDB with database and Docker persistence configuration.

Changes

Cohort / File(s) Summary
Configuration & Infrastructure
.gitignore, docker-compose.yml, pom.xml, README.md, src/main/resources/META-INF/persistence.xml
Added .env to .gitignore, MySQL 8.3 Docker Compose service with persistent volume, Maven dependencies (gson, dotenv-java, Jakarta Persistence, SLF4J, JavaFX, JavaFX plugin), updated README with badges and checklist completions, and JPA persistence unit configuration for Hibernate with MySQL dialect.
JPA Entity Layer
src/main/java/org/example/movie/entity/Movie.java, src/main/java/org/example/movie/entity/Person.java, src/main/java/org/example/movie/entity/Role.java, src/main/java/org/example/movie/entity/RoleType.java
New entity classes with JPA annotations: Movie with one-to-many relationship to Role, Person with bidirectional Role management, Role with ManyToOne references to Movie and Person including bidirectional consistency enforcement, and RoleType enum (ACTOR, DIRECTOR).
DTO Layer
src/main/java/org/example/dto/MovieDTO.java, src/main/java/org/example/dto/MovieDetailsDTO.java, src/main/java/org/example/dto/TopRatedResponseDTO.java, src/main/java/org/example/dto/CastDTO.java, src/main/java/org/example/dto/CrewDTO.java, src/main/java/org/example/dto/CreditsDTO.java
Six new record-based DTOs for deserializing TMDB API JSON responses, using Gson \@SerializedName annotations for snake_case field mapping (e.g., release_date, poster_path, vote_average).
API Client
src/main/java/org/example/api/TmdbClient.java
New TMDB API client with three constructor variants, environment-based configuration (apiKey, baseUrl via Dotenv), and three public methods (getTopRatedMovies, getMovieDetails, getMovieCredits) that perform HTTP GET requests with 10-second timeout, status validation, and Gson deserialization with RuntimeException wrapping.
Repository & Service Stubs
src/main/java/org/example/repository/MovieRepository.java, src/main/java/org/example/repository/MovieRepositoryImp.java, src/main/java/org/example/service/MovieServices.java
Skeleton classes for future repository and service layer implementations; currently empty with no methods or fields.
UI Layer
src/main/java/org/example/ui/MainApp.java, src/main/java/org/example/ui/MainController.java, src/main/java/org/example/ui/MovieDataSource.java, src/main/java/org/example/ui/ApiMovieDataSource.java, src/main/resources/MainView.fxml
MainApp bootstraps JavaFX by loading MainView.fxml with custom controller factory injecting MovieDataSource; MainController initializes UI with fetched movies and renders cards in a FlowPane; MovieDataSource interface defines contract; ApiMovieDataSource adapts TmdbClient; MainView.fxml defines BorderPane layout with ScrollPane and FlowPane for responsive movie display.
Utility & Bootstrap
src/main/java/org/example/App.java, src/main/java/org/example/util/JPAUtil.java
App.java converted to JavaFX Application entry point delegating to MainApp; JPAUtil provides singleton EntityManagerFactory with methods for obtaining EntityManagers and executing transactional work with automatic commit/rollback.

Sequence Diagram

sequenceDiagram
    participant User
    participant App as App.java
    participant MainApp
    participant MainController
    participant ApiDataSource
    participant TmdbClient
    participant TMDB as TMDB API
    participant Database
    participant JPAUtil

    User->>App: launch(args)
    App->>JPAUtil: inTransaction (init block)
    JPAUtil->>Database: create EntityManager
    Database-->>JPAUtil: EntityManager ready
    JPAUtil->>Database: commit
    Database-->>JPAUtil: success
    App->>MainApp: start(stage)
    MainApp->>MainApp: load MainView.fxml
    MainApp->>MainApp: create TmdbClient()
    MainApp->>MainApp: create ApiDataSource(tmdbClient)
    MainApp->>MainController: instantiate with ApiDataSource
    MainController->>MainController: `@FXML` initialize()
    MainController->>ApiDataSource: getTopRatedMovies()
    ApiDataSource->>TmdbClient: getTopRatedMovies()
    TmdbClient->>TMDB: GET /movie/top_rated?api_key=...
    TMDB-->>TmdbClient: JSON response (movies)
    TmdbClient->>TmdbClient: deserialize to TopRatedResponseDTO
    TmdbClient-->>ApiDataSource: TopRatedResponseDTO
    ApiDataSource-->>MainController: List<MovieDTO>
    MainController->>MainController: render movie cards in FlowPane
    MainApp->>App: stage.show()
    App-->>User: display UI
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Role.java bidirectional relationship management: Setters for movie and person enforce consistency by adding the role to collections; review for potential duplicate handling edge cases.
  • TmdbClient HTTP handling: Verify 10-second timeout configuration, HTTP status code validation (200 enforcement), and exception wrapping strategy across three API methods.
  • MainApp controller factory and FXML wiring: Complex error handling around resource lookup, controller instantiation, and scene setup; verify exception propagation and cleanup logic.
  • MainController card rendering logic: Review FlowPane layout, poster path resolution with fallback image, and UI initialization order.
  • JPAUtil transaction management: Verify rollback triggers on exceptions, EntityManager closure in finally block, and persistence unit naming correctness.
  • DTO and Entity field mapping: Confirm all Gson \@SerializedName annotations align with TMDB API response structure (release_date, poster_path, vote_average, etc.).

Poem

🐰 A database hops to life, with TMDB in sight,
JavaFX cards display the movies, oh what a delight!
Entities and DTOs dance with API grace,
Spring and MySQL spin in Docker's embrace—
The cinema awaits, one top-rated film at a time! 🎬

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Mats branch 2' is generic and does not convey meaningful information about the changeset, which implements a JavaFX GUI with DTOs, API client, and JPA entities. Revise the title to be more descriptive, such as 'Implement JavaFX GUI with TMDB API integration and JPA entity layer' to clearly communicate the main changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🧹 Nitpick comments (22)
src/main/java/org/example/repository/MovieRepository.java (1)

3-4: Consider defining this as an interface to follow the repository pattern.

The standard repository pattern uses an interface to define the contract and a separate implementation class. This improves testability and allows for multiple implementations.

🔎 Proposed refactor to interface
-public class MovieRepository {
+public interface MovieRepository {
+    // Define repository methods here when ready
 }
src/main/java/org/example/service/MovieServices.java (1)

1-4: Consider removing the stub class or adding a TODO comment.

This empty class provides no functionality. If it's genuinely temporary, consider either:

  • Removing it until implementation is ready (preferred)
  • Adding a TODO comment explaining the planned functionality

Keeping empty placeholder classes can create confusion about the codebase's actual structure.

src/main/java/org/example/ui/MovieDataSource.java (1)

9-10: Minor: Remove extra blank lines.

Consider removing the trailing blank lines for cleaner formatting.

src/main/resources/MainView.fxml (2)

14-32: VBox wrapper appears unnecessary.

The VBox wraps only the ScrollPane and the commented-out ToolBar. If the toolbar remains disabled, the VBox adds no value. Consider placing the ScrollPane directly in the <center> or uncommenting the toolbar when ready.


25-30: Commented-out code should be tracked separately.

The disabled toolbar hints at planned functionality. Consider removing the commented code and tracking it in an issue or TODO list to keep the FXML clean.

pom.xml (1)

47-61: Inconsistent indentation in dependencies block.

These dependencies use different indentation levels than the newly added ones (lines 19-28, 63-87). Consider normalizing to 4-space indentation for consistency.

src/main/java/org/example/ui/ApiMovieDataSource.java (2)

11-13: Consider adding null validation for the constructor parameter.

If tmdbClient is null, the error will only surface when getTopRatedMovies() is called, making debugging harder.

🔎 Proposed fix
 public ApiMovieDataSource(TmdbClient tmdbClient) {
+    if (tmdbClient == null) {
+        throw new IllegalArgumentException("tmdbClient must not be null");
+    }
     this.tmdbClient = tmdbClient;
 }

15-18: Remove the noise comment.

The inline comment // or searchMovies(), etc. doesn't add value and should be removed.

🔎 Proposed fix
 @Override
 public List<MovieDTO> getTopRatedMovies() {
-    return tmdbClient.getTopRatedMovies().results(); // or searchMovies(), etc.
+    return tmdbClient.getTopRatedMovies().results();
 }
src/main/java/org/example/ui/MainApp.java (2)

14-20: Inconsistent indentation.

Lines 15-20 use 8-space indentation while the rest of the method uses 4-space. Normalize the formatting.

🔎 Proposed fix
     public void start(Stage stage) {
         try {
-                TmdbClient tmdbClient = new TmdbClient();
-                MovieDataSource dataSource = new ApiMovieDataSource(tmdbClient);
-                var fxmlUrl = getClass().getResource("/MainView.fxml");
-                if (fxmlUrl == null) {
-                throw new IllegalStateException("Cannot find FXML resource: /MainView.fxml");
-                }
+            TmdbClient tmdbClient = new TmdbClient();
+            MovieDataSource dataSource = new ApiMovieDataSource(tmdbClient);
+            var fxmlUrl = getClass().getResource("/MainView.fxml");
+            if (fxmlUrl == null) {
+                throw new IllegalStateException("Cannot find FXML resource: /MainView.fxml");
+            }

             FXMLLoader loader = new FXMLLoader(fxmlUrl);

15-16: TmdbClient initialization may fail silently before UI loads.

TmdbClient() reads from .env and throws IllegalArgumentException if TMDB_API_KEY or TMDB_BASE_URL is missing. This exception gets caught by the outer catch block and shows a generic error. Consider providing a more specific error message for missing configuration.

🔎 Proposed fix
     public void start(Stage stage) {
         try {
-            TmdbClient tmdbClient = new TmdbClient();
+            TmdbClient tmdbClient;
+            try {
+                tmdbClient = new TmdbClient();
+            } catch (IllegalArgumentException e) {
+                throw new IllegalStateException(
+                    "TMDB configuration missing. Please ensure TMDB_API_KEY and TMDB_BASE_URL are set in .env file.", e);
+            }
             MovieDataSource dataSource = new ApiMovieDataSource(tmdbClient);
src/main/resources/META-INF/persistence.xml (1)

35-42: Review production readiness of these settings.

Several settings here are suitable for development but problematic for production:

  • hbm2ddl.auto=update can cause unintended schema changes
  • show_sql, format_sql, highlight_sql enabled can leak query details and impact performance
  • Hibernate's built-in connection pool (connection.pool_size) is not production-grade

Consider using profiles or external configuration to differentiate dev/prod settings.

src/main/java/org/example/util/JPAUtil.java (1)

13-21: Consider handling static initialization failure gracefully.

If Persistence.createEntityManagerFactory("myPU") fails (e.g., database unavailable), it throws an ExceptionInInitializerError which can be difficult to diagnose. Consider wrapping initialization to provide clearer error messaging or implementing lazy initialization.

src/main/java/org/example/dto/MovieDetailsDTO.java (1)

5-15: DTO structure looks good.

The record correctly maps TMDB API response fields with appropriate @SerializedName annotations for JSON deserialization.

Regarding the comment on line 14 about genres: TMDB returns genres as an array of objects ([{id, name}]), so you'd need a separate GenreDTO record and add List<GenreDTO> genres here if needed.

Would you like me to help create a GenreDTO and integrate it?

src/main/java/org/example/ui/MainController.java (3)

3-18: Remove unused imports.

Several imports are not used in this file: FXMLLoader, ListView, MovieServices, and Movie. Clean these up to avoid confusion.

🔎 Proposed fix
 package org.example.ui;
 
 import javafx.fxml.FXML;
-import javafx.fxml.FXMLLoader;
 import javafx.scene.control.Label;
-import javafx.scene.control.ListView;
 import javafx.scene.control.TextField;
 import javafx.scene.image.Image;
 import javafx.scene.image.ImageView;
 import javafx.scene.layout.FlowPane;
 import javafx.scene.layout.Priority;
 import javafx.scene.layout.VBox;
 import org.example.dto.MovieDTO;
-import org.example.service.MovieServices;
-import org.example.movie.entity.Movie;
 
 import java.util.List;
 import java.util.Objects;

22-29: Remove commented-out legacy code.

Dead/commented code clutters the file and creates confusion. If this code might be needed later, consider using version control history instead.

Also applies to: 115-124


46-54: Avoid e.printStackTrace() in production code.

Use a proper logging framework (e.g., SLF4J) instead of printStackTrace(). Also, consider whether re-throwing a RuntimeException is the desired behavior, as it may cause the application to crash.

src/main/java/org/example/movie/entity/Person.java (1)

4-4: Remove unused import.

java.time.LocalDate is imported but not used.

🔎 Proposed fix
 import jakarta.persistence.*;
-import java.time.LocalDate;
 import java.util.ArrayList;
src/main/java/org/example/movie/entity/Role.java (1)

48-61: Bidirectional sync is duplicated across entities.

Both Role.setMovie()/setPerson() and Movie.addRole()/Person.addRole() manage the bidirectional relationship. For example, calling movie.addRole(role) sets role.movie = this, which then tries to add the role back to movie.roles. The contains() check prevents duplicates but creates confusing semantics.

Consider choosing one owner for the relationship sync:

  1. Keep sync only in the owning side helpers (Movie.addRole, Person.addRole) and simplify setters:
🔎 Simplified setters
     public void setMovie(Movie movie) {
         this.movie = movie;
-        if (movie != null && !movie.getRoles().contains(this)) {
-            movie.getRoles().add(this);
-        }
     }

     public void setPerson(Person person) {
         this.person = person;
-        if (person != null && !person.getRoles().contains(this)) {
-            person.getRoles().add(this);
-        }
     }
src/main/java/org/example/api/TmdbClient.java (3)

5-5: Remove unused import.

The JSONUtils import is not used anywhere in this class.

🔎 Proposed fix
-import nonapi.io.github.classgraph.json.JSONUtils;

53-53: Replace debug print with proper logging.

The System.out.println statement should be removed or replaced with a proper logging framework (e.g., SLF4J) for production code.

🔎 Proposed fix
-        System.out.println("TMDB BASE URL = " + baseUrl);

56-132: Consider extracting common HTTP logic to reduce duplication.

All three methods (getTopRatedMovies, getMovieDetails, getMovieCredits) share nearly identical HTTP request/response handling logic. Consider extracting this into a private helper method to improve maintainability.

🔎 Example refactoring approach
private <T> T fetchFromTmdb(String endpoint, Class<T> responseType) {
    try {
        String url = baseUrl + endpoint + (endpoint.contains("?") ? "&" : "?") + "api_key=" + apiKey;
        
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .timeout(Duration.ofSeconds(10))
            .GET()
            .build();
        
        HttpResponse<String> response =
            httpClient.send(request, HttpResponse.BodyHandlers.ofString());
        
        if (response.statusCode() != 200) {
            throw new RuntimeException(
                "TMDB API error: HTTP " + response.statusCode() + " - " + response.body()
            );
        }
        
        return gson.fromJson(response.body(), responseType);
        
    } catch (Exception e) {
        throw new RuntimeException("Could not fetch from TMDB: " + endpoint, e);
    }
}

public TopRatedResponseDTO getTopRatedMovies() {
    return fetchFromTmdb("/movie/top_rated", TopRatedResponseDTO.class);
}

public MovieDetailsDTO getMovieDetails(int movieId) {
    return fetchFromTmdb("/movie/" + movieId, MovieDetailsDTO.class);
}

public CreditsDTO getMovieCredits(int movieId) {
    return fetchFromTmdb("/movie/" + movieId + "/credits", CreditsDTO.class);
}
src/main/java/org/example/movie/entity/Movie.java (1)

38-41: Consider validating title in the constructor.

The title field has @Column(nullable = false), but the constructor doesn't validate that the provided title is not null. While JPA will enforce this constraint at persistence time, validating in the constructor provides fail-fast behavior and clearer error messages.

🔎 Proposed fix
 public Movie(String title, Integer tmdbId) {
+    if (title == null || title.trim().isEmpty()) {
+        throw new IllegalArgumentException("title cannot be null or empty");
+    }
+    if (tmdbId == null || tmdbId <= 0) {
+        throw new IllegalArgumentException("tmdbId must be a positive number");
+    }
     this.title = title;
-    this.tmdbId = tmdbId;
+    this.tmdbId = tmdbId;
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8794408 and 5f8f90c.

⛔ Files ignored due to path filters (1)
  • src/main/resources/Images/no-poster.jpg is excluded by !**/*.jpg
📒 Files selected for processing (26)
  • .gitignore (1 hunks)
  • README.md (2 hunks)
  • docker-compose.yml (1 hunks)
  • pom.xml (2 hunks)
  • src/main/java/org/example/App.java (1 hunks)
  • src/main/java/org/example/api/TmdbClient.java (1 hunks)
  • src/main/java/org/example/dto/CastDTO.java (1 hunks)
  • src/main/java/org/example/dto/CreditsDTO.java (1 hunks)
  • src/main/java/org/example/dto/CrewDTO.java (1 hunks)
  • src/main/java/org/example/dto/MovieDTO.java (1 hunks)
  • src/main/java/org/example/dto/MovieDetailsDTO.java (1 hunks)
  • src/main/java/org/example/dto/TopRatedResponseDTO.java (1 hunks)
  • src/main/java/org/example/movie/entity/Movie.java (1 hunks)
  • src/main/java/org/example/movie/entity/Person.java (1 hunks)
  • src/main/java/org/example/movie/entity/Role.java (1 hunks)
  • src/main/java/org/example/movie/entity/RoleType.java (1 hunks)
  • src/main/java/org/example/repository/MovieRepository.java (1 hunks)
  • src/main/java/org/example/repository/MovieRepositoryImp.java (1 hunks)
  • src/main/java/org/example/service/MovieServices.java (1 hunks)
  • src/main/java/org/example/ui/ApiMovieDataSource.java (1 hunks)
  • src/main/java/org/example/ui/MainApp.java (1 hunks)
  • src/main/java/org/example/ui/MainController.java (1 hunks)
  • src/main/java/org/example/ui/MovieDataSource.java (1 hunks)
  • src/main/java/org/example/util/JPAUtil.java (1 hunks)
  • src/main/resources/META-INF/persistence.xml (1 hunks)
  • src/main/resources/MainView.fxml (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (7)
src/main/java/org/example/ui/ApiMovieDataSource.java (1)
src/main/java/org/example/api/TmdbClient.java (1)
  • TmdbClient (16-133)
src/main/java/org/example/ui/MainApp.java (2)
src/main/java/org/example/api/TmdbClient.java (1)
  • TmdbClient (16-133)
src/main/java/org/example/service/MovieServices.java (1)
  • MovieServices (3-4)
src/main/java/org/example/movie/entity/Role.java (2)
src/main/java/org/example/movie/entity/Movie.java (1)
  • Entity (8-88)
src/main/java/org/example/movie/entity/Person.java (1)
  • Entity (9-40)
src/main/java/org/example/movie/entity/Person.java (2)
src/main/java/org/example/movie/entity/Movie.java (1)
  • Entity (8-88)
src/main/java/org/example/movie/entity/Role.java (1)
  • Entity (5-62)
src/main/java/org/example/App.java (2)
src/main/java/org/example/ui/MainApp.java (1)
  • MainApp (9-51)
src/main/java/org/example/util/JPAUtil.java (1)
  • JPAUtil (9-43)
src/main/java/org/example/movie/entity/Movie.java (2)
src/main/java/org/example/movie/entity/Person.java (1)
  • Entity (9-40)
src/main/java/org/example/movie/entity/Role.java (1)
  • Entity (5-62)
src/main/java/org/example/ui/MainController.java (1)
src/main/java/org/example/service/MovieServices.java (1)
  • MovieServices (3-4)
🔇 Additional comments (16)
.gitignore (1)

3-3: LGTM! Essential security practice.

Adding .env to the ignore list prevents API keys and sensitive configuration from being committed to the repository, which aligns with the TmdbClient's use of environment-based configuration.

docker-compose.yml (1)

13-14: Good practice: Port binding restricted to localhost.

Binding the MySQL port to 127.0.0.1 prevents external network access, which is appropriate for a development database.

src/main/java/org/example/movie/entity/RoleType.java (1)

3-6: LGTM! Clean enum definition.

The enum provides clear type-safe constants for role categorization. The two values (ACTOR, DIRECTOR) cover the current requirements and can be extended later if additional role types are needed.

src/main/java/org/example/dto/CreditsDTO.java (1)

5-9: LGTM! Appropriate DTO record structure.

The record definition is clean and follows the pattern established in other DTOs. The field names appear to match the expected JSON structure from TMDB API responses for credits.

src/main/java/org/example/ui/MovieDataSource.java (1)

6-8: LGTM! Clean abstraction for movie data access.

The interface provides a clear contract for fetching top-rated movies, enabling easy swapping between data sources (API, database, mock) for testing and flexibility.

src/main/java/org/example/dto/CrewDTO.java (1)

5-10: LGTM! Proper JSON field mapping.

The record correctly uses @SerializedName to map the snake_case JSON field profile_path to the camelCase Java field profilePath, following Java naming conventions while maintaining compatibility with the TMDB API response format.

src/main/java/org/example/dto/TopRatedResponseDTO.java (1)

7-12: LGTM!

Clean record definition with appropriate Gson annotations for snake_case field mapping.

pom.xml (2)

14-14: > Likely an incorrect or invalid review comment.


63-67: Keep the explicit jakarta.persistence-api dependency.

The Jakarta Persistence API is not automatically provided by Hibernate and should remain as an explicit dependency for compilation and use in this project. All current documentation and examples with Hibernate 7.x show jakarta.persistence-api declared separately.

src/main/java/org/example/util/JPAUtil.java (1)

27-42: Transaction handling looks correct.

The inTransaction method properly handles the transaction lifecycle: begins transaction, executes work, commits on success, rolls back on failure if active, and always closes the EntityManager in the finally block.

src/main/java/org/example/dto/MovieDTO.java (1)

5-12: LGTM!

The record is well-structured for the top-rated movies list response, with correct @SerializedName annotations for TMDB API field mapping.

src/main/java/org/example/movie/entity/Person.java (1)

9-39: Entity structure looks good.

The Person entity is correctly configured with JPA annotations, has a proper protected no-arg constructor for JPA, and the bidirectional relationship with Role is well-managed through the addRole() method.

src/main/java/org/example/movie/entity/Role.java (1)

5-33: Entity mapping is correct.

The Role entity properly defines the join table structure with @ManyToOne relationships to both Movie and Person, uses lazy fetching appropriately, and stores the enum as a string.

src/main/java/org/example/api/TmdbClient.java (1)

29-42: LGTM: Constructor validation is well-implemented.

The constructors properly validate that apiKey and baseUrl are not null, throwing IllegalArgumentException with clear messages when configuration is missing. The constructor chaining pattern is also appropriate.

Also applies to: 44-54

src/main/java/org/example/movie/entity/Movie.java (2)

84-87: LGTM: Bidirectional relationship management is correctly implemented.

The addRole method properly maintains bidirectional consistency by adding the role to the collection and setting the movie reference. The implementation in Role.setMovie (from Role.java) includes a duplicate check that prevents the same role from being added twice, ensuring data integrity.


8-34: LGTM: JPA entity structure follows best practices.

The entity is properly annotated with appropriate constraints:

  • @Column(nullable = false, unique = true) on tmdbId ensures data integrity for the external TMDB identifier
  • @Column(length = 1000) on description provides adequate space for movie descriptions
  • @OneToMany with CascadeType.ALL ensures related roles are managed with the movie lifecycle
  • Protected no-arg constructor satisfies JPA requirements

Comment on lines +9 to +12
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: moviedb
MYSQL_USER: movieuser
MYSQL_PASSWORD: moviepass
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Move database credentials to environment variables.

Hardcoding database credentials directly in docker-compose.yml creates a security risk, as these values are committed to version control and could be exposed.

🔎 Recommended approach using environment variables

Create a .env file (already in .gitignore) with:

MYSQL_ROOT_PASSWORD=rootpass
MYSQL_DATABASE=moviedb
MYSQL_USER=movieuser
MYSQL_PASSWORD=moviepass

Then reference them in docker-compose.yml:

     environment:
-      MYSQL_ROOT_PASSWORD: rootpass
-      MYSQL_DATABASE: moviedb
-      MYSQL_USER: movieuser
-      MYSQL_PASSWORD: moviepass
+      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
+      MYSQL_DATABASE: ${MYSQL_DATABASE}
+      MYSQL_USER: ${MYSQL_USER}
+      MYSQL_PASSWORD: ${MYSQL_PASSWORD}

Provide a .env.example file in the repository with placeholder values for team members.

🤖 Prompt for AI Agents
In docker-compose.yml around lines 9 to 12, the DB credentials are hardcoded;
move those values into environment variables by removing the literal passwords
and referencing env vars (e.g. MYSQL_ROOT_PASSWORD, MYSQL_DATABASE, MYSQL_USER,
MYSQL_PASSWORD) so docker-compose reads them from the environment; create a .env
(and add it to .gitignore if not already) containing the real secrets for local
use and add a .env.example with placeholder values committed to the repo so team
members know required variables; ensure docker-compose.yml continues to
reference the same variable names and validate by running docker-compose config.

Comment on lines +68 to +72
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.13</version>
</dependency>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

SLF4J API without a binding will produce warnings.

Adding slf4j-api without an implementation (e.g., slf4j-simple, logback-classic) results in "No SLF4J providers were found" warnings at runtime. Consider adding an SLF4J binding or removing this dependency if Hibernate's logging suffices.

🤖 Prompt for AI Agents
In pom.xml around lines 68 to 72, the project declares org.slf4j:slf4j-api
without providing a concrete SLF4J binding, which will cause "No SLF4J providers
were found" warnings at runtime; fix this by either adding a binding dependency
(for example ch.qos.logback:logback-classic or org.slf4j:slf4j-simple) with an
appropriate version to the pom, or remove the slf4j-api entry if you intend to
rely solely on your existing logging implementation (e.g., Hibernate's),
ensuring only one SLF4J binding is present to avoid multiple-binding conflicts.

Comment on lines +13 to +17
- [ x ] Klona GitHub-repo
- [ x ] Konfigurera persistence.xml eller använd PersistenceConfiguration i kod
- [ x ] Skapa entiteter och verifiera tabellgenerering
- [ x ] Lägg till relationer (One-to-Many, Many-to-Many)
- [ x ] Arbeta på feature-branches och använd pull requests för kodgranskning
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix markdown checkbox syntax.

The completed checkboxes use [ x ] which won't render properly in markdown. Remove the space between [ and x for correct rendering.

🔎 Proposed fix
-- [ x ] Klona GitHub-repo
-- [ x ] Konfigurera persistence.xml eller använd PersistenceConfiguration i kod
-- [ x ] Skapa entiteter och verifiera tabellgenerering
-- [ x ] Lägg till relationer (One-to-Many, Many-to-Many)
-- [ x ] Arbeta på feature-branches och använd pull requests för kodgranskning
+- [x] Klona GitHub-repo
+- [x] Konfigurera persistence.xml eller använd PersistenceConfiguration i kod
+- [x] Skapa entiteter och verifiera tabellgenerering
+- [x] Lägg till relationer (One-to-Many, Many-to-Many)
+- [x] Arbeta på feature-branches och använd pull requests för kodgranskning
📝 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.

Suggested change
- [ x ] Klona GitHub-repo
- [ x ] Konfigurera persistence.xml eller använd PersistenceConfiguration i kod
- [ x ] Skapa entiteter och verifiera tabellgenerering
- [ x ] Lägg till relationer (One-to-Many, Many-to-Many)
- [ x ] Arbeta på feature-branches och använd pull requests för kodgranskning
- [x] Klona GitHub-repo
- [x] Konfigurera persistence.xml eller använd PersistenceConfiguration i kod
- [x] Skapa entiteter och verifiera tabellgenerering
- [x] Lägg till relationer (One-to-Many, Many-to-Many)
- [x] Arbeta på feature-branches och använd pull requests för kodgranskning
🤖 Prompt for AI Agents
In README.md around lines 13 to 17 the markdown checkboxes are written as "[ x
]" which won't render as checked items; update each checkbox token by removing
the extra spaces so completed items use "[x]" (and empty boxes use "[ ]" if
needed) to ensure proper GitHub-flavored Markdown rendering.

Comment on lines +56 to +80
public TopRatedResponseDTO getTopRatedMovies() {
try {
String url = baseUrl + "/movie/top_rated?api_key=" + apiKey;

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(Duration.ofSeconds(10))
.GET()
.build();

HttpResponse<String> response =
httpClient.send(request, HttpResponse.BodyHandlers.ofString());

if (response.statusCode() != 200) {
throw new RuntimeException(
"TMDB API error: HTTP " + response.statusCode() + " - " + response.body()
);
}

return gson.fromJson(response.body(), TopRatedResponseDTO.class);

} catch (Exception e) {
throw new RuntimeException("Could not get top rated movies from TMDB", e);
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Use TMDB Access Token with Authorization header instead of api_key query parameter.

The API key is currently passed as a query parameter. If logged anywhere—in error messages, debugging output, or monitoring systems—the API key could be exposed.

TMDB API supports a more secure approach: obtain an API Read Access Token from account settings and send it as an Authorization header. This eliminates the risk of accidental exposure through URL logging while providing a read-only authentication method.

🤖 Prompt for AI Agents
In src/main/java/org/example/api/TmdbClient.java around lines 56 to 80, replace
the current use of api_key as a query parameter with the TMDB Read Access Token
sent in an Authorization: Bearer <token> header: stop appending "?api_key=" +
apiKey to the URL (use baseUrl + "/movie/top_rated"), add an Authorization
header using a securely stored accessToken field (e.g., this.accessToken) when
building the HttpRequest, and ensure the access token is not interpolated into
any exception or log messages; also keep the existing response status check and
JSON parsing but avoid including sensitive tokens in thrown messages or logs.

Comment on lines 16 to 22
public static void main(String[] args) {
System.out.println("Hello There!");
launch(args);

JPAUtil.inTransaction(em -> {
System.out.println("Database schema initialized");
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Database initialization runs after application exits.

Application.launch() is a blocking call that only returns when the JavaFX application closes. The JPAUtil.inTransaction() block will execute after the user quits the app, which is almost certainly unintended. Move database initialization into start() or before launch().

🔎 Proposed fix
 public static void main(String[] args) {
+    // Initialize database before starting UI
+    JPAUtil.inTransaction(em -> {
+        System.out.println("Database schema initialized");
+    });
+
     launch(args);
-
-    JPAUtil.inTransaction(em -> {
-        System.out.println("Database schema initialized");
-    });
 }
🤖 Prompt for AI Agents
In src/main/java/org/example/App.java around lines 16–22, the
JPAUtil.inTransaction call is placed after Application.launch(), so DB
initialization runs only after the JavaFX app exits; move the initialization out
of main and into the JavaFX lifecycle: either call JPAUtil.inTransaction before
launch() in main (if it does not require the FX thread) or override start(...)
in your Application subclass and run JPAUtil.inTransaction there (or in init()
if you need pre-FX-thread setup); if initialization is long-running, run it off
the FX thread (e.g., CompletableFuture or Executor) and ensure any UI updates
are executed on the FX Application Thread.

Comment on lines +38 to +41
public Movie(String title, Integer tmdbId) {
this.title = title;
this.tmdbId = tmdbId;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Apply consistent validation in the constructor.

The constructor directly assigns tmdbId without validation, but the setter (lines 75-80) validates that tmdbId is not null and must be positive. This inconsistency allows invalid tmdbId values to be created via the constructor, violating the @Column(nullable = false, unique = true) constraint and the business rule enforced by the setter.

🔎 Proposed fix
 public Movie(String title, Integer tmdbId) {
     this.title = title;
-    this.tmdbId = tmdbId;
+    setTmdbId(tmdbId);
 }

Alternatively, duplicate the validation logic in the constructor:

 public Movie(String title, Integer tmdbId) {
+    if (tmdbId == null || tmdbId <= 0) {
+        throw new IllegalArgumentException("tmdbId must be a positive number");
+    }
     this.title = title;
     this.tmdbId = tmdbId;
 }
📝 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.

Suggested change
public Movie(String title, Integer tmdbId) {
this.title = title;
this.tmdbId = tmdbId;
}
public Movie(String title, Integer tmdbId) {
this.title = title;
setTmdbId(tmdbId);
}
🤖 Prompt for AI Agents
In src/main/java/org/example/movie/entity/Movie.java around lines 38 to 41, the
constructor assigns tmdbId directly which bypasses the validation present in the
setter and can create invalid entities; update the constructor to enforce the
same validation as the setter by either calling setTmdbId(tmdbId) from the
constructor or replicating the checks (ensure tmdbId is not null and > 0) and
throw an IllegalArgumentException if the value is invalid so the
@Column(nullable = false, unique = true) and business rules are preserved.

Comment on lines +3 to +4
public class MovieRepositoryImp {
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Fix naming convention and implement the repository interface.

Two improvements needed:

  1. Naming: The class name should use Impl (not Imp) suffix to follow Java naming conventions for implementation classes.
  2. Interface: This implementation class should implement the MovieRepository interface (assuming MovieRepository becomes an interface as suggested).
🔎 Proposed refactor

First, rename the file from MovieRepositoryImp.java to MovieRepositoryImpl.java, then apply:

-public class MovieRepositoryImp {
+public class MovieRepositoryImpl implements MovieRepository {
+    // Implement repository methods here when ready
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/org/example/repository/MovieRepositoryImp.java around lines
3-4, the class uses the wrong naming suffix and does not implement the
repository interface; rename the file to MovieRepositoryImpl.java, change the
class declaration to public class MovieRepositoryImpl implements
MovieRepository, add any required imports, implement (or stub) all methods
declared in the MovieRepository interface (or delegate to an underlying
DAO/service), and update all call sites to reference MovieRepositoryImpl; ensure
compilation by adding @Override annotations for implemented methods and
adjusting package declarations if the file move requires it.


public class MainApp {

private final MovieServices movieServices = new MovieServices();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Unused field movieServices.

The movieServices field is instantiated but never used. Remove it or implement its intended functionality.

🔎 Proposed fix
 public class MainApp {
-
-    private final MovieServices movieServices = new MovieServices();

     public void start(Stage stage) {
📝 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.

Suggested change
private final MovieServices movieServices = new MovieServices();
public class MainApp {
public void start(Stage stage) {
🤖 Prompt for AI Agents
In src/main/java/org/example/ui/MainApp.java at line 11, the private final field
"movieServices" is instantiated but never used; remove the unused field
declaration or use it where intended. If the service is unnecessary, delete the
line "private final MovieServices movieServices = new MovieServices();" and
remove any imports referencing MovieServices; if it should be used, inject or
initialize it properly (prefer constructor injection or a singleton accessor)
and replace any direct new usages with that instance, then call its methods
where needed in the class to fulfill its intended role.

Comment on lines +101 to +107
Label overview = new Label(movie.overview());
overview.setWrapText(true);
overview.setMaxWidth(150);

VBox.setVgrow(title, Priority.NEVER);

card.getChildren().addAll(poster, title);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Unused overview label created but never added to card.

The overview Label is created and configured (lines 101-103) but never added to card.getChildren(). This appears to be either dead code or an incomplete feature.

🔎 If you want to display the overview, add it to the card
-        card.getChildren().addAll(poster, title);
+        card.getChildren().addAll(poster, title, overview);

Or remove the unused code if the overview shouldn't be displayed.

📝 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.

Suggested change
Label overview = new Label(movie.overview());
overview.setWrapText(true);
overview.setMaxWidth(150);
VBox.setVgrow(title, Priority.NEVER);
card.getChildren().addAll(poster, title);
Label overview = new Label(movie.overview());
overview.setWrapText(true);
overview.setMaxWidth(150);
VBox.setVgrow(title, Priority.NEVER);
card.getChildren().addAll(poster, title, overview);
🤖 Prompt for AI Agents
In src/main/java/org/example/ui/MainController.java around lines 101 to 107, a
Label named "overview" is created and configured but never added to the card
children; either add the overview to the card (e.g., include it in the
card.getChildren().addAll(...) call so the overview is displayed, ensuring
layout order and that wrapping/maxWidth remain appropriate) or remove the unused
overview creation and its related configuration lines if the overview should not
be shown.

Comment on lines +27 to +30
<property name="jakarta.persistence.jdbc.user"
value="movieuser"/>
<property name="jakarta.persistence.jdbc.password"
value="moviepass"/>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Hardcoded credentials are a security risk.

Database credentials should not be stored in version-controlled configuration files. Consider using environment variables or external configuration (e.g., via persistence-unit property overrides at runtime, or a dotenv approach mentioned in the PR description).

🤖 Prompt for AI Agents
In src/main/resources/META-INF/persistence.xml around lines 27 to 30 the JDBC
username/password are hardcoded which is a security risk; remove the literal
values and change the configuration to read credentials from environment or
external configuration at runtime (e.g., replace with placeholders or omit the
properties and pass
jakarta.persistence.jdbc.user/jakarta.persistence.jdbc.password via system
properties, environment variables, or a runtime persistence-unit property
override), store real secrets in environment variables or a secrets manager (or
dotenv loaded only in non-production), and update deployment docs and CI to
supply the credentials securely rather than committing them to source control.

@bamsemats bamsemats closed this Dec 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants