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
+}