I am developing a calories calculator. I have entities Dish
(edible item consisting of ingredients or other dishes) and Ingredient
(atomic edible, e.g. "water" of "potatoes"). Both extends class AbstractEdible
which implements interface Edible
.
Dish
contains field recipe
of type Map<Edible, Double>
, where Double
represents weight of each "dish element" (ingredient or other dish).
I'm trying to persist all this stuff using JPA and Derby. I expect to have a join table with following fields:
DOUBLE
value representing weight of "dish element"Here is what I have in reality:
Instead of VARCHAR
key to "dish element" entity I have a BLOB
and I can't figure out why. Because of that I'm having problems to update existing Dish (getting ERROR 42818: Comparisons between 'BLOB' and 'BLOB' are not supported
). I will really appreaciate your help!
Dish
package com.singularityfx.kcalibri2.model.edibles;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.MapKeyJoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
import javax.persistence.JoinColumn;
@Entity
public class Dish extends AbstractEdible implements EdiblesCollection {
@Transient
private static final long serialVersionUID = -5646610412222252829L;
@ElementCollection
@CollectionTable(name="dish_recipe")
@Column(name="weight")
@MapKeyJoinColumn(name="ingredient_or_dish", referencedColumnName="name")
private Map<Edible, Double> recipe = new HashMap<Edible, Double>();
@Transient
private KCalculator kCalculator = new KCalculator();
public Dish() {}
public Dish(String name, DishCategory category) {
this.name = name;
this.category = category;
}
@Override
public double getKCalNonDessert() {
kCalculator.init(recipe);
return kCalculator.getkCalNonDessertPer100g();
}
@Override
public double getKCalDessert() {
kCalculator.init(recipe);
return kCalculator.getkCalDessertPer100g();
}
@Override
public double getKCal() {
kCalculator.init(recipe);
return kCalculator.getkCalPer100g();
}
@Override
public double getKCalDessertTotal() {
kCalculator.init(recipe);
return kCalculator.getTotalKCalDessert();
}
@Override
public double getKCalNonDessertTotal() {
kCalculator.init(recipe);
return kCalculator.getTotalKCalNonDessert();
}
@Override
public double getKCalTotal() {
kCalculator.init(recipe);
return kCalculator.getTotalKCal();
}
public Double addIngredient(Edible edible, Double weight) {
return recipe.put(edible, weight);
}
public Double removeIngredient(Edible edible) {
return recipe.remove(edible);
}
@Override
public Map<Edible, Double> getContent() {
return recipe;
}
@Override
public void setContent(Map<Edible, Double> content) {
this.recipe = content;
}
@Override
public void clearContent() {
this.recipe.clear();
}
public double getWeight(Edible edible) {
return recipe.get(edible);
}
public Collection<Edible> getEdibles() {
return recipe.keySet();
}
}
Ingredient
package com.singularityfx.kcalibri2.model.edibles;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.NamedQuery;
import javax.persistence.Transient;
@Entity
public class Ingredient extends AbstractEdible {
@Transient
private static final long serialVersionUID = -7669934586986624995L;
private double kCal;
private boolean isDessert;
public Ingredient() {}
public Ingredient(String name, IngredientCategory category,
double kCal, boolean isDessert) {
this.name = name;
this.kCal = kCal;
this.category = category;
this.isDessert = isDessert;
}
@Override
public double getKCalNonDessert() {
return isDessert ? 0 : kCal;
}
@Override
public double getKCalDessert() {
return isDessert ? kCal : 0;
}
}
AbstractEdible
package com.singularityfx.kcalibri2.model.edibles;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.MappedSuperclass;
import javax.persistence.OneToOne;
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public abstract class AbstractEdible implements Edible, Serializable {
private static final long serialVersionUID = 8684184950268663225L;
@Id
protected String name;
@OneToOne()
@JoinColumn(name="NAME_OF_CATEGORY")
protected EdibleCategory category;
@Override
public String getName() {
return name;
}
@Override
public EdibleCategory getCategory() {
return category;
}
public void setName(String name) {
this.name = name;
}
public void setCategory(EdibleCategory category) {
this.category = category;
}
@Override
public String toString() {
return name + " [" + category + "]";
}
@Override
public int compareTo(Edible c) {
return name.compareTo(c.getName());
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((category == null) ? 0 : category.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AbstractEdible other = (AbstractEdible) obj;
if (category == null) {
if (other.category != null)
return false;
} else if (!category.equals(other.category))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
Edible
package com.singularityfx.kcalibri2.model.edibles;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
public interface Edible extends Comparable<Edible> {
public double getKCalNonDessert();
public double getKCalDessert();
public String getName();
public EdibleCategory getCategory();
}
My mistake was here:
private Map<Edible, Double> recipe
Edible
(interface implemented by my entities) is not an entity itself, therefore persistence provider doesn't recognize map key as entity.
When I refer to abstract class (which is annotaded with @Entity
) instead of interface everything works as expected:
private Map<AbstractEdible, Double> recipe