我使用Java Persistence创建了一个基本的EAV模型(需要EAV或类似功能,因为用户将不时在运行时动态创建新属性)。
我最近遇到了一个我无法正确解决的问题(只有我不喜欢的解决方法,下面的解决方法,请参阅DataEntity#buildIndex())
问题如下:
DataEntity属于某个DataEntityType,此DataEntityType具有许多PropertyType,用于定义属性(Type,Validators,Length等)。
DataEntity有许多属性,每个属性对应DataEntity的DataEntityType。
问题如下:(另请参阅下面的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已经在这里,因为一个Property在作为Object加载时都有对它们的引用!
我想到的另一个想法是用@PostLoad编写一个CriteriaQuery运行但是我想在我做之前首先要求输入一些,因为这基本上和我现在做的一样......而且我我甚至不确定是否明智地做一个更复杂的查询以明智地填充Map的性能...也许在应用程序而不是数据库中执行此操作会更好,因为数据必须以任何方式加载和传输...
感谢您的帮助。
以下代码:
@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()
{
}
}