最有效的方法是在JPA 2中选择吗?

时间:2013-12-14 04:59:09

标签: google-app-engine jpa google-cloud-datastore jpa-2.0

我有Entity看起来像这样:

@Entity
public class Relationship
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Key key;

    @Basic
    private UUID from;

    @Basic
    private UUID to;
}

现在我可以像这样拥有任意级别的间接:

final Relationship r0 = new Relationship(a,b);
final Relationship r1 = new Relationship(b,c);
final Relationship r2 = new Relationship(c,d);
final Relationship rN = new Relationship(d,e);

现在我希望尽可能高效地找到a给我回复e rN N级别为SQL

如果我经常写SELECT r.to FROM relationship r WHERE r.from = 'a' AND r.to NOT IN ( SELECT r.from FROM relationship r) ,我会做类似跟随伪代码的事情:

List

我唯一可以在网上找到的是将Criteria.Builder.In作为参数传递给Datastore,但我没有列表,我需要使用子选择作为列出?

此外,这是使用Google App Engine中的Datastore,并且仅限于通过JPA 2支持的某些内容。

我是否必须使用低级{{1}} API?

2 个答案:

答案 0 :(得分:0)

在数据存储区中,没有办法发出单个查询来从'a'获取'e'。事实上,获得e的唯一方法是线性地单独查询每个关系,因此您需要进行四次查询。

您可以将列表作为参数传入,但这仅适用于IN查询。 NOT IN个查询不可用,JOIN也没有。

(旁白:您可以使用fromto属性的组合来创建密钥,在这种情况下,您只需获取实体而不是查询。)

通常,GAE数据存储版本的服务是非规范化,即编写可以启用查询的额外数据。 (这很痛苦,因为它也意味着当你更新一个实体时,你需要小心更新非规范化数据,并且很难同步它 - 它专为网络类型流量设计,其中读取发生得更多经常比写。)

这是一个潜在的解决方案:

@Entity
public class Relationship
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Key key;

    @Basic
    private UUID from;

    @Basic
    private UUID to;

    @ElementCollection
    private Collection<UUID> reachable;
}

在这种情况下,您只需查询

WHERE from = 'a' and reachable = 'e'

答案 1 :(得分:0)

解决方案

令人惊讶的是,即使有1000级间接,这种递归方法也不会出现StackOverflow错误,至少在本地开发服务器上是这样。

public UUID resolve(@Nonnull final UUID uuid)
{
    final EntityManager em = EMF.TRANSACTIONS_OPTIONAL.createEntityManager();
    try
    {
        final String qs = String.format("SELECT FROM %s a WHERE a.from = :from ", Alias.class.getName());
        final TypedQuery<Alias> q = em.createQuery(qs, Alias.class);
        q.setParameter("from", uuid);
        final Alias r;
        try
        {
            r = q.getSingleResult();
            final Key tok = KeyFactory.createKey(Alias.class.getSimpleName(), r.getTo().toString());
            if (em.find(Alias.class, tok) == null)
            {
                return r.getTo();
            }
            else
            {
                return this.resolve(r.getTo());
            }
        }
        catch (final NoResultException e)
        {
            /* this is expected when there are no more aliases */
            return uuid;
        }
    }
    finally
    {
        em.close();
    }
}

我所拥有的压力测试代码是在实际的GAE服务上超时,但我并不担心,我在实践中不会一次创建多个间接层。并且它不仅仅是一小部分间接,而且无论如何它都会在最终版本中被提升到Memcache