Versão ALPHA! Este artigo está em versão 'Alpha' e, portanto, não foi ainda revisado corretamente

Implementando Cache: Caches

Siga o checklist abaixo: <table class="table table-bordered">

Implementação do Cache (Guice) help! Link para os códigos help!

</table>

Implementação do Cache (Guice)

Finalizado o teste, chegou a hora de fazer a implementação do cache, para isso, deve-se voltar para a interface e em cima da declaração da mesma colocar a anotação @ImplementedBy com o nome da interface seguido por Guice.class.

@ImplementedBy(CacheDeFuncionarioGuice.class)
public interface CacheDeFuncionario

Feito isso, irá aparecer um erro de compilação de compilação, pois não existe a classe ainda. Para criá-la novamente aperte Ctrl+1 e escolha a opção criar classe, esta classe ficará no mesmo pacote da interface, antes de finalizar a criação, aperte Alt+U para definir como DEFAULT, e Alt+A para implementar a interface, irá abrir uma nova janela, ao começar a digitar o nome da interface, o eclipse irá sugerir algumas, escolher a que acabara de criar, aperte ENTER, e em seguida finalizar.

Depois que isso foi feito, irá sumir o erro de compilação. Na classe CacheDeFuncionarioGuice, coloque a anotação @Singleton. Note que já foram criados os métodos definidos na interface na criação da classe CacheDeFuncionarioGuice, e eles aparecem com o retorno null. A anotação @Singleton que deve ser selecionada é da biblioteca com.google.inject.Singleton. Sempre que for criar uma classe que implementa uma interface e já na criação com o Alt+A definir qual interface implementada, o retorno dos métodos será null.

@Singleton
class CacheDeFuncionarioGuice implements CacheDeFuncionario

@Singleton – a anotação é utilizada em classes que se quer apenas uma instância para ser reutilizada para todas as injeções que se tem ligação, garantindo assim sua imutabilidade. A ausência da anotação pode causar pode causar grande perda de desempenho. Por exemplo em uma conexão com banco de dados, onde esta conexão é chamada várias vezes durante a execução de um código, sem a anotação toda vez será criada uma nova instância da classe, o uso da anotação faz com que a classe seja instanciada uma única vez para aquela execução.

Agora será criada a variável que retornará os dados armazenados no cache.

private final LoadingCache<Integer, Funcionario> idCache;

A utilização desta variável na implementação do optional será responsável por retornar a informação esperada dado um id.

Será necessário a criação de mais uma variável para retornar os dados do outro método, a lista de superiores. A declaração desta é assim:

private final LoadingCache<Superior, List<Funcionario>> superiorCache;

OBS:. Toda interface que é passada como chave para o LoadingCache deve implementar e permitir a sobrescrita do Equals e HashCode, se caso não tiver esta implementação no Jdbc está errado e o resultado da busca será zero. Uma solução para este problema é criar uma Key. Para mais informações sobre como se implementa Equals e HashCode e o que é uma key clique aqui.

A interface Superior não implementa o Equals e HashCode e por isso foi criado uma SuperiorKey, o método lista_por_superior terá que ser modificado para porSuperiorKey e ficará assim:

public void lista_por_superior_key() {
   List<Funcionario> contra = FuncionariosFalso.getTodos();
   List<String> prova = transform(contra, new FuncionarioToString());

    Funcionario funcionario = FuncionariosFalso.FUNCIONARIO_1;
    Superior superior = funcionario.getSuperior();
    SuperiorKey superiorKey = new SuperiorKey(superior);
    List<Funcionario> list = cache.porSuperiorKey(superiorKey);
    List<String> res = transform(list, new FuncionarioToString());

    assertThat(res.size(), equalTo(1));
    assertThat(res, equalTo(prova));
  }

O LoadingCache também será alterado para lista_por_superior_key e a declaração ficará assim:

private final LoadingCache<SuperiorKey, List<Funcionario>> superiorKeyCache;

Após as declarações de variáveis, deverá declarar o a interface do buscador para qual o cache está sendo implementado, que neste exemplo ficará assim:

private final BuscarFuncionario buscarFuncionario;

Obs.: Na declaração do Buscar não terá a anotação @Inject.

Note que o nome da classe estará com um erro de compilação, isso acontece pois a classe reclama a ausência de um construtor, para criá-lo aperte Alt+S+A, e aperte ENTER, o construtor criado ficará com esta cara:

CacheDeFuncionarioGuice(LoadingCache<Integer, Funcionario> idCache,
	                  LoadingCache<SuperiorKey, List<Funcionario>> superiorKeyCache,
	                  BuscarFuncionario buscarFuncionario) {
    this.idCache = idCache;
    this.superiorKeyCache = superiorKeyCache;
    this.buscarFuncionario = buscarFuncionario;
 }

Deve-se fazer algumas alterações, remover os dois LoadingCache, e no lugar colocar o CacheBuilder, que irá de fato fazer a criação do cache, passando o tamanho e a inner class que irá implementar o construtor. Após as alterações o construtor ficará assim:

@Inject
public CacheDeFuncionarioGuice(CacheBuilder cacheBuilder,BuscarFuncionario buscarFuncionario)  {
    this.idCache = cacheBuilder
	.small()
	.build(new IdLoader());
    this.superiorKeyCache = cacheBuilder
	.small()
	.build(new SuperiorKeyLoader());
    this.buscarFuncionario = buscarFuncionario;
}

Muito importante: Colocar a anotação @Inject em cima do construtor.

Existe um .small(), que corresponde ao número máximo de informações que o cache irá armazenar, para evitar um dimensionamento errado, verifique uma implementação parecida e coloque o mesmo tamanho. A nível de curiosidade o tamanho small tem um tamanho máximo de 25, o medium 200 e o large 500.

Criado o construtor e colocada a anotação, já se pode fazer a implementação dos métodos, o primeiro será o porId.

  @Override
  public Optional<Funcionario> porId(int id) {
    try {
      Funcionario pojo = idCache.getUnchecked(id);
      return Optional.of(pojo);
    } catch (InvalidCacheLoadException e) {
      return Optional.absent();
    }

A implementação ficará assim, vale a pena ressaltar que, independente da interface, as implementações geralmente são parecidas, mudando apenas o nome da variável criada, e a interface.

O próximo método é o porSuperiorKey, a implementação dele é diferente pelo fato do retorno ser uma lista e não um optional como a implementação anterior, que ficará desta forma:

@Override
public List<Funcionario> porSuperiorKey(SuperiorKey key) {
 return superiorKeyCache.getUnchecked(key); 
}

A implementação da inner class do porId ficará com esse formato:

private class IdLoader extends CacheLoader<Integer, Funcionario> {
  @Override
  public Funcionario load(Integer id) throws Exception {
    return buscarFuncionario.porId(id);
    }
  }

E por fim, a implementação do SuperiorKeyLoader ficará desta forma:

private class SuperiorKeyLoader extends CacheLoader<SuperiorKey, List<Funcionario>> {
  @Override
  public List<Funcionario> load(SuperiorKey key) throws Exception {
     return buscarFuncionario.porSuperiorKey(key);
    }
  }

Vale ressaltar que para métodos de busca parecidos com o do exemplo, a implementação será idêntica, modificando apenas o nome da interface e do buscador.

CacheDeFuncionario.java
CacheDeFuncionarioGuice.java
TesteDeCacheDeFuncionario.java


 
 

objectos, Fábrica de Software LTDA

  • R. Demóstenes, 627. cj 123
  • 04614-013 - Campo Belo
  • São Paulo - SP - Brasil
  • +55 11 5093-8640
  • +55 11 2359-8699
  • contato@objectos.com.br