1

私のPOJO(Categoryという名前)には、マッピングlangMapを格納する (Language Map) があります。Locale -> String次のように定義されています。

@Entity
class Category implements Serializable {

  @ElementCollection
  @MapKeyColumn(name = "locale")
  @Column(name = "name")
  @CollectionTable(name = "CategoryName", joinColumns = @JoinColumn(name = "category_id"))
  private Map<Locale, String> langMap = new HashMap<>();

  // other fields skipped.

}

マップを更新するまではうまく機能します: コードは単純です:

  public void replaceLangMap(Map<Locale, String> map) {
    langMap.clear();
    langMap.putAll(map);
  }

すべてをクリアしてlangMap、新しい値を入力します。(確かに、それは@Transactionalmerged() です)

しかし、ビューレイヤーを更新すると、古いマップの結果が表示されることがあります。キャッシュレイヤーを追加していないと確信しています。

ここに私が見るものがあります:

たとえば、 を格納するカテゴリが 1 つあります。

en -> Vocation
de_DE -> Berufung

mysqlでは正しく表示されます:

mysql> select * from CategoryName where category_id = 1;
+-------------+----------+--------+
| category_id | name     | locale |
+-------------+----------+--------+
|           1 | Berufung | de_DE  |
|           1 | Vocation | en     |
+-------------+----------+--------+
2 rows in set (0.00 sec)

ビューレイヤーでは、意図的に各名前に「X」を追加しました。

ここに画像の説明を入力

コミット後、古いマップが正しく置き換えられ、mysql で値が実際に変更されます。

mysql> select * from CategoryName where category_id = 1;
+-------------+-----------+--------+
| category_id | name      | locale |
+-------------+-----------+--------+
|           1 | BerufungX | de_DE  |
|           1 | VocationX | en     |
+-------------+-----------+--------+
2 rows in set (0.00 sec)

しかし、そのようなページをリロードすると、古い地図が表示されることがあります (常にではなく、約 50/50 ):

18:14:42.698 INFO  models.Category - en -> VocationX
18:14:42.698 INFO  models.Category - de_DE -> BerufungX

18:14:42.706 INFO  models.Category - en -> VocationX
18:14:42.706 INFO  models.Category - de_DE -> BerufungX

18:14:44.165 INFO  models.Category - en -> Vocation
18:14:44.165 INFO  models.Category - de_DE -> Berufung

Categoryログは、ビュー層ではなく、ドメイン オブジェクト () に書き込まれます。更新するたびに、 POJO でログがトリガーされます。したがって、ビューレイヤーは何もキャッシュしていないと確信しています。

メモリから消去されていない古いものがあるようでlangMap、休止状態は時々そのバージョンを取得します。もう一度修正すると、ランダムにローテーションする 3 つのバージョンのマップが表示されます。これは奇妙です。

サーバーを再起動するだけで、常に正しいlangMap.

ここで何が間違っている可能性がありますか?

環境 :

hibernate-jpa-2.1-api-1.0.0.Final
Hibernate 4.3.1.Final
MySQL 5.5.21 - MySQL Community Server 
Table is innodb
mysql client library : mysql-connector-java 5.1.27

- - - - - - 更新しました - - - - - -

好奇心から、CategoryDao.get(1)本当に db にヒットするかどうかを調べたいと思います。をオンにしhibernate.show_sql=true、いくつかのログインを追加してからCategoryDao.get(1)、プロセスを再実行しました。

  @Override
  public Category get(Serializable id) {
    if (id == null)
      throw new PersistenceException("id may not be null");
    Category obj = emp.get().find(Category.class, id);
    logger.info("get id={} of object class {}", id, Category.class.getSimpleName());
    return obj;
  }

そして結果:

    select aaa as aaa , bbb  as bbb , … // fields skipped
    from
        Category category0_ 
    left outer join
        CategoryName langmap1_ 
            on category0_.id=langmap1_.category_id 
    where
        category0_.id=?
INFO  d.CategoryDao$$EnhancerByGuice$$1904dfdf - get id=1 of object class Category
INFO  d.CategoryDao$$EnhancerByGuice$$1904dfdf - get id=1 of object class Category
INFO  d.CategoryDao$$EnhancerByGuice$$1904dfdf - get id=1 of object class Category

すべてget()がロガーをトリガーしますが、予想どおり、古いデータには SQL ログが表示されません。彼らはデータベースにアクセスしていないようです。最新のデータに SQL ログが表示されないことがあります。とにかく、SQLコードが表示されれば、結果は間違いなく最新です。

これはキャッシュの問題のようです。ただし、ここではキャッシュ (ehcahce を含む) を使用していません。hibernate.cache.use_query_cachehibernate.cache.use_second_level_cacheを false に設定しましたが、無駄でした。

ここで何が間違っている可能性がありますか?

------------ 更新 2 ------------

@Transactionalコメントで、 DAOのget(id)方法を紹介することで問題を解決できると思いました。ただし、(Web の) アクション全体がカテゴリのみを取得する場合にのみ機能します。たとえば、次は問題ありません。

  public Result category(@Param("id") Long id ) {
    Category category = categoryDao.get(id);
    return Results.html()    
      .render("category" , category);
   }

カテゴリをどのように変更しても、正常に機能langMaplangMap、 db に正しく保存され、 db から取得されます。SQL が表示されます。すべてget(id)が実際にデータベースにヒットします。

しかし実際には、このアクションは通常、1 つのカテゴリ オブジェクトをレンダリングするだけではありません。たとえばsubCategories、カテゴリの下のアイテムまたはアイテムをフェッチする他のクエリがあります。

Category category = categoryDao.get(id);
Map<Category , Long> catMap = categoryDao.getSubCategories(category).stream()
  .collect(Collectors.toMap(cat -> cat, cat -> categoryDao.getChildCount(cat)));

List<DataDto> dataList = dataService.getDataList(category , page , count);

そんなアクションも良さそうですが、オフラインテストもOK。しかし、オンラインの場合、カテゴリの を更新した後langMap、不思議なことにキャッシュされlangMapた が再び浮き上がることがあります (WTF!)。また、categoryDao.get(id)常に DB にヒットするとは限りません。

さて、ここで何が間違っているのでしょうか?

4

1 に答える 1

1

正確なコードがなければ、何が問題なのかを判断するのは非常に困難です。

いくつかの観察:

常に @Transactional (書き込みアクセス用) または @UnitOfWork (読み取り専用) のいずれかを使用します。@Transactional または @UnitOfWork の外にあるものはすべて接続されません。接続がない場合、オブジェクトのフィールドは魔法のように更新されません。

したがって、Ninja html テンプレート内で Hibernate プロキシ オブジェクトをレンダリングすると、うまくいかない場合があります。特にプロキシが切り離されていないか、適切に同期されていない場合。接続がありません。また、テンプレートは、特別なプロキシ プロパティの処理方法を認識している場合と認識していない場合があります。

ビューパターンのセッションについて:私はそれをアンチパターンとしてしか知りません。Ninja で動作させる理論的な方法はありますが、お勧めしません。経験則では、魔法を使わずに POJO のみをレンダリングします。何か奇妙なことが起こる割合を劇的に減らします。

それとは別に、あなたの質問は guice-persist (Ninja が使用) と Ninja に関連しているようです。

ここにいくつかのリンクがあります:

于 2014-03-07T17:23:12.520 に答える