Java EE/JPA:应用键值表技术

6
使用JPA时,我发现我的实体具有动态增长的特点,即实体的属性数量可能会变化。知道有一些基于键值表的解决方案后,我很想了解如何将这些技术应用到JPA(JPQL)中。
一个示例结构如下:
REF-ID       KEY         VALUE
1000         name        foo
1000         category    basic
1001         name        bar
1001         category    advanced
1001         descr       none
--------------------|------------
     PRIMARY        |

现在的问题是,这种类型的表可能会增长到数百万条记录。

下一个问题是如何映射查询,例如:

SELECT name, category, descr
FROM KEYSTORE 
WHERE id=1000;

我在使用JPA时是否有什么概念遗漏,以满足这些要求?还是说我需要使用其他技术?对于性能方面如何考虑呢?其中“descr”可能可用也可能不可用。

1个回答

6

数百万条记录是数据库的正常使用情况。请记住在ref-id上建立索引(如果它不是主键)。您还可以通过ref-id对表进行分区(几乎所有DB都支持,包括MySQL),以提高性能,但这是DB优化而不是JPA优化。

至于JPA,您可以映射Map<>值。如果您的属性将表示为字符串(名称、值),那么只需

class ComplexEntity {
 @ElementCollection
 @CollectionTable....
 Map<String, String> attributes;
}
....
String descr = entity.getAttributes.get("descr")

请参考使用JPA存储Map<String,String>

或者您可以定义一个新的实体:属性并将其映射到该实体:

@Entity
class Attribute {
    @ID
    Long id;
    @Column(name="name")
    String name;
    @Column(name="pvalue")
    String value;
}

class ComplexEntity {
@OneToMany(cascade=CascadeType.ALL,orphanRemoval = true)
@MapKey(name="name") 
@JoinTable(name = "ATTR_TABLE")
private Map<String,Attribute> attributes;

使用这种映射方式,属性映射的键可以是其值的实际字段(上述属性由Attribute.name索引)。
请注意,不要将列/对象字段称为KEY、VALUE、PARAM或任何其他可能的SQL/JPQL关键字,因为它通常在复杂查询中无法正常工作,但很难找到原因(我是通过吸取教训而学习到这一点的)。
至于查询,普通的JPQL可以使用,所以您只需要获取您需要的实体,然后从属性映射中访问所需的值。或者,您也可以针对它们进行查询。
SELECT a.value FROM ComplexEntity c INNER JOIN c.attributes a WHERE c.id = :id AND a.name IN :names

非常感谢您的帮助。但是关于表格的旋转怎么办?我的意思是看看我的查询。当我需要在单个语句中获取实体名称、描述和类别时该怎么办? - John Rumpel
我的答案中的最后一个select语句检索它们,如果你想知道哪一个是哪一个,那么你必须使用AS和UNION(可能)拆分select语句。但是在JPA中,一切都与对象有关。您希望使用未知数量的属性对对象进行建模,因此您可以这样做,但然后,您应该只获取具有所有属性的对象并使用您需要的属性,而不是单独访问属性。如果只是一个具有许多字段的简单对象,那么您将执行以下操作(如果您正在使用Java和JPA,则不应过多考虑底层DB和特定的select语句)。 - Zielu
嗯,但是假设我有10k个对象,每个对象引用1k个(键,值)对。现在我想创建一个动态表格,只包含10个1k个键作为列。使用Map<T,T>,我会选择所有实体,从数据库中获取所有映射(在内存中有100亿条目),并手动“翻译”我的查询以进行编程选择/检查映射条目,对吗? - John Rumpel
使用Attribute实体方式(而不是简单的字符串映射),按照上述设置进行选择(WHERE a.name IN :names_of_interest),每个属性都有其名称和值属性,因此您知道它们代表什么,并且如果检索多个,则可以构造您的动态表格(在属性上添加ManyToOne到ComplexEntity)以了解它们属于哪个。该映射是惰性的,因此直到需要时才会读取。最后,如果您不关心CoplexEntity的对象视图,则完全不要在JPA中映射它,并手动管理表格。 - Zielu
如果您不介意@Zielu,您能解释一下上面查询中的c.id是从哪里来的吗?我假设它是一个我们没有在ComplexEntity中定义的id字段。这是我“应该”生成的东西吗? - D-Klotz
1
@D-Klotz,c.id是CompleEntity的id,所有JPA实体都必须有一个id。虽然上面的示例中没有包含它,但ComplexEntity类也应该声明它(class ComplexEntity { '@ID' id, '@OneToMany' ...)。在属性上使用OneToMany注释时,此id的值用于引用ATTR_TABLE。 - Zielu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接