我已经使用 Java Persistence 创建了一个基本的 EAV 模型(需要 EAV 或类似的,因为用户会不时在运行时动态创建新属性)。
我最近遇到了一个我无法正确解决的问题(仅使用我不喜欢的解决方法,下面的解决方法,请参阅 DataEntity#buildIndex() )
问题如下: DataEntity 属于某个 DataEntityType,此 DataEntityType 有许多 PropertyType,它们定义了属性(类型、验证器、长度等)。
然后,一个 DataEntity 有许多属性,一个对应于 DataEntity 的 DataEntityType 所具有的每个 PropertyType。
问题如下:(另请参阅下面的 Class DataEntity 以了解我在此处以带有注释的代码形式描述的内容)我想像这样映射这些属性:
Map<PropertyType, PropertyString> strings;
Map<PropertyType, PropertyInteger> integers;
等等。
我尝试使用@EmbeddedId 执行此操作,但这会导致一列以字符串为PK 看起来像这样的“12-16”,即Entity.id 和Property.id
我想避免这种情况并像现在一样使用属性表:
property_string(id, entity_id, property_type_id, value)
property_integer(id, entity_id, property_type_id, value)
注意:entity_id 和 property_type_id 已经在这里,因为当作为 Object 加载时,Property 对这两者都有引用!
我正在考虑的另一个想法是使用@PostLoad 编写一个 CriteriaQuery 运行,但我想在这样做之前先询问一些输入,因为这基本上与我现在正在做的事情相同......而且我不是甚至可以确定是否明智地进行更复杂的查询以填充地图的性能......也许在应用程序而不是数据库中这样做会更好,因为数据必须以任何一种方式加载和传输......
谢谢您的帮助。
下面的代码:
@Entity
public class DataEntity
{
@Getter
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
protected long id;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "entity_type")
protected DataEntityType type;
@OneToMany(mappedBy = "entity", cascade = CascadeType.ALL)
protected List<PropertyInteger> integers = new ArrayList<PropertyInteger>();
@OneToMany(mappedBy = "entity", cascade = CascadeType.ALL)
protected List<PropertyString> strings = new ArrayList<PropertyString>();
// CURRENT SOLUTION; CREATE THOSE AFTER LOADING (SEE method "indexData()" below)
// WHAT IM TRYING TO DO IS LOAD THESE MAPS DIRECTLY WITHOUT THE MIDDLE STEP
@Transient
protected Map<PropertyType, PropertyInteger> mappedIntegers = new HashMap<PropertyType,PropertyInteger>();
@Transient
protected Map<PropertyType, PropertyString> mappedStrings = new HashMap<PropertyType, PropertyString>();
protected DataEntity(DataEntityType t)
{
type = t;
}
@Deprecated
protected DataEntity()
{
}
@PostLoad
private void indexData()
{
indexData(integers, mappedIntegers);
indexData(strings, mappedStrings);
}
private <T extends Property<?>> void indexData(List<T> list, Map<PropertyType, T> map)
{
map.clear();
for (T p : list)
{
map.put(p.getType(), p);
}
}
// methods to deal with property creation/modification/etc skipped since they have no influence
// on the mappings
}
@Entity
public class DataEntityType
{
@Getter
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Getter
@OneToMany(mappedBy = "entityType", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<PropertyType> propertyTypes = new ArrayList<PropertyType>();
protected DataEntityType()
{
}
}
@MappedSuperclass
public class Property<T>
{
@Getter
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Getter
@ManyToOne(cascade = CascadeType.ALL)
private PropertyType type;
@Getter
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "entity", nullable = false)
private DataEntity entity;
@Getter
@Setter
@Column(name = "value", nullable = true)
private T value;
protected Property(DataEntity e, PropertyType t, T v)
{
entity = e;
type = t;
value = v;
}
@Deprecated
protected Property()
{
}
}
@Entity
public class PropertyType
{
@Getter
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name = "code", nullable = false)
private String code;
@ManyToOne
@JoinColumn(name = "entity_type")
private DataEntityType entityType;
protected PropertyType(DataEntityType t, String c)
{
entityType = t;
code = c;
}
@Deprecated
protected PropertyType()
{
}
}
@Entity
public class PropertyString extends Property<String>
{
public PropertyString(PropertyType t, DataEntity e, String v)
{
super(e, t, v);
}
@Deprecated
protected PropertyString()
{
}
}
@Entity
public class PropertyInteger extends Property<Integer>
{
public PropertyInteger(PropertyType t, DataEntity e, Integer v)
{
super(e, t, v);
}
@Deprecated
protected PropertyInteger()
{
}
}