Couchbase e Java EE

O Couchbase é um banco de dados NoSQL que é baseado em duas tecnologias já existentes, o memcache e o CouchDB. Ou seja, ele é um banco de dados não relacional que possui suporte a duas implementações: chave-valor e documento. O objetivo desse post é falar um pouco das vantagens desse banco, instalação com o docker além do seu uso com Java EE.

 

Qual o diferencial do Couchbase para outros bancos dados?

Os pontos fortes do Couchbase:

  • Ele é bastante visual: Isso mesmo, boa parte das configurações que são feitas por ele é a partir uma interface gráfica bem intuitiva a partir do browser. Com essa interface é possível, criar índices, definir tamanho de memória, juntar clusters, criar buckets, número de memória principal que um bucket utilizará dentre outras coisas.

  • Dois bancos em um: Com o Couchbase é possível trabalhar com documento ou chave ou os dois ao mesmo tempo com um único banco.
  • Uma ferramenta de monitoração excelente: Como parte da sua riquíssima inteface gráfica, com ele é possível saber a saúde dos servidores, quantos servidores estão conectados, processamento, memória, logs e muito mais.
  • N1QL: O N1QL, se pronuncia níquel, é uma query language declarativa que estende do SQL, ou seja, com ele é possível realizar consultas ou alterações no banco de dados de uma maneira bem semelhante ao que se faz com o banco relacional, tecnologia bem popular para a maioria dos desenvolvedores Java. Tendo como diferencial, o N1QL é uma query focado em consultas para JSON.

SELECT * FROM databases WHERE category='NoSQL'

 

  • Um motor de busca integrado: Ainda em fase de desenvolvimento. O motor de busca é um recurso muito comum quando se fala em banco de dados distribuídos, principalmente, para evitar as queries com like e coringa, o famoso like ‘%as%’. Com ele é possível criar índice com Character Filter, Tokenizer, Token Filters, Analizer, Mappings, além de atualizar índice, tudo isso, a partir de uma interface gráfica muito intuitiva.
  • Estruturas especiais para a implementação de chave valor: Assim como o redis, o couchbase possui estruturas especiais, assim com o couchbase é possível utilizar Set, List, Queue e também Map.

Mas porque não utilizar simplesmente o cache?

A grande vantagem está nas suas chamadas específicas, imagine que um sistema armazena a lista dos 1000 produtos mais comprados e é necessário adicionar mais um. Com um simples cache, será necessário serializar os mil elementos, adicionar um novo elemento e apenas em seguida retornar para o banco de dados a chave com mil e um elementos. Com a estrutura especial como uma lista, é necessário apenas enviar o elemento que se deseja adicionar, assim, economiza rede e também processamento. O outro ponto é que os bancos de dados chave-valor foram criados para que os dados tenho um tempo de vida maior que um cache.

 

Instalação

A instalação é bastante prática, existe a possibilidade de fazer a instalação manualmente ou uma imagem docker.

Para mais informações:

https://hub.docker.com/r/couchbase/server/

Durante a instalação:

  • Nas configurações habilite a opção Full-Text Resource
  • Quando estiver configurado e com usuário e senha definida crie um bucket, nesse artigo utilizaremos o bucket “heroes”.
  • Siga as instruções no site do couchbase e crie um índice para o bucket anterior, sugestão de nome, heroes-index.

Para o artigo não ficar muito longo a instalação e a configuração do docker e/ou couchbase não fará parte desse artigo, para informações:

Começando com o modelo

Como nosso objetivo é criar um exemplo simples com Java EE e Couchbase, utilizaremos o CDI 2.0 com Java SE e faremos um simples exemplo com heróis. E com vilões:

 

@Entity
public class Hero implements Serializable {

    @Id
    private String id;

    @Column
    private String name;

    @Column
    private String realName;

    @Column
    private Integer age;

    @Column
    private Set<String> powers;

}

@Entity
public class Villain implements Serializable {

    @Id
    private String id;

    @Column
    private String name;

}

 

Uma coisa importante, é que mesmo não sendo JPA, as anotações são fortemente inspiradas nele. O objetivo do JNoSQL foi tentar usar o máximo a nomenclatura do JPA, porém, levando em consideração que o banco em questão é NoSQL.

 

Configuração

Com o servidor funcionando e rodando o próximo passo será a definir a configuração do banco de dados. Para isso será necessário criar um arquivo dentro do META-INF, no caso o jnosql.json. Como a ideia é exibir as duas APIs, será necessário definir a configuração para as duas APIs, para chave-valor e documentos, porém, poderá ser feito num único documento como mostra o código abaixo:

[
   {
      "description":"The couchbase document configuration",
      "name":"document",
      "provider":"org.jnosql.diana.couchbase.document.CouchbaseDocumentConfiguration",
      "settings":{
         "couchbase-host-1":"localhost",
         "couchbase-user":"root",
         "couchbase-password":"123456"
      }
   },
   {
      "description":"The couchbase key-value configuration",
      "name":"key-value",
      "provider":"org.jnosql.diana.couchbase.key.CouchbaseKeyValueConfiguration",
      "settings":{
         "couchbase-host-1":"localhost",
         "couchbase-user":"root",
         "couchbase-password":"123456"
      }
   }
]

 

O arquivo tem configuração com os seguintes parâmetros:

  • name: o nome da unidade de configuração
  • description: uma descrição, essa informação não é utilizada pelo JNoSQL
  • provider: O nome da classe que será invocada para chamar interpretar as configurações e rodasr as configurações do banco.
  • Settings: São informações específicas de cada banco de dados, no caso do couchbase configuramos os hosts clientes, o usuário e a senha para se conectar no banco de dados.

O arquivo configurado o próximo passo é injetar o as configurações dentro do código.

 

@ApplicationScoped
public class CouchbaseProducer {

    private static final String HEROES = "heroes";

    @Inject
    @ConfigurationUnit(name = "document")
    private DocumentCollectionManagerFactory<CouchbaseDocumentCollectionManager> entityManager;

    @Inject
    @ConfigurationUnit(name = "key-value")
    private BucketManagerFactory<BucketManager> bucketManager;


    @Produces
    public DocumentCollectionManager getManager() {
        return entityManager.get(HEROES);
    }

    @Produces
    public CouchbaseDocumentCollectionManager getCouchbaseDocumentCollectionManager() {
        return entityManager.get(HEROES);
    }

    @Produces
    public List<String> getHeroList() {
        return bucketManager.getList(HEROES, String.class);
    }


    @Produces
    public Set<String> getHeroSet() {
        return bucketManager.getSet(HEROES, String.class);
    }

    @Produces
    public BucketManager getBucketManager() {
        return bucketManager.getBucketManager(HEROES);
    }

}

 

A partir da anotação ConfigurationUnit ele retornará uma fábrica responsável pela criação de gerentes de entidades. Seja para chave valor, BucketManagerFactory, seja para documentos, DocumentCollectionManagerFactory. A partir da produção das classes que serão gerentes de entidades o JNoSQL se encarregará de gerenciar todo ciclo de vidas das outras classes que veremos a seguir.

Documentos

Uma vez configurado e os modelos definidos o próximo passo é realizar as operações básicas no banco de dados. Para isso será utilizado a classe DocumentTemplate, as classes to tipo template realizam operações CRUD de maneira simples e intuitiva, é ela que se encarregará de converter as entidades para a camada de comunicação, aka Diana, que por sua vez enviará para o banco de dados.

public class App {


    public static void main(String[] args) {

        try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {

            Hero ironMan = Hero.builder().withRealName("Tony Stark").withName("iron_man")
                    .withAge(34).withPowers(Collections.singleton("rich")).build();
            DocumentTemplate template = container.select(DocumentTemplate.class).get();

            template.insert(ironMan);

            DocumentQuery query = select().from("Hero").where(eq(Document.of("_id", "iron_man"))).build();
            List<Hero> heroes = template.select(query);
            System.out.println(heroes);

        }
    }

    private App() {
    }
}

 

Couchbase é um banco que possui recursos particulares muito interessante, por exemplo, é possível utilizar o motor de busca dentro do couchbase além do uso do N1QL. Como olhar recursos específicos é uma das premissas do JNoSQL. O Couchbase tem uma extensão do DocumentTemplate, o CouchbaseTemplate, essa classe é uma especialização do DocumentTemplate que executa recursos específicos do Couchbase, já mencionado anteriormente.

public class App1 {


    public static void main(String[] args) {

        try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {

            Hero ironMan = Hero.builder().withRealName("Tony Stark").withName("iron_man")
                    .withAge(34).withPowers(Collections.singleton("rich")).build();

            CouchbaseTemplate couchbaseTemplate = container.select(CouchbaseTemplate.class).get();
            couchbaseTemplate.insert(ironMan);

            DocumentQuery query = select().from("Hero").where(eq(Document.of("_id", "iron_man"))).build();
            List<Hero> heroes = couchbaseTemplate.select(query);
            System.out.println(heroes);

            MatchQuery match = SearchQuery.match("rich").field("powers");
            SearchQuery search = new SearchQuery("heroes-index", match);
            List<Hero> searchResult = couchbaseTemplate.search(search);
            System.out.println(searchResult);


        }
    }
    private App1() {
    }
}

Além do template é possível utilizar as classes do tipo Repositório, que por sua vez, são interfaces no qual possui métodos comuns além de ser possível adicionar os seus métodos e o JNoSQL se encarregará de implementá-los.

public interface HeroRepository extends Repository<Hero, String> {

    Optional<Hero> findByName(String name);

    Stream<Hero> findByAgeGreaterThan(Integer age);

    Stream<Hero> findByAgeLessThan(Integer age);

    void deleteByName(String name);

}

Mais uma vez, levando em consideração a diversidade, existe a especialização do repositório para o Couchbase, a interface CouchbaseRepository. Essa interface tem como principal recurso a execução do N1QL.

 

public interface HeroRepository extends CouchbaseRepository<Hero, String> {

    Optional<Hero> findByName(String name);

    Stream<Hero> findByAgeGreaterThan(Integer age);

    Stream<Hero> findByAgeLessThan(Integer age);

    void deleteByName(String name);

    @N1QL("select * from heroes where realName= $realName")
    Optional<Hero> find(@Param("realName") String realName);

}

public class App2 {



    public static void main(String[] args) {

        try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {
            Hero ironMan = Hero.builder().withRealName("Tony Stark").withName("iron_man")
                    .withAge(34).withPowers(Collections.singleton("rich")).build();

            HeroRepository repository = container.select(HeroRepository.class).get();
            repository.save(ironMan);

            System.out.println(repository.findByName("iron_man"));
            System.out.println(repository.find("Tony Stark"));

        }
    }

    private App2() {
    }
}```
<h3>Chave valor</h3>
Utilizando a API de chave-valor é possível utilizar as estruturas especiais, a partir da chave. Nesse caso, será utilizando uma List e um Set de String e assim como no documento, existe a classe template para chave valor.


```java
@ApplicationScoped
public class VillainService {

    @Inject
    private List<String> names;

    @Inject
    private Set<String> powers;

    @Inject
    private KeyValueTemplate template;


    public void addName(String hero) {
        names.add(hero);
    }

    public void addPower(String ids) {
        this.powers.add(ids);
    }

    public void put(Villain villain) {
        template.put(villain);
    }

    public Optional<Villain> get(String name) {
        return template.get(name, Villain.class);
    }

    public List<String> getNames() {
        return names;
    }

    public Set<String> getPowers() {
        return powers;
    }
}


public class App3 {


    public static void main(String[] args) {

        try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {
            Villain lock = new Villain();
            lock.setId("lock");
            lock.setName("Lock");

            VillainService service = container.select(VillainService.class).get();


            service.addName("Doctor Doom");
            service.addName("Magneto");
            service.addName("Red Skull");

            service.addPower("Strong");
            service.addPower("Strong");
            service.addPower("fly");

            service.put(lock);

            System.out.println(service.get("lock"));
            System.out.println("The villain powers");
            service.getPowers().forEach(System.out::println);
            System.out.println("The villain names");
            service.getNames().forEach(System.out::println);

        }
    }

    private App3() {
    }
}

A opção de repositório para chave valor também está disponível, porém, como os recursos de busca num banco chave valor são limitados, o recurso de query method em chave valor não é suportado.

public interface VillainRepository extends Repository<Villain, String> {
}

 

Porém, uma vez com a classe repositória definida é possível utilizá-la como documento ou como chave-valor.

 

@Inject
@Database(DatabaseType.COLUMN)
private VillainRepository villainRepository;

@Inject
@Database(DatabaseType.KEY_VALUE)
private VillainRepository villainRepository;

public class App4 {


    public static void main(String[] args) {

        try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {
            Villain lock = new Villain();
            lock.setId("lock");
            lock.setName("Lock");

            Villain doom = new Villain();
            doom.setId("doom");
            doom.setName("Dc Doom");

            VillainRepository repository = container.select(VillainRepository.class, DatabaseQualifier.ofKeyValue()).get();

            repository.save(lock);
            repository.save(doom);
            System.out.println(repository.findById("lock"));
            System.out.println(repository.findById("doom"));

        }
    }

    private App4() {
    }
}


 

 

Com isso foram apresentados os recurso do Couchbase, um banco de dados NoSQL que suporta dois tipos de bancos NoSQL, chave valor e documentos, além de ter uma incrível e rica interface visual na qual é possível fazer diversos tipos de configurações. Também foi possível demonstrar o uso desses recursos em parceria com o JNoSQL.

 

Demo: https://github.com/JNOSQL/artemis-demo/tree/master/artemis-demo-java-se/couchbase
Couchbase: https://www.couchbase.com/

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s