La semana pasada mientras estaba en JFokus conocí a Matti Tahvonentrabaja en Vaadin. Llevan años proponiendo un marco web de código abierto para aplicaciones ricas de Internet en Java y lo hacen realmente bien. Personalmente estoy muy contento de escribir una aplicación web moderna completa sólo en Java.
Hemos tardado 10 minutos en tener un ejemplo de Vaadin CRUD funcionando almacenando objetos en Couchbase. El resultado está disponible en Github. Desde entonces también he migrado una muestra basada en JPA también disponible aquí. Puedes ver el poco trabajo que requiere y lo fácil que es pasar de JPA a Couchbase con la herramienta dif.
Spring Data Couchbase con Vaadin
Generar el proyecto
El primer paso al iniciar un proyecto Spring es ir a Inicialización de muelles. Aquí puedes seleccionar la versión y las dependencias que quieres para tu proyecto. Selecciona Spring Boot versión 1.4.0(SNAPSHOT) y añade Vaadin y Couchbase como dependencias.

Ahora puede generar el proyecto e importarlo como un proyecto Maven en el editor de su elección.
Persona básica Entidad CRUD
Este ejemplo CRUD va a almacenar Cliente objetos. Un cliente tiene un id, a firstName y un apellido. Además, el apellido no debe ser nulo. Para expresar esto como una entidad, basta con añadir el atributo @Documento en la clase, @Id en el campo que se utilizará como clave de Couchbase, generar getters y setters y ya está. Para expresar las restricciones not null, podemos simplemente usar las anotaciones de validación de Java @NotNull. Para asegurarnos de que se recoge al escribir la entidad, tendremos que declarar un 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); } } |
Depósito de clientes
Una vez definida una entidad, hay que crear el repositorio asociado. Cree una interfaz que extienda la interfaz CouchbasePagingAndSortingRepository. Este repositorio gestiona Cliente con una cadena como clave.
He redefinido el findAll para devolver un Lista en lugar de un Iterable ya que funciona mejor con las estructuras de Vaadin. El findAll está respaldado por una Vista. Para que sus vistas se definan automáticamente, puede añadir el método @VerIndexado anotación. También debe asegurarse de que ha establecido el parámetro spring.data.couchbase.auto-index a true en su aplicación.propiedades archivo.
También he añadido un findByLastName(String apellido) método. En función del nombre del método, se generará automáticamente la consulta N1QL adecuada. Pero para ejecutar la consulta N1Ql, se necesita un índice primario. Que también se puede generar automáticamente a través de la @N1QLPrimaryIndexed anotación.
|
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); } |
Configuración
Estoy utilizando spring-boot-starter-data-couchbase. Proporciona autoconfiguración. Esta autoconfiguración se puede activar configurando el parámetro spring.couchbase.bootstrap-hosts propiedad. Hasta ahora mi aplicación.propiedades se ve así:
|
1 2 3 |
spring.couchbase.bootstrap-hosts=localhost spring.data.couchbase.auto-index=true |
Crear un cliente
Ahora tengo todo lo que necesito para guardar un Cliente Entity en Couchbase. Podemos probar esto fácilmente con un 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(); } } |
Verás que también he añadido el bean validador mencionado anteriormente. Utiliza un ValidatingCouchbaseEventListener declarado automáticamente por la configuración automática de Spring Boot.
Uso de Vaadin para la interfaz de usuario
El backend está listo, podemos empezar a pensar en el frontend. Quiero una aplicación básica CRUD que mostrará una lista de clientes, la posibilidad de añadir, editar o eliminar elementos de la lista. Aquí's una captura de pantalla:

Para construir esto empezamos creando un formulario que permita al usuario introducir un nombre y un apellido. Crea una clase que extienda un Formulario abstracto de Cliente. Esta clase no está disponible en Vaadin Core, por lo que tenemos que añadir Viritin.
Viritin es una librería de mejora del lado del servidor para Vaadin. Corrige algunos defectos en el núcleo del framework y proporciona una API más fluida e inteligente para los componentes existentes. También proporciona varias mejoras importantes para la vinculación de bases de datos y proporciona componentes completamente nuevos hechos con la composición del lado del servidor (no se necesita widgetset).
Y sí proporciona la Formulario abstracto que está perfectamente integrada con Spring Data y los validadores. Necesitamos editar la clase firstName y apellido campos del Cliente por lo que definimos dos campos de texto llamados firstName y apellido. Deben tener el mismo nombre que su campo Cliente. Lo que también es bueno de este componente es que recogerá la anotación de validación en su entidad. De esta manera se obtiene la validación automática en el cliente y en el servidor. Y soporta anotaciones más complejas que el@NotNull como Tamaño o @Patrón.
|
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()); } } |
Ahora que el formulario está listo podemos construir la UI completa mostrando la rejilla de la tabla. Este será el componente principal de su aplicación Vaadin, la página web principal. Dado que la Depósito de clientes y el ClienteEditor son beans de Spring, podemos inyectarlos directamente en el constructor. Si usted está familiarizado con la escritura de Java UI, el código comentado a continuación debe ser sencillo.
|
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()); } } |
Y entonces usted está todo listo, todo lo que tienes que hacer es ejecutar esto como una aplicación Java Spring Boot habitual. Así que fue bastante fácil ¿eh?