diff --git a/.gitignore b/.gitignore
index 91a800a..610fce9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,6 +39,9 @@ nb-configuration.xml
# Local environment
.env
+# Database
+.local-db
+
# Plugin directory
/.quarkus/cli/plugins/
# TLS Certificates
diff --git a/pom.xml b/pom.xml
index 8a3917b..bea9076 100644
--- a/pom.xml
+++ b/pom.xml
@@ -55,6 +55,15 @@
io.quarkus
quarkus-hibernate-validator
+
+ io.quarkus
+ quarkus-jdbc-h2
+ runtime
+
+
+ io.quarkus
+ quarkus-hibernate-orm-panache
+
io.quarkus
quarkus-junit-mockito
diff --git a/src/main/java/de/schulung/quarkus/recipes/Ingredient.java b/src/main/java/de/schulung/quarkus/recipes/Ingredient.java
index 27c6189..4ddc77e 100644
--- a/src/main/java/de/schulung/quarkus/recipes/Ingredient.java
+++ b/src/main/java/de/schulung/quarkus/recipes/Ingredient.java
@@ -1,16 +1,26 @@
package de.schulung.quarkus.recipes;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.Size;
-
import lombok.Getter;
import lombok.Setter;
+@Entity(name = "Ingredient")
+@Table(name = "ingredients")
@Getter
@Setter
public class Ingredient {
+ /**
+ * The ingredient ID (internal, not exposed via JSON).
+ */
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @JsonIgnore
+ private Long id;
/**
* The ingredient name.
*/
@@ -26,6 +36,7 @@ public class Ingredient {
* The unit of the ingredient.
*/
@NotNull
+ @Enumerated(EnumType.STRING)
private IngredientUnit unit;
}
diff --git a/src/main/java/de/schulung/quarkus/recipes/Recipe.java b/src/main/java/de/schulung/quarkus/recipes/Recipe.java
index e016622..0fe5c74 100644
--- a/src/main/java/de/schulung/quarkus/recipes/Recipe.java
+++ b/src/main/java/de/schulung/quarkus/recipes/Recipe.java
@@ -1,6 +1,7 @@
package de.schulung.quarkus.recipes;
import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.persistence.*;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
@@ -11,6 +12,8 @@
import java.time.OffsetDateTime;
import java.util.List;
+@Entity(name = "Recipe")
+@Table(name = "recipes")
@Getter
@Setter
public class Recipe {
@@ -18,6 +21,8 @@ public class Recipe {
/**
* The recipe ID (read-only, assigned by the server).
*/
+ @Id
+ @GeneratedValue(strategy = GenerationType.UUID)
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private String id;
/**
@@ -49,18 +54,22 @@ public class Recipe {
/**
* Difficulty level of the recipe.
*/
+ @Enumerated(EnumType.STRING)
private Difficulty difficulty;
/**
* List of ingredients.
*/
@NotNull
@Size(max = 100)
+ @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
+ @JoinColumn(name = "recipe_id")
private List<@Valid @NotNull Ingredient> ingredients;
/**
* Preparation instructions.
*/
@NotNull
@Size(max = 2000)
+ @Column(length = 2000)
private String preparation;
}
diff --git a/src/main/java/de/schulung/quarkus/recipes/RecipesRepository.java b/src/main/java/de/schulung/quarkus/recipes/RecipesRepository.java
new file mode 100644
index 0000000..09a084a
--- /dev/null
+++ b/src/main/java/de/schulung/quarkus/recipes/RecipesRepository.java
@@ -0,0 +1,9 @@
+package de.schulung.quarkus.recipes;
+
+import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
+import jakarta.enterprise.context.ApplicationScoped;
+
+@ApplicationScoped
+public class RecipesRepository
+ implements PanacheRepositoryBase {
+}
diff --git a/src/main/java/de/schulung/quarkus/recipes/RecipesService.java b/src/main/java/de/schulung/quarkus/recipes/RecipesService.java
index fb2a3a6..9d34ae5 100644
--- a/src/main/java/de/schulung/quarkus/recipes/RecipesService.java
+++ b/src/main/java/de/schulung/quarkus/recipes/RecipesService.java
@@ -1,38 +1,36 @@
package de.schulung.quarkus.recipes;
import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.transaction.Transactional;
import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
import java.time.OffsetDateTime;
-import java.util.Map;
import java.util.Optional;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
@ApplicationScoped
+@RequiredArgsConstructor
public class RecipesService {
- private final Map recipes = new ConcurrentHashMap<>();
+ private final RecipesRepository repository;
public Stream findAll() {
- return recipes
- .values()
+ return repository
+ .listAll()
.stream();
}
public Optional findById(String id) {
- return Optional
- .ofNullable(recipes.get(id));
+ return repository
+ .findByIdOptional(id);
}
+ @Transactional
public void create(
@Valid
Recipe recipe
) {
-
- final var id = UUID.randomUUID().toString();
- recipe.setId(id);
recipe.setLastEdited(OffsetDateTime.now());
if (null == recipe.getImg()) {
@@ -43,6 +41,6 @@ public void create(
recipe.setDifficulty(Difficulty.medium);
}
- recipes.put(id, recipe);
+ repository.persist(recipe);
}
}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index e701ad4..3677084 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1 +1,4 @@
quarkus.jackson.fail-on-unknown-properties=true
+quarkus.datasource.jdbc.url=jdbc:h2:./.local-db/customers
+quarkus.hibernate-orm.schema-management.strategy=update
+quarkus.hibernate-orm.log.sql=true
diff --git a/src/test/java/de/schulung/quarkus/recipes/RecipesResourceTests.java b/src/test/java/de/schulung/quarkus/recipes/RecipesResourceTests.java
index b75a3f6..0ca83d0 100644
--- a/src/test/java/de/schulung/quarkus/recipes/RecipesResourceTests.java
+++ b/src/test/java/de/schulung/quarkus/recipes/RecipesResourceTests.java
@@ -1,5 +1,6 @@
package de.schulung.quarkus.recipes;
+import io.quarkus.test.TestTransaction;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.http.ContentType;
import org.junit.jupiter.api.Test;
@@ -15,6 +16,7 @@
import static org.hamcrest.Matchers.*;
@QuarkusTest
+@TestTransaction
class RecipesResourceTests {
// --- Content Negotiation ---
diff --git a/src/test/java/de/schulung/quarkus/recipes/RecipesServiceTests.java b/src/test/java/de/schulung/quarkus/recipes/RecipesServiceTests.java
index f897add..ba92625 100644
--- a/src/test/java/de/schulung/quarkus/recipes/RecipesServiceTests.java
+++ b/src/test/java/de/schulung/quarkus/recipes/RecipesServiceTests.java
@@ -1,5 +1,6 @@
package de.schulung.quarkus.recipes;
+import io.quarkus.test.TestTransaction;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.Test;
@@ -7,6 +8,7 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
@QuarkusTest
+@TestTransaction
public class RecipesServiceTests {
@Inject