diff --git a/.gitignore b/.gitignore index 6ac465db..244268f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target/ /.idea/ +.env diff --git a/README.md b/README.md index 5150e50f..2ab82cb2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/_uV8Mn8f) # 📘 Projektarbete: JPA + Hibernate med GitHub-flöde Projektet genomförs som antingen en Java CLI-applikation eller med hjälp av JavaFX om ni vill ha ett grafiskt gränssnitt. @@ -9,11 +10,11 @@ Kommunikation med databasen ska ske med JPA och Hibernate, enligt code first-met ## 🗓️ Veckoplanering med Checklista ### ✅ Vecka 1 – Grundläggning och struktur -- [ ] Klona GitHub-repo -- [ ] Konfigurera persistence.xml eller använd PersistenceConfiguration i kod -- [ ] Skapa entiteter och verifiera tabellgenerering -- [ ] Lägg till relationer (One-to-Many, Many-to-Many) -- [ ] Arbeta på feature-branches och använd pull requests för kodgranskning +- [x] Klona GitHub-repo +- [x] Konfigurera persistence.xml eller använd PersistenceConfiguration i kod +- [x] Skapa entiteter och verifiera tabellgenerering +- [x] Lägg till relationer (One-to-Many, Many-to-Many) +- [x] Arbeta på feature-branches och använd pull requests för kodgranskning ### ✅ Vecka 2 – Funktionalitet och relationer - [ ] Dela upp funktioner mellan gruppmedlemmar diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..afd5779f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +version: '3.8' + +services: + mysql: + image: mysql:9.5.0 + container_name: restaurant_booking_db + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: ${MYSQL_DATABASE} + ports: + - "3306:3306" + volumes: + - mysql_data:/var/lib/mysql + restart: unless-stopped + +volumes: + mysql_data: diff --git a/pom.xml b/pom.xml index 909503d0..20c6f873 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,16 @@ 5.21.0 + + com.zaxxer + HikariCP + 7.0.2 + + + org.hibernate.orm + hibernate-hikaricp + 7.2.0.Final + org.junit.jupiter junit-jupiter @@ -50,5 +60,10 @@ classgraph 4.8.184 + + jakarta.persistence + jakarta.persistence-api + 3.2.0 + diff --git a/src/main/java/org/example/App.java b/src/main/java/org/example/App.java index 165e5cd5..6e0a41dd 100644 --- a/src/main/java/org/example/App.java +++ b/src/main/java/org/example/App.java @@ -1,7 +1,347 @@ package org.example; +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; +import jakarta.persistence.*; +import org.example.entity.*; +import org.example.entity.Table; +import org.example.entity.service.BookingService; +import org.hibernate.jpa.HibernatePersistenceConfiguration; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + public class App { public static void main(String[] args) { - System.out.println("Hello There!"); + + List> entities = getEntities("org.example.entity"); + + final PersistenceConfiguration cfg = new HibernatePersistenceConfiguration("emf") + .jdbcUrl("jdbc:mysql://localhost:3306/restaurant_booking") + .jdbcUsername(System.getenv("MYSQL_USER")) + .jdbcPassword(System.getenv("MYSQL_ROOT_PASSWORD")) + .property("hibernate.connection.provider_class", "org.hibernate.hikaricp.internal.HikariCPConnectionProvider") + .property("hibernate.hikari.maximumPoolSize", "10") + .property("hibernate.hikari.minimumIdle", "5") + .property("hibernate.hikari.idleTimeout", "300000") + .property("hibernate.hikari.connectionTimeout", "20000") + .property("hibernate.hbm2ddl.auto", "update") + .property("hibernate.show_sql", "true") + .property("hibernate.format_sql", "true") + .property("hibernate.highlight_sql", "true") + .managedClasses(entities); + + try (EntityManagerFactory emf = cfg.createEntityManagerFactory()) { + + // Skapa initial data om den inte finns + createInitialData(emf); + + // Starta huvudmeny + BookingService bookingService = new BookingService(emf); + mainMenu(bookingService, emf); + + } + } + + private static void createInitialData(EntityManagerFactory emf) { + // Kolla om data redan finns + Long count = emf.callInTransaction(em -> + em.createQuery("SELECT COUNT(ts) FROM TimeSlot ts", Long.class).getSingleResult() + ); + + if (count == 0) { + hours(emf); + createGuest(emf); + System.out.println("Initial data created!"); + System.out.println("Create tables manually in the database if necessary!"); + } + } + + private static void createGuest(EntityManagerFactory emf) { + emf.runInTransaction(em -> { + em.persist(new Guest("Gabriela", "Bord för fyra", "072762668")); + em.persist(new Guest("Samuel", "Bord för 3", "072778882")); + em.persist(new Guest("Anna", "VIP", "0701234567")); + em.persist(new Guest("Erik", "Allergisk mot nötter", "0709876543")); + }); + } + + private static void hours(EntityManagerFactory emf) { + emf.runInTransaction(em -> { + String[] times = {"16:00", "16:30", "17:00", "17:30", "18:00", "18:30", "19:00"}; + for (String start : times) { + String[] parts = start.split(":"); + int hour = Integer.parseInt(parts[0]) + 2; + String end = hour + ":" + parts[1]; + em.persist(new TimeSlot(start, end)); + } + }); + } + + private static List> getEntities(String pkg) { + try (ScanResult scanResult = new ClassGraph() + .enableClassInfo() + .enableAnnotationInfo() + .acceptPackages(pkg) + .scan()) { + return scanResult.getClassesWithAnnotation(Entity.class).loadClasses(); + } + } + + public static void mainMenu(BookingService bookingService, EntityManagerFactory emf) { + boolean running = true; + while (running) { + String menu = """ + + ╔════════════════════════════════════╗ + ║ RESTAURANT BOOKING SYSTEM ║ + ╠════════════════════════════════════╣ + ║ 1. CREATE BOOKING ║ + ║ 2. UPDATE BOOKING ║ + ║ 3. VIEW ALL BOOKINGS ║ + ║ 4. DELETE BOOKING ║ + ║ 5. VIEW TABLES ║ + ║ 6. VIEW GUESTS ║ + ║ 7. EXIT ║ + ╚════════════════════════════════════╝ + """; + + String select = IO.readln(menu + "\nSelect option: ").toLowerCase(); + + switch (select) { + case "create booking", "cb", "1" -> createBookingMenu(bookingService); + case "update booking", "ub", "2" -> updateBookingMenu(bookingService); + case "view all bookings", "rb", "3" -> showBookings(bookingService); + case "delete booking", "db", "4" -> deleteBookingMenu(bookingService); + case "view tables", "5" -> viewTables(bookingService); + case "view guests", "6" -> viewGuests(bookingService); + case "exit", "7" -> { + System.out.println("Goodbye!"); + running = false; + } + default -> System.out.println("Invalid option!"); + } + } + } + + private static void createBookingMenu(BookingService bookingService) { + System.out.println("\n═══ CREATE NEW BOOKING ═══"); + + try { + // Visa tillgängliga bord + List tables = bookingService.getAllTables(); + System.out.println("\n📋 Available Tables:"); + tables.forEach(t -> System.out.println(" " + t.getId() + ". Table " + t.getTableNumber() + " (Capacity: " + t.getCapacity() + ")")); + + Long tableId = Long.parseLong(IO.readln("\nEnter Table ID: ")); + + // Visa tillgängliga tider + List timeSlots = bookingService.getAllTimeSlots(); + System.out.println("\n⏰ Available Time Slots:"); + timeSlots.forEach(ts -> System.out.println(" " + ts.getId() + ". " + ts.getStartTime() + " - " + ts.getFinishTime())); + + Long timeSlotId = Long.parseLong(IO.readln("\nEnter TimeSlot ID: ")); + + // Datum med validering + LocalDate date = null; + while (date == null) { + String dateStr = IO.readln("\nEnter date (YYYY-MM-DD): "); + try { + date = LocalDate.parse(dateStr, DateTimeFormatter.ISO_LOCAL_DATE); + + // Validera att datumet är korrekt + LocalDate today = LocalDate.now(); + // hur många månader fram man kan boka(kan ä + LocalDate maxDate = today.plusMonths(3); + + if (date.isBefore(today)) { + System.out.println("Date cannot be in the past! Please enter a future date."); + date = null; + } else if (date.isAfter(maxDate)) { + System.out.println("Date cannot be more than 3 months in the future! (Max: " + maxDate + ")"); + date = null; + } + } catch (Exception e) { + System.out.println("Invalid date format! Please use YYYY-MM-DD"); + } + } + + // Antal gäster + int partySize = Integer.parseInt(IO.readln("\nEnter party size: ")); + + // Lägg till gäster + List guestIds = new ArrayList<>(); + String addMore = "y"; + + while (addMore.equalsIgnoreCase("y")) { + System.out.println("\n👥 ADD GUEST:"); + System.out.println("1. Select existing guest"); + System.out.println("2. Create new guest"); + + String guestChoice = IO.readln("Choose option (1 or 2): ").trim(); + + if (guestChoice.equals("1")) { + // Välj befintlig gäst + List guests = bookingService.getAllGuests(); + System.out.println("\n📋 Available Guests:"); + guests.forEach(g -> System.out.println(" " + g.getId() + ". " + g.getName() + " (" + g.getContact() + ")")); + + Long guestId = Long.parseLong(IO.readln("\nEnter Guest ID: ")); + guestIds.add(guestId); + + } else if (guestChoice.equals("2")) { + // Skapa ny gäst + System.out.println("\n═══ CREATE NEW GUEST ═══"); + String name = IO.readln("Enter guest name: "); + String contact = IO.readln("Enter contact (phone/email): "); + String note = IO.readln("Enter note (allergies, preferences, etc.): "); + + try { + Long newGuestId = bookingService.createGuest(name, note, contact); + guestIds.add(newGuestId); + System.out.println("Guest created successfully!"); + } catch (Exception e) { + System.out.println("Error creating guest: " + e.getMessage()); + } + } else { + System.out.println("Invalid option! Please enter 1 or 2."); + continue; + } + + addMore = IO.readln("\nAdd another guest? (y/n): ").trim(); + } + + // Skapa bokning med validering + try { + bookingService.createBooking(tableId, timeSlotId, date, partySize, guestIds); + } catch (IllegalArgumentException e) { + System.out.println("Booking failed: " + e.getMessage()); + } + + } catch (NumberFormatException e) { + System.out.println("Invalid input! Please enter valid numbers."); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + } + } + + private static void updateBookingMenu(BookingService bookingService) { + System.out.println("\n═══ UPDATE BOOKING ═══"); + + List bookings = bookingService.getAllBookings(); + + if (bookings.isEmpty()) { + System.out.println("No bookings found to update."); + return; + } + + // visar alla bokningar + showBookings(bookings); + + try { + Long bookingId = Long.parseLong(IO.readln("\nEnter Booking ID to update: ")); + + String statusMenu = """ + + Select new status: + 1. PENDING + 2. CONFIRMED + 3. CANCELLED + 4. COMPLETED + 5. NO_SHOW + """; + + String choice = IO.readln(statusMenu + "\nEnter choice: "); + + BookingStatus newStatus = switch (choice) { + case "1" -> BookingStatus.PENDING; + case "2" -> BookingStatus.CONFIRMED; + case "3" -> BookingStatus.CANCELLED; + case "4" -> BookingStatus.COMPLETED; + case "5" -> BookingStatus.NO_SHOW; + default -> null; + }; + + if (newStatus != null) { + try { + bookingService.updateBookingStatus(bookingId, newStatus); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + } + } else { + System.out.println("Invalid status!"); + } + } catch (NumberFormatException e) { + System.out.println("Invalid ID format!"); + } + } + + + + private static void deleteBookingMenu(BookingService bookingService) { + System.out.println("\n═══ DELETE BOOKING ═══"); + + List bookings = bookingService.getAllBookings(); + + if (bookings.isEmpty()) { + System.out.println("No bookings found to delete."); + return; + } + //Visar alla bokningar - metod längre ner + showBookings(bookings); + + try { + Long bookingId = Long.parseLong(IO.readln("\nEnter Booking ID to delete: ")); + String confirm = IO.readln("Are you sure? (y/n): "); + + if (confirm.equalsIgnoreCase("y")) { + bookingService.deleteBooking(bookingId); + } else { + System.out.println("Deletion cancelled."); + } + } catch (NumberFormatException e) { + System.out.println("Invalid ID format!"); + } + } + + private static void showBookings(BookingService bookingService) { + System.out.println("\n═══ ALL BOOKINGS ═══"); + + List bookings = bookingService.getAllBookings(); + + if (bookings.isEmpty()) { + System.out.println("📭 No bookings found."); + return; + } + + showBookings(bookings); + } + + private static void showBookings(List bookings) { + bookings.forEach(b -> { + System.out.println("\n📅 Booking ID: " + b.getId()); + System.out.println(" Date: " + b.getDate()); + System.out.println(" Time: " + b.getTimeSlot().getStartTime() + " - " + b.getTimeSlot().getFinishTime()); + System.out.println(" Table: " + b.getTable().getTableNumber()); + System.out.println(" Party Size: " + b.getParty()); + System.out.println(" Status: " + b.getStatus()); + System.out.println(" Guests: " + b.getGuests().stream().map(Guest::getName).toList()); + }); + } + + private static void viewTables(BookingService bookingService) { + System.out.println("\n═══ ALL TABLES ═══"); + bookingService.getAllTables().forEach(t -> + System.out.println("Table " + t.getTableNumber() + " - Capacity: " + t.getCapacity()) + ); + } + + private static void viewGuests(BookingService bookingService) { + System.out.println("\n═══ ALL GUESTS ═══"); + bookingService.getAllGuests().forEach(g -> + System.out.println(g.getName() + " - " + g.getContact() + " (" + g.getNote() + ")") + ); } } diff --git a/src/main/java/org/example/entity/Booking.java b/src/main/java/org/example/entity/Booking.java new file mode 100644 index 00000000..84df01e9 --- /dev/null +++ b/src/main/java/org/example/entity/Booking.java @@ -0,0 +1,128 @@ +package org.example.entity; + +import jakarta.persistence.*; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; + +@Entity +@jakarta.persistence.Table(name="Bookings") + +public class Booking { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name="booking_date", nullable = false) + private LocalDate date; //day of the reservation + + @ManyToOne + @JoinColumn(name="timeslot_id", nullable = false) + private TimeSlot timeSlot; //time selected from the determited times + + @Column(name="party_size", nullable = false) + private int partySize; + + @Enumerated(EnumType.STRING) + @Column(name = "status", nullable = false) + private BookingStatus status = BookingStatus.PENDING; + + @ManyToOne + @JoinColumn(name="table_id", nullable = false) + private Table table; + + @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @JoinTable( + name = "booking_guests", + joinColumns = @JoinColumn(name = "booking_id"), + inverseJoinColumns = @JoinColumn (name = "guest_id")) + private List guests = new ArrayList<>(); + + public void addGuest(Guest guest){ + guests.add(guest); + guest.getBookings().add(this); + } + + public void removeGuest(Guest guest){ + guests.remove(guest); + guest.getBookings().remove(this); + } + + public BookingStatus getStatus() { + return status; + } + public void confirmBooking(){ + this.status = BookingStatus.CONFIRMED; + } + public void cancelBooking(){ + this.status = BookingStatus.CANCELLED; + } + public void completeBooking(){ + this.status = BookingStatus.COMPLETED; + } + public void pendingBooking() { this.status = BookingStatus.PENDING; } + public void noShowBooking(){ + this.status = BookingStatus.NO_SHOW; + } + public Booking() {} + + public LocalDate getDate() { + return date; + } + + public void setDate(LocalDate date) { + this.date = date; + } + + public TimeSlot getTimeSlot() { + return timeSlot; + } + + public void setTimeSlot(TimeSlot timeSlot) { + this.timeSlot = timeSlot; + } + + public int getParty() { + return partySize; + } + + public void setParty(int partySize) { + this.partySize = partySize; + } + + public Table getTable() { + return table; + } + + public void setTable(Table table) { + this.table = table; + } + + public List getGuests() { + return guests; + } + + public void setGuests(List guests) { + this.guests = guests; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + @Override + public String toString() { + return "Booking{" + + "id=" + id + + ", date=" + date + + ", party=" + partySize + + ", status=" + status + + '}'; + } +} diff --git a/src/main/java/org/example/entity/BookingStatus.java b/src/main/java/org/example/entity/BookingStatus.java new file mode 100644 index 00000000..8bf2935c --- /dev/null +++ b/src/main/java/org/example/entity/BookingStatus.java @@ -0,0 +1,10 @@ +package org.example.entity; + +public enum BookingStatus { + PENDING, + CONFIRMED, + CANCELLED, + COMPLETED, + NO_SHOW +} +// Status system diff --git a/src/main/java/org/example/entity/Guest.java b/src/main/java/org/example/entity/Guest.java new file mode 100644 index 00000000..804b5c61 --- /dev/null +++ b/src/main/java/org/example/entity/Guest.java @@ -0,0 +1,78 @@ +package org.example.entity; + +import jakarta.persistence.*; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Entity +@jakarta.persistence.Table(name = "Guests") + +public class Guest { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name="Name", nullable = false) + private String name; + + @Column(name="Note", nullable = false) + private String note; + + @Column(name="Contact_info", nullable = false) + private String contact; + + @ManyToMany(mappedBy = "guests") + private List bookings = new ArrayList<>(); + + public Guest(String name, String note, String contact){ + this.name = name; + this.note = note; + this.contact = contact; + } + + public Guest(){} + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + + public String getNote() { + return note; + } + + public void setNote(String note) { + this.note = note; + } + + public String getContact() { + return contact; + } + + public void setContact(String contact) { + this.contact = contact; + } + + public List getBookings() { + return bookings; + } + + public void setBookings(List bookings) { + this.bookings = bookings; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + +} diff --git a/src/main/java/org/example/entity/Table.java b/src/main/java/org/example/entity/Table.java new file mode 100644 index 00000000..8fe91186 --- /dev/null +++ b/src/main/java/org/example/entity/Table.java @@ -0,0 +1,69 @@ +package org.example.entity; + +import jakarta.persistence.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + + +@Entity +@jakarta.persistence.Table(name = "Tables") + +public class Table { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToMany(mappedBy = "table", cascade = CascadeType.PERSIST) + private List bookings = new ArrayList<>(); + + + @Column(name="table_number", nullable = false, unique = true) + private String tableNumber; + + @Column(name="capacity", nullable = false) + private int capacity; + + + public Table(int capacity, String tableNumber){ + this.capacity = capacity; + this.tableNumber = tableNumber; + + } + + public List getBookings() { + return bookings; + } + + public void setBookings(List bookings) { + this.bookings = bookings; + } + + public String getTableNumber() { + return tableNumber; + } + + public void setTableNumber(String tableNumber) { + this.tableNumber = tableNumber; + } + + public int getCapacity() { + return capacity; + } + + public void setCapacity(int capacity) { + this.capacity = capacity; + } + + public Table() {} + + public void setId(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + +} diff --git a/src/main/java/org/example/entity/TimeSlot.java b/src/main/java/org/example/entity/TimeSlot.java new file mode 100644 index 00000000..c9f96cd7 --- /dev/null +++ b/src/main/java/org/example/entity/TimeSlot.java @@ -0,0 +1,59 @@ +package org.example.entity; + +import jakarta.persistence.*; + +@Entity +public class TimeSlot { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, unique = true) + private String startTime; + + @Column(nullable = false, unique = true) + private String finishTime; + + public TimeSlot(String startTime, String finishTime) { + this.startTime = startTime; + this.finishTime = finishTime; + } + + public TimeSlot() { + } + + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getFinishTime() { + return finishTime; + } + + public void setFinishTime(String finishTime) { + this.finishTime = finishTime; + } + + @Override + public String toString() { + return "TimeSlot{" + + "id=" + id + + ", startTime='" + startTime + '\'' + + ", finishTime='" + finishTime + '\'' + + '}'; + } +} diff --git a/src/main/java/org/example/entity/service/BookingService.java b/src/main/java/org/example/entity/service/BookingService.java new file mode 100644 index 00000000..7468b542 --- /dev/null +++ b/src/main/java/org/example/entity/service/BookingService.java @@ -0,0 +1,185 @@ +package org.example.entity.service; + +import jakarta.persistence.EntityManagerFactory; +import org.example.entity.*; + +import java.time.LocalDate; +import java.util.List; + +public class BookingService { + + private final EntityManagerFactory emf; + + public BookingService(EntityManagerFactory emf) { + this.emf = emf; + } + + // Skapa gäst + public Long createGuest(String name, String note, String contact) { + return emf.callInTransaction(em -> { + Guest guest = new Guest(name, note, contact); + em.persist(guest); + em.flush(); + return guest.getId(); + }); + } + + // Skapa bokning MED validering + public void createBooking(Long tableId, Long timeSlotId, LocalDate date, int partySize, List guestIds) { + emf.runInTransaction(em -> { + // 1. Hämta bord + Table table = em.find(Table.class, tableId); + if (table == null) { + throw new IllegalArgumentException("Table not found!"); + } + + // 2. Hämta tidslucka + TimeSlot timeSlot = em.find(TimeSlot.class, timeSlotId); + if (timeSlot == null) { + throw new IllegalArgumentException("TimeSlot not found!"); + } + + // 3. VALIDERA KAPACITET + if (partySize > table.getCapacity()) { + throw new IllegalArgumentException( + "Party size (" + partySize + ") exceeds table capacity (" + table.getCapacity() + ")!" + ); + } + + if (partySize < 1) { + throw new IllegalArgumentException("Party size must be at least 1!"); + } + + // 4. VALIDERA DATUM + LocalDate today = LocalDate.now(); + LocalDate maxDate = today.plusMonths(3); + + if (date.isBefore(today)) { + throw new IllegalArgumentException("Cannot book a date in the past!"); + } + + if (date.isAfter(maxDate)) { + throw new IllegalArgumentException("Cannot book more than 3 months in advance!"); + } + + // 5. VALIDERA ATT BORDET INTE ÄR BOKAT FÖR SAMMA TID/DATUM + Long existingBookings = em.createQuery( + "SELECT COUNT(b) FROM Booking b " + + "WHERE b.table.id = :tableId " + + "AND b.date = :date " + + "AND b.timeSlot.id = :timeSlotId " + + "AND b.status != :cancelledStatus", + Long.class + ) + .setParameter("tableId", tableId) + .setParameter("date", date) + .setParameter("timeSlotId", timeSlotId) + .setParameter("cancelledStatus", BookingStatus.CANCELLED) + .getSingleResult(); + + if (existingBookings > 0) { + throw new IllegalArgumentException( + "Table " + table.getTableNumber() + + " is already booked for " + date + + " at " + timeSlot.getStartTime() + "!" + ); + } + + // 6. Validera att minst en gäst finns + if (guestIds == null || guestIds.isEmpty()) { + throw new IllegalArgumentException("Booking must have at least one guest!"); + } + + // 7. Skapa bokning + Booking booking = new Booking(); + booking.setDate(date); + booking.setTimeSlot(timeSlot); + booking.setParty(partySize); + booking.setTable(table); + + // 8. Lägg till gäster + for (Long guestId : guestIds) { + Guest guest = em.find(Guest.class, guestId); + if (guest != null) { + booking.addGuest(guest); + } else { + throw new IllegalArgumentException("Guest with ID " + guestId + " not found!"); + } + } + + em.persist(booking); + System.out.println("Booking created successfully!"); + }); + } + + public List
getAllTables() { + return emf.callInTransaction(em -> + em.createQuery("SELECT t FROM Table t", Table.class).getResultList() + ); + } + + public List getAllTimeSlots() { + return emf.callInTransaction(em -> + em.createQuery("SELECT ts FROM TimeSlot ts", TimeSlot.class).getResultList() + ); + } + + public List getAllGuests() { + return emf.callInTransaction(em -> + em.createQuery("SELECT g FROM Guest g", Guest.class).getResultList() + ); + } + + public List getAllBookings() { + return emf.callInTransaction(em -> + em.createQuery( + "SELECT DISTINCT b FROM Booking b " + + "LEFT JOIN FETCH b.guests " + + "LEFT JOIN FETCH b.table " + + "LEFT JOIN FETCH b.timeSlot", + Booking.class + ).getResultList() + ); + } + + public void updateBookingStatus(Long bookingId, BookingStatus newStatus) { + emf.runInTransaction(em -> { + Booking booking = em.find(Booking.class, bookingId); + if (booking == null) { + System.out.println("Booking with ID " + bookingId + " not found!"); + return; + } + + switch (newStatus) { + case CONFIRMED -> booking.confirmBooking(); + case CANCELLED -> booking.cancelBooking(); + case COMPLETED -> booking.completeBooking(); + case PENDING -> booking.pendingBooking(); + case NO_SHOW -> booking.noShowBooking(); + } + + System.out.println("Booking status updated to: " + newStatus); + }); + } + + public void deleteBooking(Long bookingId) { + emf.runInTransaction(em -> { + try { + Booking booking = em.createQuery( + "SELECT b FROM Booking b " + + "LEFT JOIN FETCH b.guests " + + "WHERE b.id = :id", + Booking.class + ) + .setParameter("id", bookingId) + .getSingleResult(); + + em.remove(booking); + System.out.println("Booking deleted successfully!"); + + } catch (jakarta.persistence.NoResultException e) { + System.out.println("Booking with ID " + bookingId + " not found!"); + } + }); + } +} diff --git a/src/main/resources/META-INF/persistence.xml b/src/main/resources/META-INF/persistence.xml new file mode 100644 index 00000000..df99ab67 --- /dev/null +++ b/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,29 @@ + + + + org.example.entity.Table + org.example.entity.Booking + org.example.entity.Guest + org.example.entity.TimeSlot + + + + + + + + + + + + + + + + + +