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 |
pacote Olá; importação java.util.Objetos; importação java.util.UUID; importação javax.validação.restrições.Não nulo; importação org.estrutura de mola.dados.couchbase.núcleo.mapeamento.Documento; importação com.couchbase.cliente.java.repositório.anotação.Id; @Documento público classe Cliente { @Id privado Cordas id = UUID.UUUID aleatório().toString(); privado Cordas firstName; @Não nulo privado Cordas lastName; protegida Cliente() { } público Cliente(Cordas firstName, Cordas lastName) { este.firstName = firstName; este.lastName = lastName; } público Cordas getId() { retorno id; } público vazio setId(Cordas id) { este.id = id; } público Cordas getFirstName() { retorno firstName; } público vazio setFirstName(Cordas firstName) { este.firstName = firstName; } público Cordas getLastName() { retorno lastName; } público vazio setLastName(Cordas lastName) { este.lastName = lastName; } @Substituir público Cordas toString() { retorno Cordas.formato("Customer[id=%s, firstName='%s', lastName='%s']", id, firstName, lastName); } @Substituir público int hashCode() { int hash = 7; hash = 37 * hash + Objetos.hashCode(este.id); retorno hash; } @Substituir público booleano iguais(Objeto obj) { se (este == obj) { retorno verdadeiro; } se (obj == nulo) { retorno falso; } se (getClass() != obj.getClass()) { retorno falso; } final Cliente outros = (Cliente) obj; retorno Objetos.iguais(este.id, outros.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 |
pacote Olá; importação java.util.Lista; importação org.estrutura de mola.dados.couchbase.núcleo.consulta.N1qlPrimaryIndexed; importação org.estrutura de mola.dados.couchbase.núcleo.consulta.ViewIndexed; importação org.estrutura de mola.dados.couchbase.repositório.CouchbasePagingAndSortingRepository; @ViewIndexed(designDoc = "cliente") @N1qlPrimaryIndexed público interface CustomerRepository se estende CouchbasePagingAndSortingRepository<Cliente, Cordas> { Lista<Cliente> findAll(); Lista<Cliente> findByLastName(Cordas 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 |
mola.couchbase.bootstrap-anfitriões=localhost mola.dados.couchbase.automático-índice=verdadeiro |
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 público classe Aplicativo { privado estático final Registrador registro = Fábrica de registradores.getLogger(Aplicativo.classe); público estático vazio principal(Cordas[] argumentos) { SpringApplication.executar(Aplicativo.classe); } @Feijão público CommandLineRunner loadData(CustomerRepository repositório) { retorno (argumentos) -> { repositório.salvar(novo Cliente(nulo, "Dessler")); }; } @Feijão público Validador validador() { retorno novo 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 |
pacote Olá; importação org.vaadin.viritina.formulário.Formulário abstrato; importação org.vaadin.viritina.layouts.MFormLayout; importação com.vaadin.mola.anotação.Componente de mola; importação com.vaadin.mola.anotação.UIScope; importação com.vaadin.ui.Componente; importação com.vaadin.ui.TextField; @Componente de mola @UIScope público classe Editor de clientes se estende Formulário abstrato<Cliente> { /* Campos para editar propriedades na entidade Cliente */ TextField firstName = novo TextField("Primeiro nome"); TextField lastName = novo TextField("Sobrenome"); público Editor de clientes() { setVisible(falso); } @Substituir protegida Componente createContent() { retorno novo 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 |
pacote Olá; importação org.estrutura de mola.feijões.fábrica.anotação.Com fio automático; importação org.vaadin.viritina.campos.Tabela MT; importação org.vaadin.viritina.layouts.MVerticalLayout; importação com.vaadin.anotações.Tema; importação com.vaadin.servidor.FontAwesome; importação com.vaadin.servidor.VaadinRequest; importação com.vaadin.mola.anotação.SpringUI; importação com.vaadin.ui.Botão; importação com.vaadin.ui.IU; @SpringUI @Tema("valo") público classe VaadinUI se estende IU { privado final CustomerRepository repo; privado final Editor de clientes editor; privado final Tabela MT<Cliente> grade; privado final Botão addNewBtn; @Com fio automático público VaadinUI(CustomerRepository repo, Editor de clientes editor) { este.repo = repo; este.editor = editor; este.grade = novo Tabela MT<>(Cliente.classe).withProperties("id", "firstName", "lastName").withHeight("300px"); este.addNewBtn = novo Botão("Novo cliente", FontAwesome.MAIS); } @Substituir protegida vazio inicial(VaadinRequest solicitação) { // Conectar o cliente selecionado ao editor ou ocultar se nenhum estiver selecionado grade.addMValueChangeListener(e -> { se (e.getValue() == nulo) { editor.setVisible(falso); } mais { editor.setEntity(e.getValue()); } }); // Instanciar e editar o novo cliente quando o botão novo for clicado addNewBtn.addClickListener(e -> editor.setEntity(novo Cliente("", ""))); // Ouvir as alterações feitas pelo editor, atualizar os dados do backend editor.setSavedHandler(cliente -> { repo.salvar(cliente); listCustomers(); editor.setVisible(falso); }); editor.setResetHandler(cliente -> { editor.setVisible(falso); listCustomers(); }); editor.setDeleteHandler(cliente -> { repo.excluir(cliente); listCustomers(); }); // Inicializar a listagem listCustomers(); // criar layout setContent(novo MVerticalLayout(addNewBtn, grade, editor)); } privado vazio listCustomers() { grade.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?