Pergunta

Versão rápida

Basicamente, estou atualizando uma tabela do Hibernate e as consultas subsequentes estão carregando um valor obsoleto.

Versão detalhada

Hibernate (3.3.1.GA) e EhCache (2.4.2).

Persiste Book objeto com um List<PageContent> de páginas e estou adicionando uma página no meio deste livro.Estou usando o Databinder/Wicket, embora não ache que isso esteja relacionado.

 public void createPageContent(Book book, int index) {
     Databinder.getHibernateSession().lock(book, LockMode.UPGRADE);
     PageContent page = new PageContent(book);
     book.addPage(page, index);
     CwmService.get().flushChanges(); // commits the transaction
 }

Os campos/métodos aplicáveis ​​em Book são:

@OneToMany
@JoinColumn(name="book_id")
@IndexColumn(name="pageNum")
@Cascade({CascadeType.ALL, CascadeType.DELETE_ORPHAN})
private List<PageContent> pages = new ArrayList<PageContent>();

public synchronized void addPage(PageContent page, int index) {
    pages.add(index, page);
}

O resultado final é que há uma nova página adicionada a uma lista e o banco de dados é atualizado de acordo e eu confirmei isso em meu armazenamento de dados.No entanto, a próxima consulta para uma página, digamos "Página #4", carrega a "antiga" Página #4 em vez da nova Página #4:

criteria.add(Restrictions.eq("book", book));
criteria.add(Restrictions.eq("pageNum", pageNum));
criteria.setCacheable(true);  

Portanto, removo a contragosto o cache dos critérios.Ele consulta o armazenamento de dados, mas ainda retorna o valor errado.Porém, em ambos os casos, se eu esperar cerca de 2 minutos, tudo estará funcionando conforme o esperado.Presumo que o cache ainda esteja envolvido.Ambos PageContent e Book use esta estratégia de cache:

@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)

Confesso que sou novo em cache e acabei de configurar esse arquivo pela primeira vez.Aqui está meu ehcache.xml:

<defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" statistics="false"/>

<!-- Hibernate's Cache for keeping 'lastUpdated' data on each table.  Should never expire. -->
<cache name="org.hibernate.cache.UpdateTimestampsCache" eternal="true" />

<!-- Hibernate's Query Cache - should probably be limited -->
<cache name="org.hibernate.cache.StandardQueryCache" maxElementsInMemory="1000" />

ATUALIZAR:Removendo o @Cache anotações em meus objetos de armazenamento de dados eliminam o problema.Claro, eu gostaria de armazenar esses objetos em cache porque a modificação da página é muito menos frequente que o acesso.

Então, pensamentos?Existem vários outros problemas relacionados também, inclusive com a exclusão de páginas.Tudo atualiza o banco de dados conforme esperado, mas o comportamento real é instável.

Desde já, obrigado!

ATUALIZAÇÃO #2:Através da depuração, posso confirmar que o Datastore possui as informações corretas e quando a consulta é executada, ela recorre ao cache de segundo nível - que contém informações sujas.Presumo que não cabe a mim despejar do cache toda vez que os dados mudam?

Foi útil?

Solução 2

Eu descobri o problema, mas ele introduz outra coisa.

Basicamente, ao modificar um Book objeto List<PageContent> campo, o Hibernate faz três coisas:

  1. Expira a entrada de cache TimeStamp para ambos Book e PageContent
  2. Faz muitas consultas para redefinir o pageNum campo em cada PageContent objeto
  3. Remove o Book objeto do cache de segundo nível.

Isso garante que as consultas subsequentes procurarão novos objetos, etc.No entanto:

  1. O Hibernate falha ao remover cada renumerado PageContent objeto do cache de segundo nível

Como resultado, qualquer consulta à lista de páginas será executada corretamente, mas recorrerá a valores obsoletos do cache de segundo nível para os dados reais.

Presumo que isso seja porque o Hibernate sente uma pageNum a mudança não é uma mudança nos dados, mas uma mudança na gestão nos bastidores.No entanto, esses são os dados que eu gostaria de ler e exibir.

A solução é atualizar manualmente cada página após a inserção/exclusão.

Outras dicas

Depois CwmService.get().flushChanges(); // commits the transaction faça um commit explícito.flush() apenas libera as alterações no banco de dados, mas não as confirma.não tenho certeza sobre flushChanges() no entanto.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top