Na semana passada, enquanto eu estava no JFokus, conheci Matti Tahvonen, ele trabalha na Vaadin. Eles vêm propondo uma estrutura da Web de código aberto para aplicativos avançados de Internet em Java há anos e fazem isso muito bem. Pessoalmente, fico muito feliz em escrever um aplicativo moderno da Web completo apenas em Java.
Levamos 10 minutos para ter uma amostra funcional do Vaadin CRUD armazenando objetos no Couchbase. O resultado está disponível em Github. Desde então, também migrei uma amostra baseada em JPA, também disponível aqui. Você pode ver como é necessário muito pouco trabalho e como é fácil passar do JPA para o Couchbase com o diferença.
Spring Data Couchbase e Vaadin
Gerar o projeto
A primeira etapa ao iniciar um projeto do Spring é ir para Inicialização do Spring. Aqui você pode selecionar a versão e as dependências que deseja para o seu projeto. Selecione Spring Boot versão 1.4.0 (SNAPSHOT) e adicione Vaadin e Couchbase como dependências.

Agora você pode gerar o projeto e importá-lo como um projeto Maven no editor de sua preferência.
CRUD básico de entidade de pessoa
Esse exemplo de CRUD vai armazenar Cliente objetos. Um cliente tem um id, a firstName e um lastName. Além disso, o sobrenome não pode ser nulo. Para expressar isso como uma entidade, você só precisa adicionar a variável @Documento na classe, @Id no campo a ser usado como chave do Couchbase, gere getters e setters e pronto. Para expressar as restrições não nulas, podemos simplesmente usar as anotações de validação Java @NotNull. Para garantir que isso seja detectado ao escrever a entidade, precisaremos declarar um bean validador.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
package hello; import java.util.Objects; import java.util.UUID; import javax.validation.constraints.NotNull; import org.springframework.data.couchbase.core.mapping.Document; import com.couchbase.client.java.repository.annotation.Id; @Document public class Customer { @Id private String id = UUID.randomUUID().toString(); private String firstName; @NotNull private String lastName; protected Customer() { } public Customer(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } @Override public String toString() { return String.format("Customer[id=%s, firstName='%s', lastName='%s']", id, firstName, lastName); } @Override public int hashCode() { int hash = 7; hash = 37 * hash + Objects.hashCode(this.id); return hash; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Customer other = (Customer) obj; return Objects.equals(this.id, other.id); } } |
O repositório de clientes
Depois de definir uma entidade, você precisa criar o repositório associado. Crie uma interface que estenda a interface CouchbasePagingAndSortingRepository. Esse repositório lida com Cliente com uma String como chave.
Eu redefini o findAll para retornar um método Lista em vez de um Iterável pois ele funciona melhor com as estruturas Vaadin. O findAll é apoiado por uma visualização. Para que suas exibições sejam definidas automaticamente, você pode adicionar a opção @ViewIndexed anotação. Você também precisa se certificar de que definiu a anotação spring.data.couchbase.auto-index para true em sua propriedade application.properties arquivo.
Também adicionei um findByLastName(String lastName) method. Com base no nome do método, a consulta N1QL apropriada será gerada automaticamente. Mas para executar a consulta N1Ql, você precisa de um índice primário. Que também pode ser gerado automaticamente por meio do método @N1QLPrimaryIndexed anotação.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package hello; import java.util.List; import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed; import org.springframework.data.couchbase.core.query.ViewIndexed; import org.springframework.data.couchbase.repository.CouchbasePagingAndSortingRepository; @ViewIndexed(designDoc = "customer") @N1qlPrimaryIndexed public interface CustomerRepository extends CouchbasePagingAndSortingRepository<Customer, String> { List<Customer> findAll(); List<Customer> findByLastName(String lastName); } |
Configuração
Estou usando o spring spring-boot-starter-data-couchbase. Ele oferece autoconfiguração. Essa autoconfiguração pode ser ativada definindo o parâmetro spring.couchbase.bootstrap-hosts propriedade. Até o momento, minha application.properties se parece com isso:
|
1 2 3 |
spring.couchbase.bootstrap-hosts=localhost spring.data.couchbase.auto-index=true |
Criar um cliente
Agora tenho tudo o que preciso para salvar um Cliente Entidade no Couchbase. Podemos tentar isso facilmente com um CommandLineRunner:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
@SpringBootApplication public class Application { private static final Logger log = LoggerFactory.getLogger(Application.class); public static void main(String[] args) { SpringApplication.run(Application.class); } @Bean public CommandLineRunner loadData(CustomerRepository repository) { return (args) -> { repository.save(new Customer(null, "Dessler")); }; } @Bean public Validator validator() { return new LocalValidatorFactoryBean(); } } |
Você perceberá que também adicionei o bean validador mencionado anteriormente. Ele usa um ValidatingCouchbaseEventListener declarado automaticamente pela configuração automática do Spring Boot.
Usando o Vaadin para a interface do usuário
O backend está pronto, podemos começar a pensar no frontend. Quero um aplicativo CRUD básico que mostre uma lista de clientes, com a capacidade de adicionar, editar ou remover elementos da lista. Aqui está uma captura de tela:

Para construir isso, começamos criando um formulário que permite ao usuário inserir um nome e um sobrenome. Crie uma classe que estenda uma classe Formulário abstrato de Cliente. Essa classe não está disponível no Vaadin Core, portanto, precisamos adicionar Viritina.
O Viritin é uma biblioteca de aprimoramento do lado do servidor para o Vaadin. Ela corrige alguns padrões ruins na estrutura principal e fornece uma API mais fluente e inteligente para os componentes existentes. Também oferece vários aprimoramentos importantes para a vinculação de dados e fornece componentes completamente novos feitos com a composição do lado do servidor (não é necessário widgetset).
E sim, ele fornece o Formulário abstrato que é perfeitamente integrada ao Spring Data e aos validadores. Precisamos editar a classe firstName e lastName campos do Cliente portanto, definimos dois campos de texto chamados firstName e lastName. Eles precisam ter o mesmo nome do campo Customer. O que também é excelente nesse componente é o fato de que ele selecionará a anotação de validação em sua entidade. Dessa forma, você obtém validação automática no cliente e no servidor. E ele é compatível com anotações mais complexas do que todas as outras.@NotNull como @Tamanho ou @Padrão.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package hello; import org.vaadin.viritin.form.AbstractForm; import org.vaadin.viritin.layouts.MFormLayout; import com.vaadin.spring.annotation.SpringComponent; import com.vaadin.spring.annotation.UIScope; import com.vaadin.ui.Component; import com.vaadin.ui.TextField; @SpringComponent @UIScope public class CustomerEditor extends AbstractForm<Customer> { /* Fields to edit properties in Customer entity */ TextField firstName = new TextField("First name"); TextField lastName = new TextField("Last name"); public CustomerEditor() { setVisible(false); } @Override protected Component createContent() { return new MFormLayout(firstName, lastName, getToolbar()); } } |
Agora que o formulário está pronto, podemos criar a interface do usuário completa exibindo a grade da tabela. Esse será o principal componente de seu aplicativo Vaadin, a página principal da Web. Como o CustomerRepository e o Editor de clientes são Spring beans, podemos injetá-los diretamente no construtor. Se você estiver familiarizado com a escrita da interface do usuário Java, o código comentado abaixo deve ser simples.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
package hello; import org.springframework.beans.factory.annotation.Autowired; import org.vaadin.viritin.fields.MTable; import org.vaadin.viritin.layouts.MVerticalLayout; import com.vaadin.annotations.Theme; import com.vaadin.server.FontAwesome; import com.vaadin.server.VaadinRequest; import com.vaadin.spring.annotation.SpringUI; import com.vaadin.ui.Button; import com.vaadin.ui.UI; @SpringUI @Theme("valo") public class VaadinUI extends UI { private final CustomerRepository repo; private final CustomerEditor editor; private final MTable<Customer> grid; private final Button addNewBtn; @Autowired public VaadinUI(CustomerRepository repo, CustomerEditor editor) { this.repo = repo; this.editor = editor; this.grid = new MTable<>(Customer.class).withProperties("id", "firstName", "lastName").withHeight("300px"); this.addNewBtn = new Button("New customer", FontAwesome.PLUS); } @Override protected void init(VaadinRequest request) { // Connect selected Customer to editor or hide if none is selected grid.addMValueChangeListener(e -> { if (e.getValue() == null) { editor.setVisible(false); } else { editor.setEntity(e.getValue()); } }); // Instantiate and edit new Customer the new button is clicked addNewBtn.addClickListener(e -> editor.setEntity(new Customer("", ""))); // Listen changes made by the editor, refresh data from backend editor.setSavedHandler(customer -> { repo.save(customer); listCustomers(); editor.setVisible(false); }); editor.setResetHandler(customer -> { editor.setVisible(false); listCustomers(); }); editor.setDeleteHandler(customer -> { repo.delete(customer); listCustomers(); }); // Initialize listing listCustomers(); // build layout setContent(new MVerticalLayout(addNewBtn, grid, editor)); } private void listCustomers() { grid.setBeans(repo.findAll()); } } |
E então está tudo pronto, tudo o que você precisa fazer é executar isso como um aplicativo Java Spring Boot normal. Então, foi bem fácil, não foi?