diff --git a/.gitignore b/.gitignore index 6ac465db..c02e39f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,56 @@ +# Maven target/ -/.idea/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar + +# IntelliJ IDEA +.idea/ +*.iml +*.iws +*.ipr +out/ + +# VS Code +.vscode/ + +# OS +.DS_Store +Thumbs.db +desktop.ini + +# Logs +*.log +logs/ + +# Database files +*.db +*.mv.db +*.trace.db + +# Docker override +docker-compose.override.yml + +# Environment files +.env +.env.local +.env.*.local + +# Configuration files - VIKTIGAST! +src/main/resources/META-INF/persistence.xml +src/main/resources/application.properties +src/main/resources/application.yml +src/main/resources/application.yaml + +# Temp files +*.tmp +*.temp +*.bak +*.swp +*~ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..4f7a9f20 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM maven:3.8-openjdk-17 AS builder +WORKDIR /app +COPY pom.xml . +COPY src ./src +RUN mvn clean package -DskipTests + +FROM openjdk:17-jdk-slim +COPY --from=builder /app/target/*.jar app.jar +ENTRYPOINT ["java", "-jar", "app.jar"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..820b4893 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,36 @@ +version: '3.8' + +services: + mysql-db: + image: mysql:8.0 + container_name: music-mysql + restart: always + environment: + MYSQL_ROOT_PASSWORD: rootpassword + MYSQL_DATABASE: musicdb + MYSQL_USER: music_app + MYSQL_PASSWORD: music_app_pass + ports: + - "3306:3306" + volumes: + - mysql-data:/var/lib/mysql + command: + - --default-authentication-plugin=mysql_native_password + - --character-set-server=utf8mb4 + - --collation-server=utf8mb4_unicode_ci + + phpmyadmin: + image: phpmyadmin/phpmyadmin + container_name: music-phpmyadmin + restart: always + environment: + PMA_HOST: mysql-db + PMA_PORT: 3306 + ports: + - "8080:80" + depends_on: + - mysql-db + +volumes: + mysql-data: + driver: local diff --git a/musicdb.trace.db b/musicdb.trace.db new file mode 100644 index 00000000..3dc574c5 --- /dev/null +++ b/musicdb.trace.db @@ -0,0 +1,9 @@ +2025-12-16 15:54:24.333089+01:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement "create table Album (id bigint generated by default as identity, title varchar(255), [*]year integer not null, artist_id bigint, primary key (id))"; expected "identifier"; SQL statement: +create table Album (id bigint generated by default as identity, title varchar(255), year integer not null, artist_id bigint, primary key (id)) [42001-224] +2025-12-16 15:54:24.344107+01:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "ALBUM" not found; SQL statement: +alter table Album add constraint FKeornxb63o72l560qifpvd6ty foreign key (artist_id) references Artist [42102-224] +2025-12-16 15:54:24.345111+01:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "ALBUM" not found; SQL statement: +alter table Song add constraint FK5i04cpyv4w83t78ui446eelht foreign key (album_id) references Album [42102-224] diff --git a/mysql-init.sql b/mysql-init.sql new file mode 100644 index 00000000..cb6be394 --- /dev/null +++ b/mysql-init.sql @@ -0,0 +1,7 @@ +CREATE DATABASE IF NOT EXISTS musicdb; +USE musicdb; + + +CREATE USER IF NOT EXISTS 'musicuser'@'%' IDENTIFIED BY 'musicpass'; +GRANT ALL PRIVILEGES ON musicdb.* TO 'musicuser'@'%'; +FLUSH PRIVILEGES; diff --git a/persistence.xml.example b/persistence.xml.example new file mode 100644 index 00000000..38c1aa12 --- /dev/null +++ b/persistence.xml.example @@ -0,0 +1,24 @@ + + + + org.hibernate.jpa.HibernatePersistenceProvider + + org.example.model.Artist + org.example.model.Album + org.example.model.Song + org.example.model.Playlist + org.example.model.PlaylistSong + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 909503d0..a1a166cb 100644 --- a/pom.xml +++ b/pom.xml @@ -1,54 +1,36 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 + http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - org.example JavaJPA 1.0-SNAPSHOT - 25 + 17 + 17 UTF-8 - 6.0.1 - 3.27.6 - 5.21.0 + 5.6.15.Final + 8.0.33 + - org.junit.jupiter - junit-jupiter - ${junit.jupiter.version} - test + org.hibernate + hibernate-core + ${hibernate.version} - org.assertj - assertj-core - ${assertj.core.version} - test + javax.persistence + javax.persistence-api + 2.2 - org.mockito - mockito-junit-jupiter - ${mockito.version} - test + mysql + mysql-connector-java + ${mysql.version} - - org.hibernate.orm - hibernate-core - 7.2.0.Final - - - com.mysql - mysql-connector-j - 9.5.0 - runtime - - - io.github.classgraph - classgraph - 4.8.184 - diff --git a/src/main/java/org/example/App.java b/src/main/java/org/example/App.java deleted file mode 100644 index 165e5cd5..00000000 --- a/src/main/java/org/example/App.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.example; - -public class App { - public static void main(String[] args) { - System.out.println("Hello There!"); - } -} diff --git a/src/main/java/org/example/Main.java b/src/main/java/org/example/Main.java new file mode 100644 index 00000000..4078015c --- /dev/null +++ b/src/main/java/org/example/Main.java @@ -0,0 +1,50 @@ +package org.example; + +import persistence.repository.ArtistRepository; +import persistence.repository.AlbumRepository; +import persistence.repository.SongRepository; +import persistence.repository.PlaylistRepository; +import org.example.ui.MenuManager; +import javax.persistence.*; +import java.util.Scanner; + +public class Main { + public static void main(String[] args) { + System.out.println("🎵 Startar Musikappen..."); + + try { + // Skapa EntityManagerFactory + EntityManagerFactory emf = Persistence.createEntityManagerFactory("musicPU"); + System.out.println("✅ Ansluten till databasen"); + + // Skapa EntityManager + EntityManager em = emf.createEntityManager(); + + // Skapa Scanner för användarinput + Scanner scanner = new Scanner(System.in); + + // Skapa alla repositories + System.out.println("🔄 Skapar repositories..."); + ArtistRepository artistRepo = new ArtistRepository(em); + AlbumRepository albumRepo = new AlbumRepository(em); + SongRepository songRepo = new SongRepository(em); + PlaylistRepository playlistRepo = new PlaylistRepository(em); + + // Skapa MenuManager och starta huvudmenyn + MenuManager menuManager = new MenuManager(scanner, artistRepo, albumRepo, songRepo, playlistRepo); + + // Starta applikationen + menuManager.start(); + + // Stäng alla resurser + System.out.println("👋 Stänger applikationen..."); + scanner.close(); + em.close(); + emf.close(); + + } catch (Exception e) { + System.err.println("❌ Ett fel uppstod vid start: " + e.getMessage()); + e.printStackTrace(); + } + } +} diff --git a/src/main/java/org/example/model/Album.java b/src/main/java/org/example/model/Album.java new file mode 100644 index 00000000..8b130b0d --- /dev/null +++ b/src/main/java/org/example/model/Album.java @@ -0,0 +1,104 @@ +package org.example.model; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Table(name = "album") +public class Album { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String title; + + @Column(name = "release_year") + private int releaseYear; + + @ManyToOne + @JoinColumn(name = "artist_id") + private Artist artist; + + @OneToMany(mappedBy = "album", cascade = CascadeType.ALL, orphanRemoval = true) + private List songs = new ArrayList<>(); + + // Konstruktorer + public Album() { + } + + public Album(String title, int releaseYear) { + this.title = title; + this.releaseYear = releaseYear; + } + + public Album(String title, int releaseYear, Artist artist) { + this.title = title; + this.releaseYear = releaseYear; + this.artist = artist; + } + + // Getters och Setters + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public int getReleaseYear() { + return releaseYear; + } + + public void setReleaseYear(int releaseYear) { + this.releaseYear = releaseYear; + } + + public Artist getArtist() { + return artist; + } + + public void setArtist(Artist artist) { + this.artist = artist; + } + + public List getSongs() { + return songs; + } + + public void setSongs(List songs) { + this.songs = songs; + } + + // Hjälpmetod för att lägga till låt + public void addSong(Song song) { + songs.add(song); + song.setAlbum(this); + } + + // Hjälpmetod för att ta bort låt + public void removeSong(Song song) { + songs.remove(song); + song.setAlbum(null); + } + + @Override + public String toString() { + return "Album{" + + "id=" + id + + ", title='" + title + '\'' + + ", releaseYear=" + releaseYear + + ", artist=" + (artist != null ? artist.getName() : "null") + + ", songs=" + songs.size() + + '}'; + } +} diff --git a/src/main/java/org/example/model/Artist.java b/src/main/java/org/example/model/Artist.java new file mode 100644 index 00000000..84912793 --- /dev/null +++ b/src/main/java/org/example/model/Artist.java @@ -0,0 +1,32 @@ +package org.example.model; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; + +@Entity +public class Artist { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + + @OneToMany(mappedBy = "artist", cascade = CascadeType.ALL, orphanRemoval = true) + private List albums = new ArrayList<>(); + + public Artist() {} + + public Artist(String name) { + this.name = name; + } + + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + + public List getAlbums() { return albums; } + public void setAlbums(List albums) { this.albums = albums; } +} diff --git a/src/main/java/org/example/model/Playlist.java b/src/main/java/org/example/model/Playlist.java new file mode 100644 index 00000000..18cc5076 --- /dev/null +++ b/src/main/java/org/example/model/Playlist.java @@ -0,0 +1,44 @@ +package org.example.model; + +import javax.persistence.*; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Entity +public class Playlist { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + private LocalDateTime createdAt; + + @OneToMany(mappedBy = "playlist", cascade = CascadeType.ALL, orphanRemoval = true) + @OrderBy("position ASC") + private List entries = new ArrayList<>(); + + public Playlist() {} + + public Playlist(String name) { + this.name = name; + this.createdAt = LocalDateTime.now(); + } + + public void addSong(Song song, int position) { + PlaylistSong entry = new PlaylistSong(this, song, position); + entries.add(entry); + } + + public void removeSongBySongId(Long songId) { + entries.removeIf(entry -> songId.equals(entry.getSong().getId())); + } + + public Long getId() { return id; } + public String getName() { return name; } + public LocalDateTime getCreatedAt() { return createdAt; } + public List getEntries() { return entries; } + + public void setName(String name) { this.name = name; } +} diff --git a/src/main/java/org/example/model/PlaylistSong.java b/src/main/java/org/example/model/PlaylistSong.java new file mode 100644 index 00000000..8d2301c8 --- /dev/null +++ b/src/main/java/org/example/model/PlaylistSong.java @@ -0,0 +1,38 @@ +package org.example.model; + +import javax.persistence.*; +import java.time.LocalDateTime; + +@Entity +public class PlaylistSong { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(optional = false, fetch = FetchType.LAZY) + @JoinColumn(name = "playlist_id", nullable = false) + private Playlist playlist; + + @ManyToOne(optional = false, fetch = FetchType.LAZY) + @JoinColumn(name = "song_id", nullable = false) + private Song song; + + private int position; + private LocalDateTime addedAt; + + public PlaylistSong() {} + + public PlaylistSong(Playlist playlist, Song song, int position) { + this.playlist = playlist; + this.song = song; + this.position = position; + this.addedAt = LocalDateTime.now(); + } + + public Long getId() { return id; } + public Playlist getPlaylist() { return playlist; } + public Song getSong() { return song; } + public int getPosition() { return position; } + public LocalDateTime getAddedAt() { return addedAt; } +} diff --git a/src/main/java/org/example/model/Song.java b/src/main/java/org/example/model/Song.java new file mode 100644 index 00000000..8272e06b --- /dev/null +++ b/src/main/java/org/example/model/Song.java @@ -0,0 +1,36 @@ +package org.example.model; + +import javax.persistence.*; + +@Entity +public class Song { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String title; + private int duration; + + @ManyToOne + @JoinColumn(name = "album_id") + private Album album; + + public Song() {} + + public Song(String title, int duration) { + this.title = title; + this.duration = duration; + } + + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + + public String getTitle() { return title; } + public void setTitle(String title) { this.title = title; } + + public int getDuration() { return duration; } + public void setDuration(int duration) { this.duration = duration; } + + public Album getAlbum() { return album; } + public void setAlbum(Album album) { this.album = album; } +} diff --git a/src/main/java/org/example/ui/DisplayHelper.java b/src/main/java/org/example/ui/DisplayHelper.java new file mode 100644 index 00000000..1621ba99 --- /dev/null +++ b/src/main/java/org/example/ui/DisplayHelper.java @@ -0,0 +1,79 @@ +package org.example.ui; + +import org.example.model.Artist; +import org.example.model.Album; +import org.example.model.Song; +import java.util.List; + +public class DisplayHelper { + + public static void printArtistList(List artists) { + if (artists.isEmpty()) { + System.out.println("Inga artister hittades."); + return; + } + + System.out.println("\n" + "=".repeat(60)); + System.out.printf("%-4s %-25s %-15s%n", "ID", "NAMN", "ANTAL ALBUM"); + System.out.println("-".repeat(60)); + + for (Artist a : artists) { + System.out.printf("%-4d %-25s %-15d%n", + a.getId(), + truncate(a.getName(), 23), + a.getAlbums().size()); + } + } + + public static void printAlbumList(List albums) { + if (albums.isEmpty()) { + System.out.println("Inga album hittades."); + return; + } + + System.out.println("\n" + "=".repeat(70)); + System.out.printf("%-4s %-25s %-20s %-10s%n", + "ID", "TITEL", "ARTIST", "ÅR"); + System.out.println("-".repeat(70)); + + for (Album a : albums) { + System.out.printf("%-4d %-25s %-20s %-10d%n", + a.getId(), + truncate(a.getTitle(), 23), + truncate(a.getArtist().getName(), 18), + a.getReleaseYear()); + } + } + + public static void printSongList(List songs) { + if (songs.isEmpty()) { + System.out.println("Inga låtar hittades."); + return; + } + + System.out.println("\n" + "=".repeat(80)); + System.out.printf("%-4s %-20s %-25s %-20s %-10s%n", + "ID", "ARTIST", "ALBUM", "LÅT", "LÄNGD"); + System.out.println("-".repeat(80)); + + for (Song s : songs) { + System.out.printf("%-4d %-20s %-25s %-20s %-10s%n", + s.getId(), + truncate(s.getAlbum().getArtist().getName(), 18), + truncate(s.getAlbum().getTitle(), 23), + truncate(s.getTitle(), 18), + formatDuration(s.getDuration())); + } + } + + public static String formatDuration(int seconds) { + int minutes = seconds / 60; + int remainingSeconds = seconds % 60; + return String.format("%d:%02d", minutes, remainingSeconds); + } + + private static String truncate(String text, int maxLength) { + if (text.length() <= maxLength) return text; + return text.substring(0, maxLength - 3) + "..."; + } +} diff --git a/src/main/java/org/example/ui/InputValidator.java b/src/main/java/org/example/ui/InputValidator.java new file mode 100644 index 00000000..661dcbd6 --- /dev/null +++ b/src/main/java/org/example/ui/InputValidator.java @@ -0,0 +1,52 @@ +package org.example.ui; + +import java.util.Scanner; + +public class InputValidator { + + public static int getIntInput(Scanner scanner, String prompt, int min, int max) { + while (true) { + System.out.print(prompt); + try { + int value = scanner.nextInt(); + scanner.nextLine(); + + if (value >= min && value <= max) { + return value; + } else { + System.out.printf("❌ Måste vara mellan %d och %d. Försök igen.%n", min, max); + } + } catch (Exception e) { + System.out.println("❌ Ogiltigt nummer. Försök igen."); + scanner.nextLine(); + } + } + } + + public static long getLongInput(Scanner scanner, String prompt) { + while (true) { + System.out.print(prompt); + try { + long value = scanner.nextLong(); + scanner.nextLine(); + return value; + } catch (Exception e) { + System.out.println("❌ Ogiltigt nummer. Försök igen."); + scanner.nextLine(); + } + } + } + + public static String getNonEmptyString(Scanner scanner, String prompt) { + while (true) { + System.out.print(prompt); + String input = scanner.nextLine().trim(); + + if (!input.isEmpty()) { + return input; + } else { + System.out.println("❌ Fältet får inte vara tomt. Försök igen."); + } + } + } +} diff --git a/src/main/java/org/example/ui/MenuManager.java b/src/main/java/org/example/ui/MenuManager.java new file mode 100644 index 00000000..ab1bf78f --- /dev/null +++ b/src/main/java/org/example/ui/MenuManager.java @@ -0,0 +1,727 @@ +package org.example.ui; + +import persistence.repository.ArtistRepository; +import persistence.repository.AlbumRepository; +import persistence.repository.SongRepository; +import persistence.repository.PlaylistRepository; +import org.example.model.*; +import java.util.List; +import java.util.Scanner; + +public class MenuManager { + private Scanner scanner; + private ArtistRepository artistRepo; + private AlbumRepository albumRepo; + private SongRepository songRepo; + private PlaylistRepository playlistRepo; + + public MenuManager(Scanner scanner, + ArtistRepository artistRepo, + AlbumRepository albumRepo, + SongRepository songRepo, + PlaylistRepository playlistRepo) { + this.scanner = scanner; + this.artistRepo = artistRepo; + this.albumRepo = albumRepo; + this.songRepo = songRepo; + this.playlistRepo = playlistRepo; + } + + // HUVUDMETOD + public void start() { + System.out.println("🎵 VÄLKOMMEN TILL MUSIKAPPEN 🎵"); + + boolean running = true; + while (running) { + System.out.println("\n" + "=".repeat(40)); + System.out.println("=== HUVUDMENY ==="); + System.out.println("1. 🎤 Artister"); + System.out.println("2. 💿 Album"); + System.out.println("3. 🎶 Låtar"); + System.out.println("4. 📋 Spellistor"); + System.out.println("0. ❌ Avsluta"); + System.out.println("=".repeat(40)); + + int choice = InputValidator.getIntInput(scanner, "Val: ", 0, 4); + + switch (choice) { + case 1: artistMenu(); break; + case 2: albumMenu(); break; + case 3: songMenu(); break; + case 4: playlistMenu(); break; + case 0: + running = false; + System.out.println("\n👋 Tack för idag!"); + break; + } + } + } + + // ========== ARTIST-MENY ========== + private void artistMenu() { + boolean inMenu = true; + while (inMenu) { + System.out.println("\n=== ARTIST-MENY ==="); + System.out.println("1. Visa alla artister"); + System.out.println("2. Lägg till ny artist"); + System.out.println("3. Visa artists album"); + System.out.println("4. Ta bort artist"); + System.out.println("0. ← Tillbaka till huvudmeny"); + + int choice = InputValidator.getIntInput(scanner, "Val: ", 0, 4); + + switch (choice) { + case 1: showAllArtists(); break; + case 2: addArtist(); break; + case 3: showArtistAlbums(); break; + case 4: deleteArtist(); break; + case 0: inMenu = false; break; + } + } + } + + private void showAllArtists() { + System.out.println("\n" + "=".repeat(40)); + System.out.println("ALLA ARTISTER"); + System.out.println("=".repeat(40)); + + List artists = artistRepo.findAll(); + if (artists.isEmpty()) { + System.out.println("Inga artister finns."); + return; + } + + System.out.printf("%-4s %-30s %-15s%n", "ID", "NAMN", "ANTAL ALBUM"); + System.out.println("-".repeat(50)); + + for (Artist artist : artists) { + System.out.printf("%-4d %-30s %-15d%n", + artist.getId(), + truncate(artist.getName(), 28), + artist.getAlbums().size()); + } + } + + private void addArtist() { + System.out.println("\n" + "=".repeat(40)); + System.out.println("LÄGG TILL NY ARTIST"); + System.out.println("=".repeat(40)); + + String name = InputValidator.getNonEmptyString(scanner, "Artistens namn: "); + + try { + Artist artist = new Artist(name); + artistRepo.save(artist); + System.out.println("✅ Artist sparad med ID: " + artist.getId()); + } catch (Exception e) { + System.out.println("❌ Kunde inte spara artisten: " + e.getMessage()); + } + } + + private void showArtistAlbums() { + System.out.println("\n" + "=".repeat(40)); + System.out.println("VISA ARTISTS ALBUM"); + System.out.println("=".repeat(40)); + + // Visa alla artister först + List artists = artistRepo.findAll(); + if (artists.isEmpty()) { + System.out.println("Inga artister finns."); + return; + } + + System.out.println("\n--- Tillgängliga artister ---"); + for (Artist artist : artists) { + System.out.println(artist.getId() + ". " + artist.getName() + + " (" + artist.getAlbums().size() + " album)"); + } + + Long artistId = InputValidator.getLongInput(scanner, "\nAnge Artist ID: "); + Artist artist = artistRepo.findById(artistId); + + if (artist == null) { + System.out.println("❌ Artist hittades inte."); + return; + } + + List albums = albumRepo.findByArtistId(artistId); + + System.out.println("\n" + "=".repeat(50)); + System.out.println("ALBUM AV: " + artist.getName().toUpperCase()); + System.out.println("=".repeat(50)); + + if (albums.isEmpty()) { + System.out.println("Denna artist har inga album än."); + } else { + System.out.printf("%-4s %-30s %-10s%n", "ID", "ALBUM", "ÅR"); + System.out.println("-".repeat(50)); + + for (Album album : albums) { + System.out.printf("%-4d %-30s %-10d%n", + album.getId(), + truncate(album.getTitle(), 28), + album.getReleaseYear()); + } + } + } + + private void deleteArtist() { + System.out.println("\n" + "=".repeat(40)); + System.out.println("TA BORT ARTIST"); + System.out.println("=".repeat(40)); + + Long artistId = InputValidator.getLongInput(scanner, "Artist ID att ta bort: "); + Artist artist = artistRepo.findById(artistId); + + if (artist == null) { + System.out.println("❌ Artist hittades inte."); + return; + } + + System.out.println("\n⚠️ VARNING: Detta tar också bort artistens alla album och låtar!"); + System.out.println("Artist att ta bort: " + artist.getName()); + System.out.println("Antal album: " + artist.getAlbums().size()); + + System.out.print("\nÄr du SÄKER på att du vill ta bort denna artist? (skriv 'JA' för att bekräfta): "); + String confirm = scanner.nextLine().trim(); + + if (confirm.equalsIgnoreCase("JA")) { + boolean success = artistRepo.deleteArtist(artistId); + System.out.println(success ? "✅ Artist borttagen!" : "❌ Misslyckades att ta bort artisten."); + } else { + System.out.println("❌ Avbruten. Ingen artist togs bort."); + } + } + + // ========== ALBUM-MENY ========== + private void albumMenu() { + boolean inMenu = true; + while (inMenu) { + System.out.println("\n=== ALBUM-MENY ==="); + System.out.println("1. Visa alla album"); + System.out.println("2. Lägg till nytt album"); + System.out.println("3. Visa albums låtar"); + System.out.println("4. Ta bort album"); + System.out.println("0. ← Tillbaka till huvudmeny"); + + int choice = InputValidator.getIntInput(scanner, "Val: ", 0, 4); + + switch (choice) { + case 1: showAllAlbums(); break; + case 2: addAlbum(); break; + case 3: showAlbumSongs(); break; + case 4: deleteAlbum(); break; + case 0: inMenu = false; break; + } + } + } + + private void showAllAlbums() { + System.out.println("\n" + "=".repeat(50)); + System.out.println("ALLA ALBUM"); + System.out.println("=".repeat(50)); + + List albums = albumRepo.findAll(); + if (albums.isEmpty()) { + System.out.println("Inga album finns."); + return; + } + + System.out.printf("%-4s %-25s %-20s %-10s%n", "ID", "ALBUM", "ARTIST", "ÅR"); + System.out.println("-".repeat(60)); + + for (Album album : albums) { + String artistName = "Okänd artist"; + if (album.getArtist() != null) { + artistName = album.getArtist().getName(); + } + + System.out.printf("%-4d %-25s %-20s %-10d%n", + album.getId(), + truncate(album.getTitle(), 23), + truncate(artistName, 18), + album.getReleaseYear()); + } + } + + private void addAlbum() { + System.out.println("\n" + "=".repeat(40)); + System.out.println("LÄGG TILL NYTT ALBUM"); + System.out.println("=".repeat(40)); + + // Visa alla artister först + List artists = artistRepo.findAll(); + if (artists.isEmpty()) { + System.out.println("❌ Inga artister finns. Lägg till en artist först."); + return; + } + + System.out.println("\n--- Välj artist ---"); + System.out.printf("%-4s %-30s%n", "ID", "ARTIST"); + System.out.println("-".repeat(35)); + + for (Artist artist : artists) { + System.out.printf("%-4d %-30s%n", + artist.getId(), + truncate(artist.getName(), 28)); + } + + Long artistId = InputValidator.getLongInput(scanner, "\nAnge Artist ID: "); + Artist artist = artistRepo.findById(artistId); + + if (artist == null) { + System.out.println("❌ Artist hittades inte."); + return; + } + + String title = InputValidator.getNonEmptyString(scanner, "Albumets titel: "); + int year = InputValidator.getIntInput(scanner, "Utgivningsår: ", 1900, 2100); + + // Bekräfta + System.out.println("\n" + "=".repeat(40)); + System.out.println("BEKRÄFTA ALBUM"); + System.out.println("=".repeat(40)); + System.out.println("Artist: " + artist.getName()); + System.out.println("Album: " + title); + System.out.println("År: " + year); + + System.out.print("\nVill du spara detta album? (skriv 'JA' för att bekräfta): "); + String confirm = scanner.nextLine().trim(); + + if (!confirm.equalsIgnoreCase("JA")) { + System.out.println("❌ Avbruten. Albumet sparades inte."); + return; + } + + // Skapa och spara album + try { + Album album = new Album(title, year); + album.setArtist(artist); + albumRepo.save(album); + System.out.println("✅ Album sparad med ID: " + album.getId()); + } catch (Exception e) { + System.out.println("❌ Kunde inte spara albumet: " + e.getMessage()); + e.printStackTrace(); + } + } + + private void showAlbumSongs() { + System.out.println("\n" + "=".repeat(40)); + System.out.println("VISA ALBUMS LÅTAR"); + System.out.println("=".repeat(40)); + + // Visa alla album först + List albums = albumRepo.findAll(); + if (albums.isEmpty()) { + System.out.println("Inga album finns."); + return; + } + + System.out.println("\n--- Tillgängliga album ---"); + System.out.printf("%-4s %-25s %-20s%n", "ID", "ALBUM", "ARTIST"); + System.out.println("-".repeat(50)); + + for (Album album : albums) { + String artistName = "Okänd"; + if (album.getArtist() != null) { + artistName = album.getArtist().getName(); + } + + System.out.printf("%-4d %-25s %-20s%n", + album.getId(), + truncate(album.getTitle(), 23), + truncate(artistName, 18)); + } + + Long albumId = InputValidator.getLongInput(scanner, "\nAnge Album ID: "); + Album album = albumRepo.findById(albumId); + + if (album == null) { + System.out.println("❌ Album hittades inte."); + return; + } + + System.out.println("\n" + "=".repeat(50)); + System.out.println("LÅTAR PÅ: " + album.getTitle().toUpperCase()); + if (album.getArtist() != null) { + System.out.println("ARTIST: " + album.getArtist().getName()); + } + System.out.println("ÅR: " + album.getReleaseYear()); + System.out.println("=".repeat(50)); + + List songs = songRepo.findByAlbumId(albumId); + if (songs.isEmpty()) { + System.out.println("Det här albumet har inga låtar än."); + } else { + System.out.printf("%-4s %-30s %-15s%n", "ID", "LÅT", "LÄNGD"); + System.out.println("-".repeat(50)); + + for (Song song : songs) { + System.out.printf("%-4d %-30s %-15s%n", + song.getId(), + truncate(song.getTitle(), 28), + formatDuration(song.getDuration())); + } + } + } + + private void deleteAlbum() { + System.out.println("\n" + "=".repeat(40)); + System.out.println("TA BORT ALBUM"); + System.out.println("=".repeat(40)); + + Long albumId = InputValidator.getLongInput(scanner, "Album ID att ta bort: "); + Album album = albumRepo.findById(albumId); + + if (album == null) { + System.out.println("❌ Album hittades inte."); + return; + } + + System.out.println("\n⚠️ VARNING: Detta tar också bort albumets alla låtar!"); + System.out.println("Album att ta bort: " + album.getTitle()); + if (album.getArtist() != null) { + System.out.println("Artist: " + album.getArtist().getName()); + } + System.out.println("Antal låtar: " + album.getSongs().size()); + + System.out.print("\nÄr du SÄKER på att du vill ta bort detta album? (skriv 'JA' för att bekräfta): "); + String confirm = scanner.nextLine().trim(); + + if (confirm.equalsIgnoreCase("JA")) { + boolean success = albumRepo.deleteAlbum(albumId); + System.out.println(success ? "✅ Album borttaget!" : "❌ Misslyckades att ta bort albumet."); + } else { + System.out.println("❌ Avbruten. Inget album togs bort."); + } + } + + // ========== LÅT-MENY ========== + private void songMenu() { + boolean inMenu = true; + while (inMenu) { + System.out.println("\n=== LÅT-MENY ==="); + System.out.println("1. Visa alla låtar"); + System.out.println("2. Lägg till ny låt"); + System.out.println("3. Ta bort låt"); + System.out.println("0. ← Tillbaka till huvudmeny"); + + int choice = InputValidator.getIntInput(scanner, "Val: ", 0, 3); + + switch (choice) { + case 1: showAllSongs(); break; + case 2: addSong(); break; + case 3: deleteSong(); break; + case 0: inMenu = false; break; + } + } + } + + private void showAllSongs() { + System.out.println("\n" + "=".repeat(60)); + System.out.println("ALLA LÅTAR"); + System.out.println("=".repeat(60)); + + List songs = songRepo.findAll(); + if (songs.isEmpty()) { + System.out.println("Inga låtar finns."); + return; + } + + System.out.printf("%-4s %-20s %-25s %-15s%n", "ID", "LÅT", "ALBUM", "LÄNGD"); + System.out.println("-".repeat(65)); + + for (Song song : songs) { + String albumTitle = "Okänt album"; + if (song.getAlbum() != null) { + albumTitle = song.getAlbum().getTitle(); + } + + System.out.printf("%-4d %-20s %-25s %-15s%n", + song.getId(), + truncate(song.getTitle(), 18), + truncate(albumTitle, 23), + formatDuration(song.getDuration())); + } + } + + private void addSong() { + System.out.println("\n" + "=".repeat(40)); + System.out.println("LÄGG TILL NY LÅT"); + System.out.println("=".repeat(40)); + + // Visa alla album först + List albums = albumRepo.findAll(); + if (albums.isEmpty()) { + System.out.println("❌ Inga album finns. Lägg till ett album först."); + return; + } + + System.out.println("\n--- Välj album ---"); + System.out.printf("%-4s %-25s %-20s %-10s%n", "ID", "ALBUM", "ARTIST", "ÅR"); + System.out.println("-".repeat(60)); + + for (Album album : albums) { + String artistName = "Okänd"; + if (album.getArtist() != null) { + artistName = album.getArtist().getName(); + } + + System.out.printf("%-4d %-25s %-20s %-10d%n", + album.getId(), + truncate(album.getTitle(), 23), + truncate(artistName, 18), + album.getReleaseYear()); + } + + Long albumId = InputValidator.getLongInput(scanner, "\nAnge Album ID: "); + Album album = albumRepo.findById(albumId); + + if (album == null) { + System.out.println("❌ Album hittades inte."); + return; + } + + String title = InputValidator.getNonEmptyString(scanner, "Låtens titel: "); + int duration = InputValidator.getIntInput(scanner, "Längd i sekunder: ", 1, 3600); + + // Bekräfta + System.out.println("\n" + "=".repeat(40)); + System.out.println("BEKRÄFTA LÅT"); + System.out.println("=".repeat(40)); + System.out.println("Låt: " + title); + System.out.println("Längd: " + formatDuration(duration)); + System.out.println("Album: " + album.getTitle()); + if (album.getArtist() != null) { + System.out.println("Artist: " + album.getArtist().getName()); + } + + System.out.print("\nVill du spara denna låt? (skriv 'JA' för att bekräfta): "); + String confirm = scanner.nextLine().trim(); + + if (!confirm.equalsIgnoreCase("JA")) { + System.out.println("❌ Avbruten. Låten sparades inte."); + return; + } + + // Skapa och spara låten + try { + Song song = new Song(title, duration); + song.setAlbum(album); + songRepo.save(song); + System.out.println("✅ Låt sparad med ID: " + song.getId()); + } catch (Exception e) { + System.out.println("❌ Kunde inte spara låten: " + e.getMessage()); + } + } + + private void deleteSong() { + System.out.println("\n" + "=".repeat(40)); + System.out.println("TA BORT LÅT"); + System.out.println("=".repeat(40)); + + List songs = songRepo.findAll(); + if (songs.isEmpty()) { + System.out.println("⚠️ Inga låtar finns att ta bort."); + return; + } + + System.out.println("\n--- Tillgängliga låtar ---"); + System.out.printf("%-4s %-25s %-20s%n", "ID", "LÅT", "ALBUM"); + System.out.println("-".repeat(50)); + + for (Song song : songs) { + String albumTitle = "Okänt album"; + if (song.getAlbum() != null) { + albumTitle = song.getAlbum().getTitle(); + } + + System.out.printf("%-4d %-25s %-20s%n", + song.getId(), + truncate(song.getTitle(), 23), + truncate(albumTitle, 18)); + } + + Long songId = InputValidator.getLongInput(scanner, "\nAnge ID på låten du vill ta bort: "); + Song song = songRepo.findById(songId); + + if (song == null) { + System.out.println("❌ Ingen låt hittades med ID: " + songId); + return; + } + + System.out.println("\n" + "=".repeat(40)); + System.out.println("BEKRÄFTA BORTTAGNING"); + System.out.println("=".repeat(40)); + System.out.println("Låt att ta bort: " + song.getTitle()); + System.out.println("Längd: " + formatDuration(song.getDuration())); + + if (song.getAlbum() != null) { + System.out.println("Album: " + song.getAlbum().getTitle()); + if (song.getAlbum().getArtist() != null) { + System.out.println("Artist: " + song.getAlbum().getArtist().getName()); + } + } + + System.out.print("\n⚠️ Är du SÄKER på att du vill ta bort denna låt? (skriv 'JA' för att bekräfta): "); + String confirm = scanner.nextLine().trim(); + + if (confirm.equalsIgnoreCase("JA")) { + boolean success = songRepo.deleteSong(songId); + System.out.println(success ? "✅ Låt borttagen!" : "❌ Kunde inte ta bort låten."); + } else { + System.out.println("❌ Avbruten. Låten togs INTE bort."); + } + } + + // ========== SPELLISTA-MENY ========== + private void playlistMenu() { + boolean inMenu = true; + while (inMenu) { + System.out.println("\n=== SPELLISTA-MENY ==="); + System.out.println("1. Visa alla spellistor"); + System.out.println("2. Skapa ny spellista"); + System.out.println("3. Lägg till låt i spellista"); + System.out.println("4. Ta bort låt från spellista"); + System.out.println("5. Visa spellista"); + System.out.println("6. Ta bort spellista"); + System.out.println("0. ← Tillbaka till huvudmeny"); + + int choice = InputValidator.getIntInput(scanner, "Val: ", 0, 6); + + switch (choice) { + case 1: showAllPlaylists(); break; + case 2: createPlaylist(); break; + case 3: addSongToPlaylist(); break; + case 4: removeSongFromPlaylist(); break; + case 5: showPlaylistDetails(); break; + case 6: deletePlaylist(); break; + case 0: inMenu = false; break; + } + } + } + + private void showAllPlaylists() { + System.out.println("\n" + "=".repeat(40)); + System.out.println("ALLA SPELLISTOR"); + System.out.println("=".repeat(40)); + + List playlists = playlistRepo.findAll(); + if (playlists.isEmpty()) { + System.out.println("Inga spellistor finns."); + return; + } + + System.out.printf("%-4s %-25s %-15s%n", "ID", "NAMN", "ANTAL LÅTAR"); + System.out.println("-".repeat(45)); + + for (Playlist playlist : playlists) { + System.out.printf("%-4d %-25s %-15d%n", + playlist.getId(), + truncate(playlist.getName(), 23), + playlist.getEntries().size()); + } + } + + private void createPlaylist() { + System.out.println("\n" + "=".repeat(40)); + System.out.println("SKAPA NY SPELLISTA"); + System.out.println("=".repeat(40)); + + String name = InputValidator.getNonEmptyString(scanner, "Spellistans namn: "); + + try { + Playlist playlist = playlistRepo.createPlaylist(name); + System.out.println("✅ Spellista skapad med ID: " + playlist.getId()); + } catch (Exception e) { + System.out.println("❌ Kunde inte skapa spellista: " + e.getMessage()); + } + } + + private void addSongToPlaylist() { + System.out.println("\n" + "=".repeat(40)); + System.out.println("LÄGG TILL LÅT I SPELLISTA"); + System.out.println("=".repeat(40)); + + // Visa spellistor + List playlists = playlistRepo.findAll(); + if (playlists.isEmpty()) { + System.out.println("❌ Inga spellistor finns. Skapa en först."); + return; + } + + System.out.println("\n--- Välj spellista ---"); + for (Playlist playlist : playlists) { + System.out.println(playlist.getId() + ". " + playlist.getName() + + " (" + playlist.getEntries().size() + " låtar)"); + } + + Long playlistId = InputValidator.getLongInput(scanner, "\nAnge Spellista ID: "); + + // Visa låtar + List songs = songRepo.findAll(); + if (songs.isEmpty()) { + System.out.println("❌ Inga låtar finns. Lägg till låtar först."); + return; + } + + System.out.println("\n--- Välj låt ---"); + for (Song song : songs) { + String albumInfo = ""; + if (song.getAlbum() != null) { + albumInfo = " (Album: " + song.getAlbum().getTitle() + ")"; + } + System.out.println(song.getId() + ". " + song.getTitle() + albumInfo); + } + + Long songId = InputValidator.getLongInput(scanner, "\nAnge Låt ID: "); + int position = InputValidator.getIntInput(scanner, "Position i spellistan: ", 1, 1000); + + try { + playlistRepo.addSong(playlistId, songId, position); + System.out.println("✅ Låt tillagd i spellistan!"); + } catch (Exception e) { + System.out.println("❌ Kunde inte lägga till låt: " + e.getMessage()); + } + } + + private void removeSongFromPlaylist() { + System.out.println("\n" + "=".repeat(40)); + System.out.println("TA BORT LÅT FRÅN SPELLISTA"); + System.out.println("=".repeat(40)); + + System.out.println("Denna funktion kräver mer avancerad implementation."); + System.out.println("Kommer snart..."); + } + + private void showPlaylistDetails() { + System.out.println("\n" + "=".repeat(40)); + System.out.println("VISA SPELLISTA"); + System.out.println("=".repeat(40)); + + System.out.println("Denna funktion kräver mer avancerad implementation."); + System.out.println("Kommer snart..."); + } + + private void deletePlaylist() { + System.out.println("\n" + "=".repeat(40)); + System.out.println("TA BORT SPELLISTA"); + System.out.println("=".repeat(40)); + + System.out.println("Denna funktion kräver mer avancerad implementation."); + System.out.println("Kommer snart..."); + } + + // ========== HJÄLPMETODER ========== + private String truncate(String text, int maxLength) { + if (text == null) return ""; + if (text.length() <= maxLength) return text; + return text.substring(0, maxLength - 3) + "..."; + } + + private String formatDuration(int seconds) { + int minutes = seconds / 60; + int remainingSeconds = seconds % 60; + return String.format("%d:%02d", minutes, remainingSeconds); + } +} diff --git a/src/main/java/persistence/repository/AlbumRepository.java b/src/main/java/persistence/repository/AlbumRepository.java new file mode 100644 index 00000000..a015dff3 --- /dev/null +++ b/src/main/java/persistence/repository/AlbumRepository.java @@ -0,0 +1,59 @@ +package persistence.repository; + +import org.example.model.Album; +import javax.persistence.EntityManager; +import javax.persistence.EntityTransaction; +import java.util.List; + +public class AlbumRepository { + private final EntityManager em; + + public AlbumRepository(EntityManager em) { + this.em = em; + } + + public Album save(Album album) { + EntityTransaction transaction = em.getTransaction(); + try { + transaction.begin(); + em.persist(album); + transaction.commit(); + return album; + } catch (Exception e) { + if (transaction.isActive()) transaction.rollback(); + throw e; + } + } + + public Album findById(Long id) { + return em.find(Album.class, id); + } + + public List findAll() { + return em.createQuery("SELECT a FROM Album a", Album.class).getResultList(); + } + + public List findByArtistId(Long artistId) { + return em.createQuery("SELECT a FROM Album a WHERE a.artist.id = :artistId", Album.class) + .setParameter("artistId", artistId) + .getResultList(); + } + + public boolean deleteAlbum(Long id) { + EntityTransaction transaction = em.getTransaction(); + try { + transaction.begin(); + Album album = em.find(Album.class, id); + if (album != null) { + em.remove(album); + transaction.commit(); + return true; + } + transaction.rollback(); + return false; + } catch (Exception e) { + if (transaction.isActive()) transaction.rollback(); + return false; + } + } +} diff --git a/src/main/java/persistence/repository/ArtistRepository.java b/src/main/java/persistence/repository/ArtistRepository.java new file mode 100644 index 00000000..943be7cd --- /dev/null +++ b/src/main/java/persistence/repository/ArtistRepository.java @@ -0,0 +1,53 @@ +package persistence.repository; + +import org.example.model.Artist; +import javax.persistence.EntityManager; +import javax.persistence.EntityTransaction; +import java.util.List; + +public class ArtistRepository { + private final EntityManager em; + + public ArtistRepository(EntityManager em) { + this.em = em; + } + + public Artist save(Artist artist) { + EntityTransaction transaction = em.getTransaction(); + try { + transaction.begin(); + em.persist(artist); + transaction.commit(); + return artist; + } catch (Exception e) { + if (transaction.isActive()) transaction.rollback(); + throw e; + } + } + + public Artist findById(Long id) { + return em.find(Artist.class, id); + } + + public List findAll() { + return em.createQuery("SELECT a FROM Artist a", Artist.class).getResultList(); + } + + public boolean deleteArtist(Long id) { + EntityTransaction transaction = em.getTransaction(); + try { + transaction.begin(); + Artist artist = em.find(Artist.class, id); + if (artist != null) { + em.remove(artist); + transaction.commit(); + return true; + } + transaction.rollback(); + return false; + } catch (Exception e) { + if (transaction.isActive()) transaction.rollback(); + return false; + } + } +} diff --git a/src/main/java/persistence/repository/PlaylistRepository.java b/src/main/java/persistence/repository/PlaylistRepository.java new file mode 100644 index 00000000..36fd3821 --- /dev/null +++ b/src/main/java/persistence/repository/PlaylistRepository.java @@ -0,0 +1,82 @@ +package persistence.repository; + +import org.example.model.Playlist; +import org.example.model.Song; +import org.example.model.PlaylistSong; +import javax.persistence.EntityManager; +import javax.persistence.EntityTransaction; +import java.util.List; + +public class PlaylistRepository { + private final EntityManager em; + + public PlaylistRepository(EntityManager em) { + this.em = em; + } + + public Playlist createPlaylist(String name) { + EntityTransaction transaction = em.getTransaction(); + try { + transaction.begin(); + Playlist p = new Playlist(name); + em.persist(p); + transaction.commit(); + return p; + } catch (Exception e) { + if (transaction.isActive()) transaction.rollback(); + throw e; + } + } + + public List findAll() { + return em.createQuery("SELECT p FROM Playlist p ORDER BY p.createdAt DESC", Playlist.class) + .getResultList(); + } + + public void addSong(Long playlistId, Long songId, int position) { + EntityTransaction transaction = em.getTransaction(); + try { + transaction.begin(); + Playlist playlist = em.find(Playlist.class, playlistId); + if (playlist == null) throw new IllegalArgumentException("Playlist not found."); + + Song song = em.find(Song.class, songId); + if (song == null) throw new IllegalArgumentException("Song not found."); + + PlaylistSong entry = new PlaylistSong(playlist, song, position); + em.persist(entry); + transaction.commit(); + } catch (Exception e) { + if (transaction.isActive()) transaction.rollback(); + throw e; + } + } + + public void removeSong(Long playlistId, Long songId) { + EntityTransaction transaction = em.getTransaction(); + try { + transaction.begin(); + Playlist playlist = em.find(Playlist.class, playlistId); + if (playlist == null) throw new IllegalArgumentException("Playlist not found."); + + // Hitta PlaylistSong-attributet + PlaylistSong entry = em.createQuery( + "SELECT ps FROM PlaylistSong ps WHERE ps.playlist.id = :playlistId AND ps.song.id = :songId", + PlaylistSong.class) + .setParameter("playlistId", playlistId) + .setParameter("songId", songId) + .getResultStream() + .findFirst() + .orElse(null); + + if (entry != null) { + em.remove(entry); + } + + transaction.commit(); + } catch (Exception e) { + if (transaction.isActive()) transaction.rollback(); + throw e; + } + } +} diff --git a/src/main/java/persistence/repository/SongRepository.java b/src/main/java/persistence/repository/SongRepository.java new file mode 100644 index 00000000..da9cf57a --- /dev/null +++ b/src/main/java/persistence/repository/SongRepository.java @@ -0,0 +1,59 @@ +package persistence.repository; + +import org.example.model.Song; +import javax.persistence.EntityManager; +import javax.persistence.EntityTransaction; +import java.util.List; + +public class SongRepository { + private final EntityManager em; + + public SongRepository(EntityManager em) { + this.em = em; + } + + public Song save(Song song) { + EntityTransaction transaction = em.getTransaction(); + try { + transaction.begin(); + em.persist(song); + transaction.commit(); + return song; + } catch (Exception e) { + if (transaction.isActive()) transaction.rollback(); + throw e; + } + } + + public Song findById(Long id) { + return em.find(Song.class, id); + } + + public List findAll() { + return em.createQuery("SELECT s FROM Song s", Song.class).getResultList(); + } + + public List findByAlbumId(Long albumId) { + return em.createQuery("SELECT s FROM Song s WHERE s.album.id = :albumId", Song.class) + .setParameter("albumId", albumId) + .getResultList(); + } + + public boolean deleteSong(Long id) { + EntityTransaction transaction = em.getTransaction(); + try { + transaction.begin(); + Song song = em.find(Song.class, id); + if (song != null) { + em.remove(song); + transaction.commit(); + return true; + } + transaction.rollback(); + return false; + } catch (Exception e) { + if (transaction.isActive()) transaction.rollback(); + return false; + } + } +} diff --git a/src/main/java/persistence/repository/TransactionHelper.java b/src/main/java/persistence/repository/TransactionHelper.java new file mode 100644 index 00000000..74f2579b --- /dev/null +++ b/src/main/java/persistence/repository/TransactionHelper.java @@ -0,0 +1,26 @@ +package persistence.repository; + +import javax.persistence.EntityManager; + +public class TransactionHelper { + private final EntityManager em; + + public TransactionHelper(EntityManager em) { + this.em = em; + } + + public void executeInTransaction(Runnable operation) { + var transaction = em.getTransaction(); + try { + transaction.begin(); + operation.run(); + transaction.commit(); + } catch (Exception e) { + if (transaction.isActive()) { + transaction.rollback(); + } + System.err.println("Transaction failed: " + e.getMessage()); + throw e; + } + } +} diff --git a/src/main/resources/META-INF/persistence.xml.example b/src/main/resources/META-INF/persistence.xml.example new file mode 100644 index 00000000..65644b90 --- /dev/null +++ b/src/main/resources/META-INF/persistence.xml.example @@ -0,0 +1,22 @@ + + + + org.hibernate.jpa.HibernatePersistenceProvider + org.example.model.Artist + org.example.model.Album + org.example.model.Song + org.example.model.Playlist + org.example.model.PlaylistSong + + + + + + + + + + + + diff --git a/src/test/java/org/example/AppTest.java b/src/test/java/org/example/AppTest.java index d522a7e2..af6a1eac 100644 --- a/src/test/java/org/example/AppTest.java +++ b/src/test/java/org/example/AppTest.java @@ -9,4 +9,4 @@ class AppTest { void test() { assertThat(true).isTrue(); } -} \ No newline at end of file +}