1

Java で使用できるオブジェクト/リレーショナル マッピング (ORM) ツールの 1 つがこれらの要件を満たすことを願っています。

  • JPA またはネイティブ SQL クエリを使用して、多数の行をフェッチし、それらをエンティティオブジェクトとして返します。
  • 行 (エンティティ) の反復と、現在のエンティティに変更を加えた後の永続化を許可します。

複雑なバッチ操作を実行したいと考えています (実際には、ファイルからの既知の適切なデータをデータベース内のデータと比較して調整しています) 1 行ずつ実行します。もっと簡単なら、JDBC を使って SQL を実行するしかありません。しかし、この場合、Bean からデータベースに直接移行することで本当にメリットが得られます。

SQL では、更新可能なカーソルを使用して目標を効率的に達成できました。

参考までに、組み込みの Java H2 環境でこれらすべてをテストしています。

JPAクエリ

私の最初の素朴な試みは、エンティティ Bean を正常に返すQuery.getResultList()を呼び出すことでしたが、それらは「切断」されています。私が呼び出すと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 に頼る必要があります。

4

1 に答える 1

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();
}

データの種類とコンテキストによっては、より良いコードが存在する可能性があります。

更新: (N)Hibernate の使用中に効率的な C# を使用したランダム アクセス

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();
}

コメントで述べたように、session の使用を entityManager に、C# コンストラクトを Java に置き換えます。エンティティが独立している場合は、複数のスレッド、セッションを使用して並列化することもできます。

于 2012-10-19T09:48:20.577 に答える