【问题标题】:Do any of the JPA implementations (or wider Java ORM implementations) support updatable Cursors是否有任何 JPA 实现(或更广泛的 Java ORM 实现)支持可更新游标
【发布时间】:2012-10-18 17:42:59
【问题描述】:

我希望可用于 Java 的对象/关系映射 (ORM) 工具之一能够满足这些要求:

  • 使用 JPA 或本机 SQL 查询获取大量行并将它们作为 entity 对象返回。
  • 在我对当前实体进行更改后,允许通过行(实体)和持久性进行迭代。

我想逐行执行复杂的批处理操作(实际上,我正在将文件中的已知良好数据与数据库中的数据进行比较和协调)。如果它更简单,我会求助于使用 JDBC 并执行一些 SQL;但在这种情况下,我确实从直接从 bean 到数据库中受益。

在 SQL 中,我可以使用可更新的游标来有效地实现我的目标。

作为参考,我正在嵌入式 Java H2 环境中测试所有这些。

JPA 查询

我的第一次天真的尝试是调用 Query.getResultList(),它可以很好地返回实体 bean,但它们“断开连接”。如果我打电话给persistenceUnitUtil.getIdentifier(myEntity),那么它会抱怨它不是实体类型。

休眠

然后我研究了支持ScrollableResults的Hibernate。该界面允许我按名称获取各个列的值,但不能获取实体。

EclipseLink

接下来是支持ScrollableCursor 的EclipseLink。我对这个抱有很好的希望,将它用于:

Query query = entityManager.createQuery(jpaQuery);
query.setHint("eclipselink.cursor", true);
CursoredStream cursoredStream = (CursoredStream)query.getSingleResult();

不幸的是cursoredStream.next(); 再次返回实体的“断开连接”版本。所以,我看不到写回实体的方法。

结论

我目前正在研究至少将实体的@Id 作为查询的一部分传回的方法(不幸的是,我希望保持工具的灵活性,有时我将字符串作为键,而其他时候将复合键对象)。这至少可以让我遍历行然后单独查找和持久化每个实体。

但是,我更希望有一个游标支持的迭代器,它可以为我获取一个与 JPA 连接的实体,并允许我对其进行更改并将其持久化。

如果这不是 ORM 工具之一的已知功能,我可能不得不放弃并求助于老式 JDBC。

【问题讨论】:

  • 您是否考虑过:批量加载实体、迭代和更新它们并一次性刷新更改、重复?
  • 那很好,但我如何获得一批“连接”实体?查询界面给了我“断开连接”的。
  • 它们已断开连接,因为您不在交易中。当事务处于活动状态时,对象是连接的。
  • 嗨@Zagrev,这并不反映我的经验。就我而言,我正在进行交易。
  • 如果你!做!有一个活跃的事务,你的类必须被管理(不是分离的)。如果您确定您的事务处于活动状态,则很可能您使用的 JPA 实现已损坏。

标签: java hibernate orm eclipselink ibatis


【解决方案1】:

伪代码(C#)

void Execute(ISession session, string filepath)
{
    int page = 0;
    int pagesize = 5000;
    int batchindex = int.MaxValue;
    List<Entity> batch = new List<Entity>();

    TextReader file = new StreamReader(filepath)

    string line;
    while ((line = file.ReadLine) != null)
    {
        if (batchindex > batch.Count)
        {
            session.Flush();
            session.Clear();
            batch = session.CreateCriteria<Entity>()
                .AddOrder(Order.Asc(<same order as in file>))
                .SetFirstResult(page * pagesize)
                .SetMaxResults(pagesize)
                .List<Entity>();
            page++;
            batchindex = 0;
        }
        if (database has more rows than the file
        while (!LineIsForEntity(batch[batchindex], line))
        {
            batchindex++;
            // same if (batchindex > batch.Count) as above
        }

        UpdateEntity(batch[batchindex], line);
    }
    session.Flush();
    session.Clear();
}

根据数据类型和上下文,可能会有更好的代码。

更新:使用 C# 进行随机访问,在使用 (N)Hibernate 时应该很高效

const int pagesize = 2000;
var nextbatch = Enumerable.Repeat(0, pagesize)
    .Select(_ => file.ReadLine())
    .TakeWhile(line => line != null);

string[] batch;
while ((batch = nextbatch.ToArray()).Length > 0)
{
    // ignore results, we only want the entities in cache
    session.QueryOver<Entity>()
        .WhereRestrictionOn(e => e.Id).In(batch.Select(line => ExtractId(line)).ToList())
        .List();

    foreach(string line in batch)
    {
        Update(session.Get<Entity>(ExtractId(line)), line);
    }
    session.Flush();
    session.Clear();
}

正如 cmets 中所说,将 session 的使用替换为 entityManager,将 C# 构造替换为 Java。如果实体是独立的,您甚至可以将 while 与多个线程、会话并行化。

【讨论】:

  • 感谢您的发帖,但我不确定这有什么帮助。我编写逻辑地遍历实体并更新它们的代码没有问题(事实上,我会选择遍历数据库结果并在内存结构中查找实体;这更适合我的用例)。我的问题是关于如何使用 JPA 实现来实现这个迭代和更新周期。
  • 用于与 entitymanager 的 JPA 交换会话。流程是一样的:一侧进行迭代,另一侧进行批量访问。如果订单不匹配并且您需要随机访问,则迭代一批,通过一次往返加载相应的实体,更新它们并立即刷新。
猜你喜欢
  • 1970-01-01
  • 2010-11-03
  • 2011-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-23
  • 1970-01-01
相关资源
最近更新 更多