11

私の grails (1.3.7) アプリケーションでは、CSV ファイルから数千のレコードを一括インポートするために JDBC テンプレートを使用しています (ご想像のとおり、通常の GORM/hibernate を使用するよりもはるかに高速であるため)。

例えば

class Book {
    String title
}

// For each CSV record...
insertStatementList.add("insert into book (id, title) values (nextval('hibernate_sequence'), thetitle)")
...
JdbcTemplate bulkInsert = ...
bulkInsert.batchUpdate(insertStatementList)

このアプローチの問題点は、ドメイン クラスの変更 (subject属性の追加など) には、ドメイン クラスと SQL 挿入ステートメントの両方を変更する必要があることです。

GORM/hibernate スタックは最終的にドメイン クラス定義から SQL を導出する必要があるため、SQL 挿入ステートメントを個別に維持する必要がないように、この機能にアクセスする方法はありますか? または疑似コードでは、次のようなことが可能です。

// would return something like:
// "insert into book (id, title) values (nextval('hibernate_sequence'), 'thetitle')"
def insertStatement = hibernate.getSqlInsertForClass(Book, book.properties)
4

1 に答える 1

1

その中でそれが可能かどうかを言うのに十分なほど、私はgrailsを知りません。クラスのプロパティを一覧表示して動的に構成することで SQL 挿入フィールドを生成しようとしましたが、順序が狂ってしまいます。

CSV 内のそのフィールドの位置を定義する注釈を作成できます。

import java.lang.annotation.*

@Retention(RetentionPolicy.RUNTIME) @interface CSV { int position() }

class Product {
  @CSV(position=1) String description
  @CSV(position=3) BigDecimal price
  @CSV(position=4) Integer type
  @CSV(position=2) boolean soldout
}

複数のマッピングが必要な場合 (たとえば、独自の古い CSV をサポートするため)、エンティティから切り離されるマップ構造または XML を検討する必要があります。

次に、フィールドを繰り返してクエリを作成する必要があります。

def csv = '''rice;10.0;3;false
beet;12.0;2;false
mango;22.0;2;true'''

def properties = Product.declaredFields
  .findAll { it.declaredAnnotations }
  .sort { it.declaredAnnotations[0].position() }

def templateQuery = "INSERT INTO product(#fields) VALUES (#values)"

csv.eachLine { line ->
  def fields = line.split( /;/ )
  def sqlFields = [:]

  fields.eachWithIndex { field, i ->
    sqlFields[properties[i].name] = field
  }

  println templateQuery
    .replace('#fields', sqlFields.keySet().join(","))
    .replace('#values', sqlFields.values().join(","))
}

どちらが印刷されますか:

INSERT INTO product(description,price,type,soldout) VALUES (rice,10.0,3,false)
INSERT INTO product(description,price,type,soldout) VALUES (beet,12.0,2,false)
INSERT INTO product(description,price,type,soldout) VALUES (mango,22.0,2,true)

それはかなり生で、引用符やSQLインジェクションに対するいくつかのものなど、いくつかの研磨が必要ですが、概念実証のように機能します。

私が働いていた古いシステムでは、jboss seam を使用し、jpa バッチで作業した大量のもの、つまりすべてpersist()が完了したら手動でフラッシュしました。gorm で使用する grails にそのようなものはありませんか?

このリンクwithTransactionは、定期的にセッション クリアを適用しながら、grails 内でバッチ更新を使用するブログ投稿を示しています。

    List <Person> batch =[]
    (0..50000).each{
       Person person= new Person(....)
        batch.add(person)
        println "Created:::::"+it
        if(batch.size()>1000){
            Person.withTransaction{
                for(Person p in batch){
                    p.save()
                }
            }
            batch.clear()
        }
      session = sessionFactory.getCurrentSession()
      session.clear()             
    }

うまくいかないよね?そうでない場合は、注釈が解決策になる可能性があります。

また、Java のキャメル ケースと db のアンダースコアの違いにより、列の命名にも問題があります。その翻訳の背後にある人物は、休止状態で、ImprovedNamingStrategyです。彼から何かを得ることができるかもしれません。@CSVまたは、注釈に列名を追加します。JPAのリサイクルのように聞こえます:-)。

log4jdbcもありますが、問題は解決しないと思います。休止状態のSQL生成に忍び込む必要があります。

于 2012-09-24T01:32:26.647 に答える