From 41721bfffc38fb06be8b24092f6076ae658ca01e Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 09:56:19 +0000 Subject: [PATCH 01/13] add deadline --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5150e50f..05def9a9 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. From 4ba2a40b68f650746e636c58bbf6638b37fe504b Mon Sep 17 00:00:00 2001 From: WHITEROSE Date: Mon, 15 Dec 2025 13:54:12 +0100 Subject: [PATCH 02/13] Setup docker compose for mysql Co-authored-by: Kristina Martinovic Co-authored-by: Vilma Ollas Co-authored-by: Annika Holmqvist --- docker-compose.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..717cb0a1 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +services: + mysql: + image: mysql:9.5.0 + container_name: jpa-mysql-db + environment: + MYSQL_PASSWORD: root + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: invoice-db + + ports: + - "3306:3306" + + volumes: + - mysql-data:/var/lib/mysql + +volumes: + mysql-data: From d316237cff536bf92c6d5fcad2b8e981f0a3c760 Mon Sep 17 00:00:00 2001 From: WHITEROSE Date: Mon, 15 Dec 2025 14:49:11 +0100 Subject: [PATCH 03/13] Setup hibernate persistance Co-authored-by: Kristina Martinovic Co-authored-by: Vilma Ollas Co-authored-by: Annika Holmqvist --- JavaJPA.iml | 13 +++++++++++ pom.xml | 12 +++++++++++ src/main/java/org/example/util/JpaUtil.java | 24 +++++++++++++++++++++ src/main/resources/META-INF/persistance.xml | 20 +++++++++++++++++ 4 files changed, 69 insertions(+) create mode 100644 JavaJPA.iml create mode 100644 src/main/java/org/example/util/JpaUtil.java create mode 100644 src/main/resources/META-INF/persistance.xml diff --git a/JavaJPA.iml b/JavaJPA.iml new file mode 100644 index 00000000..a265a9b5 --- /dev/null +++ b/JavaJPA.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 909503d0..bb348498 100644 --- a/pom.xml +++ b/pom.xml @@ -51,4 +51,16 @@ 4.8.184 + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 25 + + + + diff --git a/src/main/java/org/example/util/JpaUtil.java b/src/main/java/org/example/util/JpaUtil.java new file mode 100644 index 00000000..a66b760e --- /dev/null +++ b/src/main/java/org/example/util/JpaUtil.java @@ -0,0 +1,24 @@ +package org.example.util; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.Persistence; + +public class JpaUtil { + + private static EntityManagerFactory emf; + + static { emf = Persistence.createEntityManagerFactory("jpa-hibernate-mysql"); + Runtime.getRuntime().addShutdownHook(new Thread(() -> + { + if (emf.isOpen()) { emf.close(); } })); + } + public static EntityManager getEntityManager() { + emf = Persistence.createEntityManagerFactory("org.example"); + return emf.createEntityManager(); + } + + public static EntityManagerFactory getEntityManagerFactory() { + return emf; + } +} diff --git a/src/main/resources/META-INF/persistance.xml b/src/main/resources/META-INF/persistance.xml new file mode 100644 index 00000000..7398a685 --- /dev/null +++ b/src/main/resources/META-INF/persistance.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + From b10491ae51952e6ce6a26b2654d71734fd1f6c35 Mon Sep 17 00:00:00 2001 From: WHITEROSE Date: Mon, 15 Dec 2025 15:21:21 +0100 Subject: [PATCH 04/13] create a base workflow for database transactions Co-authored-by: Kristina Martinovic Co-authored-by: Vilma Ollas Co-authored-by: Annika Holmqvist --- JavaJPA.iml | 10 ++++-- src/main/java/org/example/App.java | 14 +++++++- src/main/java/org/example/entity/User.java | 14 ++++++++ .../example/repository/UserRepository.java | 34 +++++++++++++++++++ .../java/org/example/service/UserService.java | 18 ++++++++++ src/main/java/org/example/util/JpaUtil.java | 3 +- .../{persistance.xml => persistence.xml} | 3 +- 7 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 src/main/java/org/example/entity/User.java create mode 100644 src/main/java/org/example/repository/UserRepository.java create mode 100644 src/main/java/org/example/service/UserService.java rename src/main/resources/META-INF/{persistance.xml => persistence.xml} (92%) diff --git a/JavaJPA.iml b/JavaJPA.iml index a265a9b5..9959be17 100644 --- a/JavaJPA.iml +++ b/JavaJPA.iml @@ -4,9 +4,13 @@ - - - + + + + + + + diff --git a/src/main/java/org/example/App.java b/src/main/java/org/example/App.java index 165e5cd5..41f64d1b 100644 --- a/src/main/java/org/example/App.java +++ b/src/main/java/org/example/App.java @@ -1,7 +1,19 @@ package org.example; +import jakarta.persistence.EntityManager; +import org.example.entity.User; +import org.example.repository.UserRepository; +import org.example.service.UserService; +import org.example.util.JpaUtil; + public class App { public static void main(String[] args) { - System.out.println("Hello There!"); + try (EntityManager em = JpaUtil.getEntityManager()) { + UserRepository userRepository = new UserRepository(em); + UserService userService = new UserService(userRepository); + + User user = new User(); + userService.createUser(user); + } } } diff --git a/src/main/java/org/example/entity/User.java b/src/main/java/org/example/entity/User.java new file mode 100644 index 00000000..5d9e08ee --- /dev/null +++ b/src/main/java/org/example/entity/User.java @@ -0,0 +1,14 @@ +package org.example.entity; + +import jakarta.persistence.*; + +import java.util.UUID; + +@Entity +@Table(name = "users") +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.UUID) + private UUID id; +} diff --git a/src/main/java/org/example/repository/UserRepository.java b/src/main/java/org/example/repository/UserRepository.java new file mode 100644 index 00000000..d09cd6b7 --- /dev/null +++ b/src/main/java/org/example/repository/UserRepository.java @@ -0,0 +1,34 @@ +package org.example.repository; + +import jakarta.persistence.EntityManager; +import org.example.entity.User; + +public class UserRepository { + private final EntityManager em; + + public UserRepository(EntityManager em) { + this.em = em; + } + + public void save(User user) { + try { + em.getTransaction().begin(); + em.persist(user); + em.getTransaction().commit(); + } + finally { + em.close(); + } + } + + public void delete(User user) { + try { + em.getTransaction().begin(); + em.remove(user); + } + finally { + em.close(); + } + } + +} diff --git a/src/main/java/org/example/service/UserService.java b/src/main/java/org/example/service/UserService.java new file mode 100644 index 00000000..906010de --- /dev/null +++ b/src/main/java/org/example/service/UserService.java @@ -0,0 +1,18 @@ +package org.example.service; + +import org.example.entity.User; +import org.example.repository.UserRepository; + +public class UserService { + + private final UserRepository userRepository; + + public UserService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + public void createUser(User user) { + userRepository.save(user); + } + +} diff --git a/src/main/java/org/example/util/JpaUtil.java b/src/main/java/org/example/util/JpaUtil.java index a66b760e..44a3a01c 100644 --- a/src/main/java/org/example/util/JpaUtil.java +++ b/src/main/java/org/example/util/JpaUtil.java @@ -6,7 +6,7 @@ public class JpaUtil { - private static EntityManagerFactory emf; + private static final EntityManagerFactory emf; static { emf = Persistence.createEntityManagerFactory("jpa-hibernate-mysql"); Runtime.getRuntime().addShutdownHook(new Thread(() -> @@ -14,7 +14,6 @@ public class JpaUtil { if (emf.isOpen()) { emf.close(); } })); } public static EntityManager getEntityManager() { - emf = Persistence.createEntityManagerFactory("org.example"); return emf.createEntityManager(); } diff --git a/src/main/resources/META-INF/persistance.xml b/src/main/resources/META-INF/persistence.xml similarity index 92% rename from src/main/resources/META-INF/persistance.xml rename to src/main/resources/META-INF/persistence.xml index 7398a685..ae075dd9 100644 --- a/src/main/resources/META-INF/persistance.xml +++ b/src/main/resources/META-INF/persistence.xml @@ -4,13 +4,14 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_2.xsd"> + org.example.entity.User - + From 36ccaa07e99aa80feca720f77be89ce250b52cb0 Mon Sep 17 00:00:00 2001 From: WHITEROSE Date: Mon, 15 Dec 2025 15:59:37 +0100 Subject: [PATCH 05/13] create repository methods for testing Co-authored-by: Kristina Martinovic Co-authored-by: Vilma Ollas Co-authored-by: Annika Holmqvist --- src/main/java/org/example/App.java | 11 ++++++- src/main/java/org/example/entity/User.java | 16 +++++++++ .../example/repository/UserRepository.java | 33 ++++++++++--------- .../java/org/example/service/UserService.java | 15 +++++++++ 4 files changed, 58 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/example/App.java b/src/main/java/org/example/App.java index 41f64d1b..dce37aca 100644 --- a/src/main/java/org/example/App.java +++ b/src/main/java/org/example/App.java @@ -9,11 +9,20 @@ public class App { public static void main(String[] args) { try (EntityManager em = JpaUtil.getEntityManager()) { + UserRepository userRepository = new UserRepository(em); UserService userService = new UserService(userRepository); - User user = new User(); + User user = new User("testUser"); + + //Operation 1 userService.createUser(user); + + User saved = userService.getUserByUsername("testUser"); + System.out.println(saved); + + //Operation 2 + userService.deleteUserById(saved.getId()); } } } diff --git a/src/main/java/org/example/entity/User.java b/src/main/java/org/example/entity/User.java index 5d9e08ee..dbb81008 100644 --- a/src/main/java/org/example/entity/User.java +++ b/src/main/java/org/example/entity/User.java @@ -11,4 +11,20 @@ public class User { @Id @GeneratedValue(strategy = GenerationType.UUID) private UUID id; + + @Column + private String username; + + + public User(String username){ + this.username = username; + } + + public User() { + + } + + public UUID getId() { + return id; + } } diff --git a/src/main/java/org/example/repository/UserRepository.java b/src/main/java/org/example/repository/UserRepository.java index d09cd6b7..3e347362 100644 --- a/src/main/java/org/example/repository/UserRepository.java +++ b/src/main/java/org/example/repository/UserRepository.java @@ -3,6 +3,8 @@ import jakarta.persistence.EntityManager; import org.example.entity.User; +import java.util.UUID; + public class UserRepository { private final EntityManager em; @@ -10,25 +12,24 @@ public UserRepository(EntityManager em) { this.em = em; } + public User getUserById(UUID id) { + return em.find(User.class, id); + } + + public User getUserByUsername(String username) { + return em.find(User.class, username); + } + + public void save(User user) { - try { - em.getTransaction().begin(); - em.persist(user); - em.getTransaction().commit(); - } - finally { - em.close(); - } + em.getTransaction().begin(); + em.persist(user); + em.getTransaction().commit(); } public void delete(User user) { - try { - em.getTransaction().begin(); - em.remove(user); - } - finally { - em.close(); - } + em.getTransaction().begin(); + em.remove(user); + em.getTransaction().commit(); } - } diff --git a/src/main/java/org/example/service/UserService.java b/src/main/java/org/example/service/UserService.java index 906010de..e11c58db 100644 --- a/src/main/java/org/example/service/UserService.java +++ b/src/main/java/org/example/service/UserService.java @@ -3,6 +3,8 @@ import org.example.entity.User; import org.example.repository.UserRepository; +import java.util.UUID; + public class UserService { private final UserRepository userRepository; @@ -11,6 +13,19 @@ public UserService(UserRepository userRepository) { this.userRepository = userRepository; } + public void deleteUserById(UUID id) { + User user = userRepository.getUserById(id); + userRepository.delete(user); + } + + public User getUserById(UUID id) { + return userRepository.getUserById(id); + } + + public User getUserByUsername(String username) { + return userRepository.getUserByUsername(username); + } + public void createUser(User user) { userRepository.save(user); } From 656239bc7f32a853402ccc8f10dd4385eddecbd1 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Tue, 16 Dec 2025 00:57:17 +0100 Subject: [PATCH 06/13] Create BaseRepository class --- src/main/java/org/example/repository/BaseRepository.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/java/org/example/repository/BaseRepository.java diff --git a/src/main/java/org/example/repository/BaseRepository.java b/src/main/java/org/example/repository/BaseRepository.java new file mode 100644 index 00000000..c0669a70 --- /dev/null +++ b/src/main/java/org/example/repository/BaseRepository.java @@ -0,0 +1,4 @@ +package org.example.repository; + +public class BaseRepository { +} From ec3261221844a80abbb11b4e8e47196ac12ca285 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Tue, 16 Dec 2025 01:00:29 +0100 Subject: [PATCH 07/13] Create abstract class BaseRepository with constructor and generics --- .../java/org/example/repository/BaseRepository.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/example/repository/BaseRepository.java b/src/main/java/org/example/repository/BaseRepository.java index c0669a70..885a6884 100644 --- a/src/main/java/org/example/repository/BaseRepository.java +++ b/src/main/java/org/example/repository/BaseRepository.java @@ -1,4 +1,14 @@ package org.example.repository; -public class BaseRepository { +import jakarta.persistence.EntityManagerFactory; + +public abstract class BaseRepository { + + private final EntityManagerFactory emf; + protected final Class entityClass; + + protected BaseRepository(EntityManagerFactory emf, Class entityClass) { + this.emf = emf; + this.entityClass = entityClass; + } } From f5e3512ac4a42a5e9c8f6de28dc158999b415363 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Tue, 16 Dec 2025 01:01:43 +0100 Subject: [PATCH 08/13] Add RunInTransaction method to handle transactional operations --- .../example/repository/BaseRepository.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/java/org/example/repository/BaseRepository.java b/src/main/java/org/example/repository/BaseRepository.java index 885a6884..e59e9313 100644 --- a/src/main/java/org/example/repository/BaseRepository.java +++ b/src/main/java/org/example/repository/BaseRepository.java @@ -1,7 +1,10 @@ package org.example.repository; +import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; +import java.util.function.Function; + public abstract class BaseRepository { private final EntityManagerFactory emf; @@ -11,4 +14,20 @@ protected BaseRepository(EntityManagerFactory emf, Class entityClass) { this.emf = emf; this.entityClass = entityClass; } + + protected R runInTransaction(Function action) { + EntityManager em = emf.createEntityManager(); + try { + em.getTransaction().begin(); + R result = action.apply(em); + em.getTransaction().commit(); + return result; + } catch (Exception e) { + if (em.getTransaction().isActive()) em.getTransaction().rollback(); + throw new RuntimeException("Transaction failed for " + entityClass.getSimpleName(), e); + } finally { + em.close(); + } + } + } From 3949abc26c29004a968231bda364139e0d672499 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Tue, 16 Dec 2025 01:02:43 +0100 Subject: [PATCH 09/13] Add executeRead method for read-only queries without transaction --- .../java/org/example/repository/BaseRepository.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/org/example/repository/BaseRepository.java b/src/main/java/org/example/repository/BaseRepository.java index e59e9313..0a165b97 100644 --- a/src/main/java/org/example/repository/BaseRepository.java +++ b/src/main/java/org/example/repository/BaseRepository.java @@ -30,4 +30,14 @@ protected R runInTransaction(Function action) { } } + protected R executeRead(Function action) { + EntityManager em = emf.createEntityManager(); + try { + return action.apply(em); + } finally { + em.close(); + } + } + + } From 452b39f1101d4e135de43507fac148f038832480 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Tue, 16 Dec 2025 01:03:27 +0100 Subject: [PATCH 10/13] Add save method with merge for managed and persist for new entities --- .../java/org/example/repository/BaseRepository.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/org/example/repository/BaseRepository.java b/src/main/java/org/example/repository/BaseRepository.java index 0a165b97..543c3089 100644 --- a/src/main/java/org/example/repository/BaseRepository.java +++ b/src/main/java/org/example/repository/BaseRepository.java @@ -39,5 +39,17 @@ protected R executeRead(Function action) { } } + public void save(T entity) { + runInTransaction(em -> { + if (em.contains(entity)) { + em.merge(entity); + } else { + em.persist(entity); + } + return null; + }); + } + + } From 70e0f70773ccfae459468103d96635cddfecf5c5 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Tue, 16 Dec 2025 01:04:02 +0100 Subject: [PATCH 11/13] Add delete method handling both managed and detached entities --- src/main/java/org/example/repository/BaseRepository.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/org/example/repository/BaseRepository.java b/src/main/java/org/example/repository/BaseRepository.java index 543c3089..af025113 100644 --- a/src/main/java/org/example/repository/BaseRepository.java +++ b/src/main/java/org/example/repository/BaseRepository.java @@ -50,6 +50,14 @@ public void save(T entity) { }); } + public void delete(T entity) { + runInTransaction(em -> { + em.remove(em.contains(entity) ? entity : em.merge(entity)); + return null; + }); + } + + } From 20ed6f84d6581221cbf36b415eafb2e17d6df6a6 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Tue, 16 Dec 2025 01:04:31 +0100 Subject: [PATCH 12/13] Add findById method returning Optional --- src/main/java/org/example/repository/BaseRepository.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/org/example/repository/BaseRepository.java b/src/main/java/org/example/repository/BaseRepository.java index af025113..88f3ecc9 100644 --- a/src/main/java/org/example/repository/BaseRepository.java +++ b/src/main/java/org/example/repository/BaseRepository.java @@ -3,6 +3,7 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; +import java.util.Optional; import java.util.function.Function; public abstract class BaseRepository { @@ -57,6 +58,10 @@ public void delete(T entity) { }); } + public Optional findById(ID id) { + return executeRead(em -> Optional.ofNullable(em.find(entityClass, id))); + } + From 4d1286a136ff188a8142672c5b1e0a3fdab14e85 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Tue, 16 Dec 2025 01:05:05 +0100 Subject: [PATCH 13/13] Add findAll method to fetch all entities of type T --- src/main/java/org/example/repository/BaseRepository.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/org/example/repository/BaseRepository.java b/src/main/java/org/example/repository/BaseRepository.java index 88f3ecc9..0a81dd22 100644 --- a/src/main/java/org/example/repository/BaseRepository.java +++ b/src/main/java/org/example/repository/BaseRepository.java @@ -3,6 +3,7 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; +import java.util.List; import java.util.Optional; import java.util.function.Function; @@ -62,6 +63,14 @@ public Optional findById(ID id) { return executeRead(em -> Optional.ofNullable(em.find(entityClass, id))); } + public List findAll() { + return executeRead(em -> + em.createQuery("SELECT e FROM " + entityClass.getSimpleName() + " e", entityClass) + .getResultList() + ); + } + +