保留 Map<Entity, Double>:在 join table 中使用 BLOB 而不是 String 键
Persisting Map<Entity, Double>: having BLOB instead of String key in join table
我正在开发卡路里计算器。我有实体 Dish
(由配料或其他菜肴组成的可食用物品)和 Ingredient
(原子可食用,例如 "water" of "potatoes")。两者都扩展了实现接口 Edible
的 class AbstractEdible
。
Dish
包含类型 Map<Edible, Double>
的字段 recipe
,其中 Double
表示每个 "dish element"(配料或其他菜肴)的重量。
我正在尝试使用 JPA 和 Derby 保留所有这些内容。我希望有一个包含以下字段的连接 table:
- 盘实体的键
DOUBLE
值表示 "dish element" 的权重
- "dish element" 实体的键
这是我现实中的情况:
"dish element" 实体的 VARCHAR
键我有一个 BLOB
而不是我不知道为什么。因此,我在更新现有 Dish 时遇到问题(得到 ERROR 42818: Comparisons between 'BLOB' and 'BLOB' are not supported
)。非常感谢您的帮助!
菜肴
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();
}
}
成分
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;
}
}
可食用
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();
}
您的 Edible 界面扩展了 Comparable。因此,您应该在接口或实现 class 中覆盖 equals
和 hashCode
。联接需要比较无法比较的对象,除非您覆盖 equals
和 hashCode
。
我的错误在这里:
private Map<Edible, Double> recipe
Edible
(由我的实体实现的接口)本身不是实体,因此持久性提供程序不会将映射键识别为实体。
当我引用抽象 class(用 @Entity
注释)而不是接口时,一切都按预期工作:
private Map<AbstractEdible, Double> recipe
我正在开发卡路里计算器。我有实体 Dish
(由配料或其他菜肴组成的可食用物品)和 Ingredient
(原子可食用,例如 "water" of "potatoes")。两者都扩展了实现接口 Edible
的 class AbstractEdible
。
Dish
包含类型 Map<Edible, Double>
的字段 recipe
,其中 Double
表示每个 "dish element"(配料或其他菜肴)的重量。
我正在尝试使用 JPA 和 Derby 保留所有这些内容。我希望有一个包含以下字段的连接 table:
- 盘实体的键
DOUBLE
值表示 "dish element" 的权重
- "dish element" 实体的键
这是我现实中的情况:
"dish element" 实体的 VARCHAR
键我有一个 BLOB
而不是我不知道为什么。因此,我在更新现有 Dish 时遇到问题(得到 ERROR 42818: Comparisons between 'BLOB' and 'BLOB' are not supported
)。非常感谢您的帮助!
菜肴
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();
}
}
成分
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;
}
}
可食用
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();
}
您的 Edible 界面扩展了 Comparable。因此,您应该在接口或实现 class 中覆盖 equals
和 hashCode
。联接需要比较无法比较的对象,除非您覆盖 equals
和 hashCode
。
我的错误在这里:
private Map<Edible, Double> recipe
Edible
(由我的实体实现的接口)本身不是实体,因此持久性提供程序不会将映射键识别为实体。
当我引用抽象 class(用 @Entity
注释)而不是接口时,一切都按预期工作:
private Map<AbstractEdible, Double> recipe