40

GORM キャッシュの問題を示しているように見える奇妙な状況があります

//begin with all book.status's as UNREAD
Book.list().each { book.status = Status.READ ; book.save() }

println (Book.findAllByStatus (Status.READ)) //will print an empty list
println (Book.list().findAll (it.status == Status.READ)) // will print all books   

最後の 2 つのクエリが異なる結果を返す理由がわかりません。

ただし、次のようにbook.save(flush:true)を変更すると。どちらの println ステートメントもすべての本を返します。

これは単一のアプリケーション内では必要ないという印象を受けました。

参考までに私が使っている

  • データベース: mysql
  • グルービー: 1.7.10
  • グレイル: 1.3.7

@ホアンロング

私の問題を以下に示します。特定のパターンではなく、action1/action2 の両方が何度も呼び出されたとします。

def action1 = {
   Foo foo = Foo.get(params.id)
   //... modify foo 
   foo.save() //if I flush here, it will be inefficient if action1 is called in sequence
}

def action2 = {
   //if I flush here, it will be inefficient if action2 is called in sequence
   List<Foo> foos = Foo.findAllByBar (params.bar)
   //... do something with foos
}

1 つの解決策は、アクション 1 によって設定され、必要に応じてフラッシュするためにアクション 2 によって使用されるフラグを持つことです。私の問題は、これが非常に複雑なソリューションであり、DB 呼び出しの複雑さが増すにつれて拡張できないことです。

boolean isFlushed = true

def action1 = {
   Foo foo = Foo.get(params.id)
   //... modify foo 
   foo.save() 
   isFlushed = false
}

def action2 = {
   if (!isFlushed) {
      //flush hibernate session here
   }
   List<Foo> foos = Foo.findAllByBar (params.bar)
   //... do something with foos
}
4

4 に答える 4

34

あなたの場合、データベースからデータを読み取るため、最初のステートメントは空のリストを返しますが、データはまだありません。

これが Hibernate の仕組みです: で save を呼び出すと(flush: true)、Hibernate セッションがフラッシュされ、セッション内のすべてのデータがすぐにデータベースに永続化されます。を使用しない場合(flush:true)、データは Hibernate セッションでのみ記録され、Hibernate セッションがフラッシュされたときにのみデータベースに保持されます。セッションをフラッシュする時間は、パフォーマンスを最適化するために Hibernate によって自動的に決定されます。

一般に、Hibernate に (最適化のために) 作業を任せるべきです - データをすぐに永続化したい場合を除きます。

ピーター・レドブルックによると:

Hibernate にその仕事を任せて、必要な場合、または少なくとも更新のバッチの最後にのみ手動でセッションをフラッシュします。データベースにあるはずのときにデータが表示されない場合にのみ、実際に使用する必要があります。それが少し希望に満ちたものであることは承知していますが、そのようなアクションが必要になる状況は、データベースの実装やその他の要因によって異なります。

GORMからの落とし穴 - パート 1

更新: すべてのオブジェクトが保存された後にセッションを 1 回フラッシュする方法を明確にする:

import org.hibernate.*

class SomeController {
  SessionFactory sessionFactory

  def save = {
    assert sessionFactory != null

    // loop and save your books here

    def hibSession = sessionFactory.getCurrentSession()
    assert hibSession != null
    hibSession.flush()
  }
}
于 2011-06-09T07:24:56.730 に答える
8

FlushMode の設定は何だったのかしら。

デフォルトでは「auto」に設定されており、DB に直接ヒットするすべてのクエリの前にセッションがフラッシュされることを意味します (おそらく他の場合も同様です)。その場合、Foo.findAllByBarは最初にセッションをフラッシュし (パフォーマンスの問題の可能性があります!)、DB から正しい値を読み取る必要があります。

FlushMode には他に 2 つの値があり、そのうちの 1 つを設定すると問題が説明されます。1 つ目は " manual " で、手動でフラッシュ セッションを行うことを意味します (たとえば、save(flush:true) を使用)。そうしないと、Foo.findAllByBarは古い DB 状態を読み取ります。2 つ目は「commit」です。これは、トランザクションがコミットされるたびにセッションがフラッシュされることを意味します。これは、grails で" withTransaction " ステートメントを使用する場合に非常に便利です。

リソース: http://schneide.wordpress.com/2011/03/08/the-grails-performance-switch-flush-modecommit/ http://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/ html/objectstate.html#d0e1215

于 2013-08-05T07:33:32.783 に答える
1

追加情報として、ドメイン クラス イベント (afterUpdate、beforeUpdate、ect) で flush または save(flush:true) を使用することはできません。スタック オーバーフロー エラーが発生します。ただし、フラッシュせずに save() を使用できます。

ゴームのドキュメント

于 2015-05-14T18:17:46.780 に答える