保留 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:

  1. 盘实体的键
  2. DOUBLE 值表示 "dish element"
  3. 的权重
  4. "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 中覆盖 equalshashCode。联接需要比较无法比较的对象,除非您覆盖 equalshashCode

我的错误在这里:

private Map<Edible, Double> recipe

Edible(由我的实体实现的接口)本身不是实体,因此持久性提供程序不会将映射键识别为实体。

当我引用抽象 class(用 @Entity 注释)而不是接口时,一切都按预期工作:

private Map<AbstractEdible, Double> recipe