refactor: revised class layout for JPA entities
This commit is contained in:
parent
9958ae5fbe
commit
88a85f2e04
6 changed files with 184 additions and 97 deletions
53
commons/src/main/java/commons/FormalIngredient.java
Normal file
53
commons/src/main/java/commons/FormalIngredient.java
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
package commons;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A formal ingredient inheriting from base {@link RecipeIngredient RecipeIngredient},
|
||||
* holds an amount and a unit suffix in {@link String String} form.
|
||||
* @see RecipeIngredient
|
||||
*/
|
||||
@Entity
|
||||
public class FormalIngredient extends RecipeIngredient {
|
||||
private double amount;
|
||||
private String unitSuffix;
|
||||
|
||||
public double getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(double amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public String getUnitSuffix() {
|
||||
return unitSuffix;
|
||||
}
|
||||
|
||||
public void setUnitSuffix(String unitSuffix) {
|
||||
this.unitSuffix = unitSuffix;
|
||||
}
|
||||
|
||||
public FormalIngredient(Ingredient ingredient, double amount, String unitSuffix) {
|
||||
super(ingredient);
|
||||
this.amount = amount;
|
||||
this.unitSuffix = unitSuffix;
|
||||
}
|
||||
|
||||
public FormalIngredient() {
|
||||
// ORM
|
||||
}
|
||||
|
||||
public double amountInBaseUnit() {
|
||||
Optional<Unit> unit = Unit.fromString(unitSuffix);
|
||||
if (unit.isEmpty() || !unit.get().isFormal() || unit.get().conversionFactor <= 0) {
|
||||
return 0.0;
|
||||
}
|
||||
return amount * unit.get().conversionFactor;
|
||||
}
|
||||
public String toString() {
|
||||
return amount + unitSuffix + " of " + ingredient.name;
|
||||
}
|
||||
}
|
||||
|
|
@ -20,7 +20,8 @@ public class Ingredient {
|
|||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
public long id;
|
||||
|
||||
@Column(name = "name", nullable = false, unique = true)
|
||||
// FIXME Dec 22 2025::temporarily made this not a unique constraint because of weird JPA behaviour
|
||||
@Column(name = "name", nullable = false, unique = false)
|
||||
public String name;
|
||||
|
||||
|
||||
|
|
@ -35,10 +36,24 @@ public class Ingredient {
|
|||
|
||||
public Ingredient() {}
|
||||
|
||||
public Ingredient(String name,
|
||||
public Ingredient(
|
||||
Long id,
|
||||
String name,
|
||||
double proteinPer100g,
|
||||
double fatPer100g,
|
||||
double carbsPer100g) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.proteinPer100g = proteinPer100g;
|
||||
this.fatPer100g = fatPer100g;
|
||||
this.carbsPer100g = carbsPer100g;
|
||||
}
|
||||
public Ingredient(
|
||||
String name,
|
||||
double proteinPer100g,
|
||||
double fatPer100g,
|
||||
double carbsPer100g) {
|
||||
|
||||
this.name = name;
|
||||
this.proteinPer100g = proteinPer100g;
|
||||
this.fatPer100g = fatPer100g;
|
||||
|
|
|
|||
|
|
@ -15,16 +15,17 @@
|
|||
*/
|
||||
package commons;
|
||||
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.CollectionTable;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.OrderColumn;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.ElementCollection;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.OrderColumn;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -59,11 +60,11 @@ public class Recipe {
|
|||
// | 1 (Steak) | 40g pepper |
|
||||
// | 1 (Steak) | Meat |
|
||||
// |----------------------------------|
|
||||
@ElementCollection
|
||||
@OneToMany
|
||||
@CollectionTable(name = "recipe_ingredients", joinColumns = @JoinColumn(name = "recipe_id"))
|
||||
@Column(name = "ingredient")
|
||||
// TODO: Replace String with Embeddable Ingredient Class
|
||||
private List<String> ingredients = new ArrayList<>();
|
||||
private List<RecipeIngredient> ingredients = new ArrayList<>();
|
||||
|
||||
// Creates another table named recipe_preparation which stores:
|
||||
// recipe_preparation(recipe_id -> recipes(id), preparation_step, step_order).
|
||||
|
|
@ -94,7 +95,7 @@ public class Recipe {
|
|||
}
|
||||
|
||||
// TODO: Replace String with Embeddable Ingredient Class for ingredients
|
||||
public Recipe(Long id, String name, List<String> ingredients, List<String> preparationSteps) {
|
||||
public Recipe(Long id, String name, List<RecipeIngredient> ingredients, List<String> preparationSteps) {
|
||||
// Not used by JPA/Spring
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
|
|
@ -119,14 +120,14 @@ public class Recipe {
|
|||
}
|
||||
|
||||
// TODO: Replace String with Embeddable Ingredient Class
|
||||
public List<String> getIngredients() {
|
||||
public List<RecipeIngredient> getIngredients() {
|
||||
// Disallow modifying the returned list.
|
||||
// You can still copy it with List.copyOf(...)
|
||||
return Collections.unmodifiableList(ingredients);
|
||||
}
|
||||
|
||||
// TODO: Replace String with Embeddable Ingredient Class
|
||||
public void setIngredients(List<String> ingredients) {
|
||||
public void setIngredients(List<RecipeIngredient> ingredients) {
|
||||
this.ingredients = ingredients;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,77 +1,60 @@
|
|||
package commons;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Inheritance;
|
||||
import jakarta.persistence.InheritanceType;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
|
||||
/**
|
||||
* The base RecipeIngredient class, holding a reference to the
|
||||
* {@link Ingredient Ingredient} it has an (in)formal amount
|
||||
* linked to.
|
||||
* It uses {@link JsonSubTypes JsonSubTypes} such that the
|
||||
* Jackson framework can conveniently decode JSON data sent
|
||||
* by client, which could be either a formal or vague
|
||||
* ingredient.
|
||||
* @see Ingredient
|
||||
*/
|
||||
@Entity
|
||||
public class RecipeIngredient {
|
||||
|
||||
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||
@JsonTypeInfo(
|
||||
use = JsonTypeInfo.Id.NAME, // identifies subtype by logical name
|
||||
include = JsonTypeInfo.As.PROPERTY,
|
||||
property = "type" // JSON property carrying the subtype id
|
||||
)
|
||||
@JsonSubTypes({
|
||||
@JsonSubTypes.Type(value = FormalIngredient.class, name = "formal"),
|
||||
@JsonSubTypes.Type(value = VagueIngredient.class, name = "vague")
|
||||
})
|
||||
public abstract class RecipeIngredient {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
public Long id;
|
||||
|
||||
// which recipe is used
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "recipe_id")
|
||||
public Recipe recipe;
|
||||
|
||||
//which ingredient is used
|
||||
/**
|
||||
* Many-to-one: Many {@link RecipeIngredient RecipeIngredient}
|
||||
* can use the same {@link Ingredient Ingredient} with varying
|
||||
* amounts. This allows better ingredient collecting for
|
||||
* nutrition view.
|
||||
*/
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "ingredient_id")
|
||||
public Ingredient ingredient;
|
||||
|
||||
public double amount;
|
||||
|
||||
// store the unit name in the database
|
||||
public String unitName;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
protected RecipeIngredient() {
|
||||
// for sebastian
|
||||
// for ORM
|
||||
}
|
||||
|
||||
public RecipeIngredient(Recipe recipe, //which recipe
|
||||
Ingredient ingredient, // which ingredient
|
||||
double amount, // the amount
|
||||
String unit) { //gram liter etc
|
||||
//store it im tha field
|
||||
this.recipe = recipe;
|
||||
public RecipeIngredient(
|
||||
Ingredient ingredient) {
|
||||
//store it in the field
|
||||
this.ingredient = ingredient;
|
||||
this.amount = amount;
|
||||
this.unitName = unit;
|
||||
}
|
||||
|
||||
// Convert unitName to Unit object so java can read it
|
||||
public Unit getUnit() {
|
||||
return switch (unitName) {
|
||||
case "GRAM" -> Unit.GRAM;
|
||||
case "KILOGRAM" -> Unit.KILOGRAM;
|
||||
case "MILLILITER" -> Unit.MILLILITER;
|
||||
case "LITER" -> Unit.LITER;
|
||||
case "TABLESPOON" -> Unit.TABLESPOON;
|
||||
case "TEASPOON" -> Unit.TEASPOON;
|
||||
case "CUP" -> Unit.CUP;
|
||||
case "PIECE" -> Unit.PIECE;
|
||||
|
||||
case "PINCH" -> Unit.PINCH;
|
||||
case "HANDFUL" -> Unit.HANDFUL;
|
||||
case "TO_TASTE" -> Unit.TO_TASTE;
|
||||
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
public double amountInBaseUnit() {
|
||||
Unit unit = getUnit();
|
||||
if (unit == null || !unit.isFormal() || unit.conversionFactor <= 0) {
|
||||
return 0.0;
|
||||
}
|
||||
return amount * unit.conversionFactor;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +1,30 @@
|
|||
package commons;
|
||||
|
||||
//what is a record class and why is it recommended
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A unit enum that holds some values.
|
||||
* A {@link Unit Unit} enum holds primarily formal mass-units
|
||||
* Except for an informal unit that always has a factor of zero
|
||||
* is made for user input purposes.
|
||||
* @see VagueIngredient
|
||||
* @see FormalIngredient
|
||||
*/
|
||||
public enum Unit {
|
||||
//formal units
|
||||
//weight units
|
||||
GRAM("GRAM", true, 1.0),
|
||||
KILOGRAM("KILOGRAM", true, 1000.0 ),
|
||||
// Mass units with their absolute scale
|
||||
GRAMME("g", true, 1.0),
|
||||
KILOGRAMME("kg", true, 1_000.0 ),
|
||||
TONNE("t", true, 1_000_000.0),
|
||||
|
||||
//volume units
|
||||
MILLILITER("MILLILITER",true, 1.0),
|
||||
LITER("LITER", true, 1000.0),
|
||||
TABLESPOON("TABLESPOON",true, 15.0),
|
||||
TEASPOON("TEASPOON", true, 5),
|
||||
CUP ("CUP", true, 240.0),
|
||||
// A special informal unit
|
||||
INFORMAL("<NONE>", false, 0.0);
|
||||
|
||||
//piece should be a formal unit to converse for portions like 3eggs can become 1,5 eggs this way
|
||||
PIECE("PIECE", true, 1.0),
|
||||
|
||||
//informal units
|
||||
PINCH("PINCH", false, 0.0),
|
||||
HANDFUL("HANDFUL", false, 0.0),
|
||||
TO_TASTE("TO_TASTE", false, 0.0);
|
||||
|
||||
public final String name;
|
||||
public final String suffix;
|
||||
public final boolean formal;
|
||||
public final double conversionFactor;
|
||||
|
||||
Unit(String name, boolean formal, double conversionFactor) {
|
||||
this.name = name;
|
||||
Unit(String suffix, boolean formal, double conversionFactor) {
|
||||
this.suffix = suffix;
|
||||
this.formal = formal;
|
||||
this.conversionFactor = conversionFactor;
|
||||
}
|
||||
|
|
@ -36,12 +33,16 @@ public enum Unit {
|
|||
return formal;
|
||||
}
|
||||
|
||||
public boolean isInformal() {
|
||||
return !formal;
|
||||
} //for oskar
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
return suffix;
|
||||
}
|
||||
public static Optional<Unit> fromString(String suffix) {
|
||||
for (Unit unit : Unit.values()) {
|
||||
if (unit.suffix != null && unit.suffix.equals(suffix)) {
|
||||
return Optional.of(unit);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
34
commons/src/main/java/commons/VagueIngredient.java
Normal file
34
commons/src/main/java/commons/VagueIngredient.java
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package commons;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
|
||||
/**
|
||||
* A vague ingredient inheriting from {@link RecipeIngredient RecipeIngredient}.
|
||||
* It has a {@link String String} description that describes an informal quantity.
|
||||
* The vague ingredient renders as its descriptor and its ingredient name
|
||||
* conjugated by a space via {@link VagueIngredient#toString() toString()} method.
|
||||
* @see RecipeIngredient
|
||||
*/
|
||||
@Entity
|
||||
public class VagueIngredient extends RecipeIngredient {
|
||||
private String description;
|
||||
public VagueIngredient() {}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public VagueIngredient(Ingredient ingredient, String description) {
|
||||
super(ingredient);
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return description + " " + ingredient.name;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue