Implementando Cache: Caches
Siga o checklist abaixo: <table class="table table-bordered">
</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
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.
Link para os códigos
CacheDeFuncionario.java
CacheDeFuncionarioGuice.java
TesteDeCacheDeFuncionario.java