【问题标题】:Most efficient way to do this select in JPA 2?在 JPA 2 中进行此选择的最有效方法是什么?
【发布时间】:2013-12-14 04:59:08
【问题描述】:

我有一个看起来像这样的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 其中rNN 级别深。

如果我写的是普通的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 的引用,但我没有列表,我需要使用子选择作为列表吗?

这也是在 Google App Engine 中使用 Datastore,并且它通过 JPA 2 支持的某些内容受到限制。

我是否必须求助于低级别的Datastore API?

【问题讨论】:

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


    【解决方案1】:

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

    您可以将列表作为参数传递,但这仅适用于IN 查询。 NOT IN 查询不可用,JOINs 也不可用。

    (另外:您可以使用fromto 属性的组合来创建一个键,在这种情况下您可以只获取实体而不是查询)。

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

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

    @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'
    

    【讨论】:

    • 我已经研究了@ElementCollection,但我看不出它对我有什么帮助,除非我取消to 属性,只在一个@ 上存储一个别名UUID 的列表987654331@ 然后我有相反的问题,如果上游别名之一发生变化,我必须反向查找所有内容...
    【解决方案2】:

    解决方案

    令人惊讶的是,即使有 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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-04-20
      • 2011-06-10
      • 2023-03-17
      • 2017-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多