segunda-feira, 31 de dezembro de 2012

[JPA] Melhorando a performance de aplicações JPA com cache de segundo nível.


O cache de segundo nível é um repositório local, gerenciado pelo mesmo mecanismo de persistencia responsável pelo ORM da aplicação, para melhorar a performance da mesma.

A intenção é melhorar a performance reduzindo as conversações entre aplicação e banco de dados.

É suportado pela especificação JPA e pode ser configurado para os seguintes modos de trabalho (Cache Mode):

ALL Todas as entidades declaradas na unidade de persistência irão para o cache.
NONE Nenhuma entidade da unidade de persistência irá para o cache.
ENABLE_SELECTIVE Habilita o cache para entidades que forem declaradas para tal com a anotação @Cacheable.
DISABLE_SELECTIVE Habilita o cache para todas as entidades da unidade de persistência, exceto para aquelas que estiverem anotadas com @Cacheable(false).
UNSPECIFIED Adota o comportamento default do mecanismo de persistência JPA utilizado.

Um problema comum do uso do cache de segundo nível é quando os dados mudam no banco mas não mudam no cache. É o que se chama de stale read. Stale reads podem ser resolvidos com a estratégia adequada de configuração do modo de trabalho do cache e outras configurações possíveis. Por isso, é muito importante conhecer profundamente o cache de segundo nível do mecanismo de persistência que você pretende adotar.

No modo ENEABLE_SELECTIVE, as subclasses das entidades anotadas com @Cacheable também serão cacheadas a menos que a anotação seja sobrescrita com @Cacheable(false).

Especificando as configurações de cache mode para melhorar a performance.

Para ajustar o cache mode para uma unidade de persistencia, especifique um dos modos possíveis como valor do elemento shared-cache-mode no persistence.xml.

<persistence-unit name="examplePU" transaction-type="JTA">
  <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
  <jta-data-source>jdbc/__default</jta-data-source>
  <shared-cache-mode>DISABLE_SELECTIVE</shared-cache-mode>
</persistence-unit>

Ou então, programaticamente:

EntityManagerFactor emf = 
    Persistence.createEntityManagerFactory(
        "myExamplePU", new Properties().add(
            "javax.persistence.sharedCache.mode", "ENABLE_SELECTIVE"));

OBS: Se o mecanismo de persistencia adotado não suportar cache de segundo nível, as configurações acima não produzirão qualquer efeito na aplicação.

Configurando cache retrieval e store modes.

Se o cache de segundo nível estiver habilitado na unidade de persistência pela configuração do shared cache mode, o comportamento do cache de segundo nível poderá ser modificado configurando as propriedades javax.persistence.cache.retrieveMode e javax.persistence.cache.stroreMode. Estas propriedades podem ser configuradas no nível do contexto de persistência, passando o nome e o valor da propriedade para o método EntityManager.setProperty, ou configurando uma operação per-EntityManager, ou seja, por entidade (EntityManager.find ou EntityManager.refresh) ou no nível per-query, ou seja, por query.

Cache Retrieval Mode

O modo de recuperação de dados a partir do cache, configurado pela propriedade javax.persistence.retrieveMode, controla como os dados são lidos do cache por chamadas ao método EntityManager.find e a partir de queries.

A propriedade retrieveMode pode ser configurada com uma das constantes definidas pelo enum javax.persistence.CacheRetrieveMode (USE que é o default ou BYPASS). Quando configurada para USE, os dados são recuperados a partir do cache, se disponíveis. Se o dado não estiver no cache, o mecanismo de persistência irá buscá-lo no banco. Quando configurada com BYPASS, o mecanismo de persistência ignora o cache e os dados são recuperados diretamente do banco de dados.

Cache Store Mode

O modo de salva dos dados do cache, configurado pela propriedade javax.presistence.storeMode, controla como os dados serão salvos no cache.

A propriedade storeMode pode ser configurada por uma das constantes definidas pelo enum javax.persistence.CacheStoreMode, podendo ser USE, que é o default, BYPASS ou REFRESH. Configurada como USE, o dado no cache é criado ou atualizado na primeira vez que for lido do banco de dados ou gravado (commited) no mesmo. Se o dado já estiver no cache, o modo USE não irá forçar um refresh quando o dado for lido do banco.

Quando configurada para BYPASS, os dados lidos ou gravados no banco não serão inseridos no cache. É isso! O cache será imutável.

Quando configurada para REFRESH o dado é salvo no cache toda vez que for lido ou gravado no banco. Se o dado já estiver no cache, será atualizado (refreshed) no cache.

Configurando o Cache Retrieval ou o Store Mode.

Para configurar o cache retrieval ou store mode, basta chamar o método EntityManager.setProperty do contexto de persistência e passar como parâmetro o par {nome, valor} da propriedade:

EntityManager em = ...;
em.setProperty("javax.persistence.cache.storeMode", "BYPASS");

Para configurar o cache retrieval ou store mode quando chamar os métodos EntityManager.find ou EntityManager.refresh, primeiro crie uma instância de Map<String, Object> e adicione os pares nome, valor conforme a seguir:

EntityManager em = ...;
Map<String, Object> props = new HashMap<String, Object>();
props.put("javax.persistence.cache.retrieveMode", "BYPASS");
String personPK = ...;
Person person = em.find(Person.class, personPK, props);

OBS: O cache retrieve mode será ignorado na chamada do método EntityManager.refresh, pois as chamadas para refresh resultam sempre em dados lidos do banco e nunca do cache.

Para configurar o retrieval ou store mode quando for usar queries, chame o método Query.setHint ou o método TypedQuerie.setHint, dependendo do tipo de query:

EntityManager em = ...;
CriteriaQuery<Person> cq = ...;
TypedQuery<Person> q = em.createQuery(cq);
q.setHint("javax.persistence.cache.storeMode", "REFRESH");
...

OBS: Configurar o store ou o retrieval mode em uma query ou chamando os métodos EntityManager.find ou EntityManager.refresh sobrescreve as configurações do entity manager.


Controlando o cache de segundo nível programaticamente.

A interface javax.presistence.Cache define métodos para interagir com o cache de segundo nível programaticamente. A interface Cache define métodos para checar se uma entidade particular tem dados salvos no cache, para remover uma entidade particular do cache, para remover todas as instâncias (inclusive de subclasses) de qualquer classe de entidade do cache e para limpar o cache de todos os dados das entidades.

OBS: Se o cache de segundo nível for desabilitado, chamadas aos métodos da interface Cache não terão qualquer efeito sobre a aplicação, exceto para o método contains, que sempre irá retornar false.

Checando se os dados de uma entidade estão no cache.

Chame o método Cache.contains para achar ou simplesmente para verificar se uma entidade está no cache de segundo nível. Este método retornará true se os dados da entidade estiverem em cache e false em caso contrário.

EntityManager em = ...;
Cache cache = em.getEntityManagerFactory().getCache();
String personPK = ...;
if (cache.contains(Person.class, personPK)) {
  // the data is cached
} else {
  // the data is NOT cached
}

Removendo uma entidade do cache.


Chame um dos métodos Cache.evict para remover uma entidade em particular ou todas as entidades de um dado tipo do cache de segundo nível.

Para remover uma entidade em particular chame Cache.evict passando a classe da entidade e o Id (chave primária).


EntityManager em = ...;
Cache cache = em.getEntityManagerFactory().getCache();
String personPK = ...;
cache.evict(Person.class, personPK);

Para remover todas as instâncias de uma classe de entidade, incluindo suas subclasses, chame evict e especifique a classe:

EntityManager em = ...;
Cache cache = em.getEntityManagerFactory().getCache();
cache.evict(Person.class);

Todas as instâncias da classe Person serão removidas do cache. Se Person tiver subclasse, chame o método evictAll para remover suas instâncias também.


Removendo todos os dados do cache.


Chamar o método Cache.evictAll irá limpar completamente o cache de segundo nível:


EntityManager em = ...;
Cache cache = em.getEntityManagerFactory().getCache();
cache.evictAll();

E é isso! Este post é o resultado da tradução, com poucas adaptações, do The Java EE 6 Tutorial, Part VI Persistence, Chapter 38 Improving the Performance of Java Persistence APIApplications by Setting a Second-Level Cache.


Fonte: http://docs.oracle.com/javaee/6/tutorial/doc/gkjjj.html
Tradução: Mauricio da Silva Marinho
Ao copiar, dê crédito ao autor.

terça-feira, 11 de dezembro de 2012

Manifesto for Software Craftsmanship

Raising the bar! Sign it today!

As aspiring Software Craftsmen we are raising the bar of professional software development by practicing it and helping others learn the craft. Through this work we have come to value:

Not only working software,
       but also well-crafted software

Not only responding to change,
       but also steadily adding value

Not only individuals and interactions,
       but also a community of professionals

Not only customer collaboration,
       but also productive partnerships

That is, in pursuit of the items on the left we have found the items on the right to be indispensable.

© 2009, the undersigned.
this statement may be freely copied in any form,
but only in its entirety through this notice.

segunda-feira, 10 de dezembro de 2012

A Importância dos Testes de Software

Testar ajuda no entendimento do código:

Normalmente quando começamos a trabalhar num projeto que já está em andamento, não temos idéia do como as coisas funcionam. Às vezes o projeto já tem vários meses de vida e o novo time precisa dar continuidade. Sem os testes unitários e/ou aceitação o tempo para entender como cada parte funciona seria bem maior.

Testar elimina suposições falsas

Quando aprendemos algo novo, suposições são naturalmente feitas. Contudo quando não há entendimento completo do que algo faz, fazemos suposições, quais podem custar muito tempo. Quando nos deparamos com um problema, validamos todas as suposições.

Escrever algumas linhas de código não sai caro e pode de grande utilidade para aumentar a confiança. Grandes problemas pelos quais passamos são causados por coisas pequenas, geralmente, por conta de suposições falsas.

É importante ter segurança

Para cada teste escrito, um pouco mais de segurança é obtida. Se não temos certeza do que um método faz, escrevemos um teste: algumas linhas de código e muitas dores de cabeça serão evitadas no futuro.

"Nada é tão simples que não mereça ser testado". Escreva testes unitários até que você tenha segurança sobre o entendimento o problema.

Testes podem ser utilizados como exemplo para outras pessoas

Testar se mostra ainda mais benéfico quando estamos trabalhando em time. Cada teste é um exemplo prático de como o código deve ser utilizado. Testes servem como exemplos e são de fácil entendimento.

Ao testar o código que você está escrevendo, você não apenas ajuda a si mesmo, mas, poupa muito tempo das pessoas que vão mantê-lo no futuro.

Até a próxima! =]

segunda-feira, 3 de dezembro de 2012

Objetos imutáveis

Objetos imutáveis são aqueles que não permitem modificação no seu estado após sua construção. Joshua Bloch, no seu livro Effective Java, faz a seguinte recomendação: 

"Classes deveriam ser imutáveis a menos que tenhamos uma ótima razão para que seja ao contrário. Se a classe não pode ser imutável, limite a mutabilidade.”  

A utilização deste recurso simplifica a codificação, caso as seguintes premissas sejam observadas:
  • Tenha certeza a classe não possa ser estendida ou tenha seus métodos sobrescritos: utilize o modificador "final" na definição da classe ou utilize fábricas estáticas e esconda os construtores com o modificador de acesso "private";
  • Todos os atributos da classe devem ter ser "private" e/ou "final".
  • Não devem estar disponíveis quaisquer métodos que possam modificar o estado do objeto, nem mesmo assessores de atribuição (sets);
  • Deve proporcionar a construção em uma única chamada, ao invés de disponibilizar um construtor sem argumentos, e sucessivas chamadas a assessores ou quaisquer outros métodos;
  • Se a classe precisa que quaisquer campos sejam mutáveis, estes terão que disponibilizar cópias defensivas.
Objetos imutáveis possuem uma grande lista de vantagens. Sem dúvidas, serão os objetos mais simples e robustos que você pode utilizar. Suas principais características são:
  • São de simples construção, teste e utilização;
  • São automaticamente "thread-safe" e não possuem problemas de sincronização;
  • Não necessitam de construtores de cópia, nem da interface “clone”;
  • Possibilitam seu uso em caches de objetos por utilizar hashCode em "lazy initializations";
  • Não necessitam serem copiados de forma defensiva quando utilizados como atributos em outros objetos (String, Integer, etc);
  • São perfeitos para utilizar como índices em Maps e elementos de um Set por não mudar de estado enquanto estão atribuídos a uma coleção;
  • Possuem o que Joshua Bloch chama de "failure atomicity": um objeto imutável quando lança uma exceção, esta nunca será deixada num estado indeterminado ou não desejado.
Exemplos de objetos imutáveis: String, Integer, Boolean, Datetime (Joda Time API).

Até a próxima! =]