Guia do CockroachDB em Java

Guia do CockroachDB em Java

1. Introdução

Este tutorial é um guia introdutório para usar o CockroachDB com Java.

Explicaremos os principais recursos, como configurar um cluster local e como monitorá-lo,along with a practical guide on how we can use Java to connect and interact with the server.

Vamos começar primeiro definindo o que é.

2. CockroachDB

O CockroachDB é um banco de dados SQL distribuído, construído sobre um armazenamento de valores-chave transacional e consistente.

Escrito em Go e totalmente de código aberto,its primary design goals are the support for ACID transactions, horizontal scalability, and survivability. Com esses objetivos de design, ele visa tolerar tudo, desde uma única falha de disco até uma falha de datacenter inteiro com interrupção de latência mínima e sem intervenção manual.

Como resultado,CockroachDB can be considered a well-suited solution for applications that require reliable, available, and correct data regardless of scale. No entanto, não é a primeira escolha quando leituras e gravações de latência muito baixa são críticas.

2.1. Características principais

Vamos continuar explorando alguns dos principais aspectos do CockroachDB:

  • SQL API and PostgreSQL compatibility - para estruturar, manipular e consultar dados

  • ACID transactions - suporta transações distribuídas e fornece consistência forte

  • Cloud-ready - projetado para ser executado na nuvem ou em uma solução local, proporcionando fácil migração entre diferentes provedores de nuvem sem qualquer interrupção de serviço

  • Scales horizontally - adicionar capacidade é tão fácil quanto apontar um novo nó no cluster em execução com sobrecarga mínima do operador

  • Replication - replica os dados para disponibilidade e garante a consistência entre as réplicas

  • Automated repair - continua sem problemas, desde que a maioria das réplicas permaneça disponível para falhas de curto prazo enquanto, para falhas de longo prazo, reequilibra automaticamente as réplicas dos nós ausentes, usando as réplicas não afetadas como fontes

3. Configurando o CockroachDB

Depois de termosinstalled CockroachDB, podemos iniciar o primeiro nó de nosso cluster local:

cockroach start --insecure --host=localhost;

Para fins de demonstração, estamos usando o atributoinsecure, tornando a comunicação não criptografada, sem a necessidade de especificar o local dos certificados.

Neste ponto, nosso cluster local está em funcionamento. Com apenas um único nó, já podemos nos conectar a ele e operar, masto better take advantages of CockroachDB’s automatic replication, rebalancing, and fault tolerance, we’ll add two more nodes:

cockroach start --insecure --store=node2 \
  --host=localhost --port=26258 --http-port=8081 \
  --join=localhost:26257;

cockroach start --insecure --store=node3 \
  --host=localhost --port=26259 --http-port=8082 \
  --join=localhost:26257;

Para os dois nós adicionais, usamos o sinalizadorjoin para conectar os novos nós ao cluster, especificando o endereço e a porta do primeiro nó, em nosso caso localhost: 26257. Each node on the local cluster requires unique store, port, and http-port values.

Ao configurar um cluster distribuído de CockroachDB, cada nó estará em uma máquina diferente e, portanto, a especificação deport,storeehttp-port pode ser evitada, pois os valores padrão são suficientes. Além disso, o IP real do primeiro nó deve ser usado ao associar os nós adicionais ao cluster.

3.1. Configurando Banco de Dados e Usuário

Uma vez que nosso cluster esteja em funcionamento, por meio do console SQL fornecido com o CockroachDB, precisamos criar nosso banco de dados e um usuário.

Em primeiro lugar, vamos iniciar o console SQL:

cockroach sql --insecure;

Agora, vamos criar nosso banco de dadostestdb, criar um usuário e adicionar concessões ao usuário para poder realizar operações CRUD:

CREATE DATABASE testdb;
CREATE USER user17 with password 'qwerty';
GRANT ALL ON DATABASE testdb TO user17;

Se quisermos verificar se o banco de dados foi criado corretamente, podemos listar todos os bancos de dados criados no nó atual:

SHOW DATABASES;

Por fim, se quisermos verificar o recurso de replicação automática do CockroachDB, podemos verificar um dos outros dois nós se o banco de dados foi criado corretamente. Para fazer isso, temos que expressar a sinalizaçãoport quando estivermos usando o console SQL:

cockroach sql --insecure --port=26258;

4. Monitorando CockroachDB

Agora que iniciamos nosso cluster local e criamos o banco de dados,we can monitor them using the CockroachDB Admin UI:

image

Esta UI Admin, que vem em um pacote com o CockroachDB, pode ser acessada emhttp://localhost:8080 assim que o cluster estiver instalado e funcionando. Em particular,it provides details about cluster and database configuration, and helps us optimize cluster performance by monitoring metrics like:

  • Cluster Health - métricas essenciais sobre a saúde do cluster

  • Runtime Metrics - métricas sobre contagem de nós, tempo de CPU e uso de memória

  • SQL Performance - métricas sobre conexões SQL, consultas e transações

  • Replication Details - métricas sobre como os dados são replicados no cluster

  • Node Details - detalhes de nós vivos, mortos e desativados

  • Database Details - detalhes sobre o sistema e bancos de dados do usuário no cluster

5. Project Setup

Dado nosso cluster local em execução do CockroachDB, para podermos nos conectar a ele, temos que adicionar umadditional dependency ao nossopom.xml:


    org.postgresql
    postgresql
    42.1.4

Ou, para um projeto Gradle:

compile 'org.postgresql:postgresql:42.1.4'

6. Usando CockroachDB

Agora que está claro com o que estamos trabalhando e que tudo está configurado corretamente, vamos começar a usá-lo.

Graças à compatibilidade do PostgreSQL,it’s either possible to connect directly with JDBC or using an ORM, such as Hibernate (no momento da escrita (janeiro de 2018) ambos os drivers foram testados o suficiente para reivindicar o suportebeta-level de acordo com os desenvolvedores). Em nosso caso, usaremos JDBC para interagir com o banco de dados.

Para simplificar, seguiremos com as operações CRUD básicas, pois são as melhores para começar.

Vamos começar conectando-nos ao banco de dados.

6.1. Conectando-se ao CockroachDB

Para abrir uma conexão com o banco de dados, podemos usar o métodogetConnection() da classeDriverManager. Este método requer um parâmetro de URL de conexãoString, um nome de usuário e uma senha:

Connection con = DriverManager.getConnection(
  "jdbc:postgresql://localhost:26257/testdb", "user17", "qwerty"
);

6.2. Criando uma tabela

Com uma conexão funcionando, podemos começar a criar a tabelaarticles que usaremos para todas as operações CRUD:

String TABLE_NAME = "articles";
StringBuilder sb = new StringBuilder("CREATE TABLE IF NOT EXISTS ")
  .append(TABLE_NAME)
  .append("(id uuid PRIMARY KEY, ")
  .append("title string,")
  .append("author string)");

String query = sb.toString();
Statement stmt = connection.createStatement();
stmt.execute(query);

Se quisermos verificar se a tabela foi criada corretamente, podemos usar o comandoSHOW TABLES:

PreparedStatement preparedStatement = con.prepareStatement("SHOW TABLES");
ResultSet resultSet = preparedStatement.executeQuery();
List tables = new ArrayList<>();
while (resultSet.next()) {
    tables.add(resultSet.getString("Table"));
}

assertTrue(tables.stream().anyMatch(t -> t.equals(TABLE_NAME)));

Vamos ver como é possível modificar a tabela recém-criada.

6.3. Alterando uma tabela

Se perdemos algumas colunas durante a criação da tabela ou porque precisamos delas mais tarde, podemos adicioná-las facilmente:

StringBuilder sb = new StringBuilder("ALTER TABLE ").append(TABLE_NAME)
  .append(" ADD ")
  .append(columnName)
  .append(" ")
  .append(columnType);

String query = sb.toString();
Statement stmt = connection.createStatement();
stmt.execute(query);

Depois de alterar a tabela, podemos verificar se a nova coluna foi adicionada usando o comandoSHOW COLUMNS FROM:

String query = "SHOW COLUMNS FROM " + TABLE_NAME;
PreparedStatement preparedStatement = con.prepareStatement(query);
ResultSet resultSet = preparedStatement.executeQuery();
List columns = new ArrayList<>();
while (resultSet.next()) {
    columns.add(resultSet.getString("Field"));
}

assertTrue(columns.stream().anyMatch(c -> c.equals(columnName)));

6.4. Excluindo uma tabela

Ao trabalhar com tabelas, às vezes precisamos excluí-las e isso pode ser facilmente alcançado com poucas linhas de código:

StringBuilder sb = new StringBuilder("DROP TABLE IF EXISTS ")
  .append(TABLE_NAME);

String query = sb.toString();
Statement stmt = connection.createStatement();
stmt.execute(query);

6.5. Inserindo dados

Depois de limpar as operações que podem ser executadas em uma tabela, agora podemos começar a trabalhar com dados. Podemos começar a definir a classeArticle:

public class Article {

    private UUID id;
    private String title;
    private String author;

    // standard constructor/getters/setters
}

Agora podemos ver como adicionar umArticle à nossa tabelaarticles:

StringBuilder sb = new StringBuilder("INSERT INTO ").append(TABLE_NAME)
  .append("(id, title, author) ")
  .append("VALUES (?,?,?)");

String query = sb.toString();
PreparedStatement preparedStatement = connection.prepareStatement(query);
preparedStatement.setString(1, article.getId().toString());
preparedStatement.setString(2, article.getTitle());
preparedStatement.setString(3, article.getAuthor());
preparedStatement.execute();

6.6. Lendo dados

Depois que os dados são armazenados em uma tabela, queremos ler esses dados e isso pode ser facilmente alcançado:

StringBuilder sb = new StringBuilder("SELECT * FROM ")
  .append(TABLE_NAME);

String query = sb.toString();
PreparedStatement preparedStatement = connection.prepareStatement(query);
ResultSet rs = preparedStatement.executeQuery();

No entanto, se não quisermos ler todos os dados dentro da tabelaarticles, mas apenas umArticle, podemos simplesmente mudar a forma como construímos nossoPreparedStatement:

StringBuilder sb = new StringBuilder("SELECT * FROM ").append(TABLE_NAME)
  .append(" WHERE title = ?");

String query = sb.toString();
PreparedStatement preparedStatement = connection.prepareStatement(query);
preparedStatement.setString(1, title);
ResultSet rs = preparedStatement.executeQuery();

6.7. Exclusão de dados

Por último, mas não menos importante, se quisermos remover dados de nossa tabela, podemos excluir um conjunto limitado de registros usando o comandoDELETE FROM padrão:

StringBuilder sb = new StringBuilder("DELETE FROM ").append(TABLE_NAME)
  .append(" WHERE title = ?");

String query = sb.toString();
PreparedStatement preparedStatement = connection.prepareStatement(query);
preparedStatement.setString(1, title);
preparedStatement.execute();

Ou podemos deletar todos os registros, dentro da tabela, com a funçãoTRUNCATE:

StringBuilder sb = new StringBuilder("TRUNCATE TABLE ")
  .append(TABLE_NAME);

String query = sb.toString();
Statement stmt = connection.createStatement();
stmt.execute(query);

6.8. Tratamento de transações

Uma vez conectado ao banco de dados, por padrão, cada instrução SQL individual é tratada como uma transação e é automaticamente confirmada logo após a conclusão da execução.

No entanto, se queremos permitir que duas ou mais instruções SQL sejam agrupadas em uma única transação, precisamos controlar a transação programaticamente.

Primeiro, precisamos desabilitar o modo de auto-commit definindo a propriedadeautoCommit deConnection parafalse, então use os métodoscommit()erollback() para controlar a transação.

Vamos ver como podemos alcançar consistência de dados ao fazer várias inserções:

try {
    con.setAutoCommit(false);

    UUID articleId = UUID.randomUUID();

    Article article = new Article(
      articleId, "Guide to CockroachDB in Java", "example"
    );
    articleRepository.insertArticle(article);

    article = new Article(
      articleId, "A Guide to MongoDB with Java", "example"
    );
    articleRepository.insertArticle(article); // Exception

    con.commit();
} catch (Exception e) {
    con.rollback();
} finally {
    con.setAutoCommit(true);
}

Neste caso, uma exceção foi lançada, no segundo insert, para a violação da restrição de chave primária e, portanto, nenhum artigo foi inserido na tabelaarticles.

7. Conclusão

Neste artigo, explicamos o que é o CockroachDB, como configurar um cluster local simples e como podemos interagir com ele a partir do Java.

O código-fonte completo deste artigo pode ser encontrado, como sempre,over on Github.