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并输入新值。(当然,它是@Transactionalmerge() )

但是,当我刷新视图层时,有时会看到旧地图的结果。我确定我没有添加任何缓存层。

这是我看到的:

例如,有一个类别存储

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的没有从内存中清除,hibernate偶尔会得到那个版本。如果我再修改一下,就会有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_cache, and设置hibernate.cache.use_second_level_cache为 false ,但徒劳无功。

这里有什么问题?

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

在评论中,我认为我通过介绍@TransactionalDAO 的get(id)方法来解决问题。但它仅在整个(网络)操作仅检索 category 时才有效。例如,以下是可以的:

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

它运行良好,无论我如何修改类别langMaplangMap都正确存储到 db 并从 db 检索。我看到了 SQL,每一个都get(id)真正击中了数据库。

但实际上,这个动作一般不会只渲染一个类别对象。例如,我有其他查询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);

这样的动作看起来还可以,离线测试也可以。但是上网的时候,更新分类后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 回答