En Couchbase Connect 2015 demostramos una aplicación de ejemplo que utiliza N1QL para consultar datos de un bucket de Couchbase de ejemplo.
Si te perdiste la conferencia, no hay problema. Vamos a repasar cómo reproducir esta aplicación y revisar algunos de los aspectos más destacados de Couchbase 4.0.
Requisitos previos
- Apache Maven 3
- Kit de desarrollo de Java (JDK) 1.7
- Servidor Couchbase 4.0
- IntelliJ IDEA 14.1+, Eclipse o NetBeans. En este ejemplo se utilizará IntelliJ IDEA.
Crear un nuevo proyecto
Abra IntelliJ IDEA y elija crear un nuevo proyecto Java, asegurándose de utilizar JDK 1.7 si se le pide. Para los propósitos de esta guía, vamos a llamar al proyecto try-cb-java.
Ahora haz clic con el botón derecho try-cb-java en el árbol de proyectos y, a continuación, seleccione Añadir compatibilidad con marcos y seleccione Maven. Esto añadirá un pom.xml a su proyecto.
Configuración de Maven
Dentro de la pom.xml comience por dar al proyecto un nombre de grupo más atractivo:
1 2 3 |
<groupId>com.couchbase.ejemplo</groupId> |
A continuación, procedemos a añadir el resto de nuestras dependencias al archivo, que incluyen Spring Boot, el cliente Couchbase y el framework de seguridad de Spring.
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 |
<padre> <groupId>org.springframework.arranque</groupId> <artifactId>primavera-arranque-arranque-padre</artifactId> <versión>1.2.3.RELEASE</versión> </padre> <dependencias> <dependencia> <groupId>org.springframework.arranque</groupId> <artifactId>primavera-arranque-arranque-web</artifactId> </dependencia> <dependencia> <groupId>org.springframework</groupId> <artifactId>primavera-tx</artifactId> </dependencia> <dependencia> <groupId>org.springframework.seguridad</groupId> <artifactId>primavera-seguridad-núcleo</artifactId> </dependencia> <dependencia> <groupId>com.couchbase.cliente</groupId> <artifactId>java-cliente</artifactId> <versión>2.2.0-dp</versión> </dependencia> </dependencias> <repositorios> <repositorio> <id>couchbase</id> <nombre>couchbase repo</nombre> <url>http://files.couchbase.com/maven2 <instantáneas><habilitado>falso</habilitado></instantáneas> </repositorio> </repositorios> <construya> <plugins> <plugin> <groupId>org.springframework.arranque</groupId> <artifactId>primavera-arranque-maven-plugin</artifactId> </plugin> </plugins> </construya> |
Creación de un perfil de carrera
En este momento, si intenta ejecutar la aplicación, se producirá un error o no ocurrirá nada porque no hay ningún perfil configurado actualmente.
En la barra de herramientas, seleccione Ejecutar -> Editar configuraciones y elija añadir una nueva configuración de Maven. Puedes ponerle el nombre que quieras, pero es importante tener lo siguiente en el campo de línea de comandos:
1 2 3 |
primavera-arranque:ejecute |
Para este artículo vamos a nombrar la configuración Spring Boot.
IntelliJ IDEA debería estar ahora listo para el desarrollo.
Creación de índices en el cubo Couchbase
Como este tutorial utiliza consultas N1QL, primero debemos añadir índices a nuestro bucket de Couchbase Server. Ahora bien, esto se puede hacer fácilmente a través de código, pero para este ejemplo vamos a atajarlo y añadirlos a través del cliente Couchbase Query (CBQ) que se instala automáticamente con una instalación de Couchbase 4+ en Mac OS y Windows.
En Mac OS, inicie CBQ que se encuentra en /Applications/Couchbase Server.app/Contents/Resources/couchbase-core/bin/cbq y ejecuta lo siguiente:
1 2 3 |
CREAR PRIMARIO ÍNDICE def_primary EN `viaje-muestra` USO DE gsi; |
En Windows, inicie CBQ que se encuentra en C:/Archivos de programa/Couchbase/Server/bin/cbq.exe y ejecute el mismo comando N1QL que en Mac OS.
Creación de una clase principal de aplicación
La clase principal de este proyecto será Aplicación.java y pueden crearse haciendo clic con el botón derecho del ratón en el icono trycb del árbol de proyectos y seleccionando Nuevo -> Clase Java.
Añade lo siguiente para poner la clase en su estado ejecutable más básico:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
paquete trycb; importar org.springframework.arranque.SpringApplication; importar org.springframework.arranque.autoconfigure.SpringBootApplication; importar org.springframework.web.vincular.anotación.RequestMapping; importar org.springframework.web.vincular.anotación.RestController; @SpringBootApplication @RestController @RequestMapping("/api") público clase Aplicación { público estático void principal(Cadena[] args) { SpringApplication.ejecute(Aplicación.clase, args); } } |
Asegúrese de que el proyecto se ejecuta sin errores seleccionando Ejecutar -> Ejecutar 'Spring Boot de la barra de herramientas de IntelliJ IDEA.
Gestión de recursos compartidos entre orígenes (CORS)
Dado que la mayor parte de nuestras pruebas se harán localmente, tenemos que asegurarnos de que CORS está habilitado, de lo contrario el navegador web se va a quejar cuando se trata de golpear a nuestros puntos finales de la API con JavaScript.
Asegúrate de que la clase Aplicación implementa la clase Filtro y añade el siguiente código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@Anular público void doFilter(ServletRequest consulte, ServletResponse res, Cadena de filtros cadena) lanza IOException, ServletException { HttpServletResponse respuesta = (HttpServletResponse) res; respuesta.setHeader("Access-Control-Allow-Origin" (Control de acceso: permitir origen), "*"); respuesta.setHeader("Access-Control-Allow-Headers" (Control de acceso: permitir encabezados), "Origin, X-Requested-With, Content-Type, Accept"); cadena.doFilter(consulte, res); } @Anular público void init(FilterConfig filterConfig) lanza ServletException {} @Anular público void destruir() {} |
Configurar las opciones de cluster y bucket de Couchbase
A partir de ahora tenemos esencialmente una aplicación Spring Boot sin interación con Couchbase. Hemos incluido el cliente Java a través de Maven, así que es hora de empezar a usarlo.
Añade lo siguiente a la clase Aplicación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
@Valor("${hostname}") privado Cadena nombre de host; @Valor("${bucket}") privado Cadena cubo; @Valor("${contraseña}") privado Cadena contraseña; público @Judía Grupo grupo() { devolver CouchbaseCluster.crear(nombre de host); } público @Judía Cubo cubo() { devolver grupo().openBucket(cubo, contraseña); } |
No hemos configurado el nombre de host, cuboy contraseña pero se utilizarán para conectarse a un cluster de Couchbase y a un bucket en particular.
Añadir variables de recursos
Viste que estábamos usando nombre de host, cuboy contraseñaAsí que ahora es el momento de fijarlos.
En el árbol de proyectos de IntelliJ IDEA, haga clic con el botón derecho del ratón en src/main/recursos y elija Nuevo -> Archivo. Nombre del nuevo archivo aplicación.propiedades y añade las siguientes líneas:
1 2 3 |
nombre de host=127.0.0.1 cubo=viaje-muestra contraseña= |
Spring Boot recogerá este aplicación.propiedades para usted. Encontrará más información sobre las propiedades relacionadas con la aplicación en la sección documentación oficial de Spring.
Creación de puntos finales RESTful
Esta aplicación va a estar basada en API por lo que es necesario crear ciertos endpoints:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
@RequestMapping(valor="/aeropuerto/encontrarTodo", método=RequestMethod.GET) público Lista<Mapa<Cadena, Objeto>> aeropuertos(@RequestParam Cadena busque en) { } @RequestMapping(valor="/flightPath/findAll", método=RequestMethod.GET) público Lista<Mapa<Cadena, Objeto>> todos(@RequestParam Cadena de, @RequestParam Cadena a, @RequestParam Cadena dejar) lanza Excepción { } @RequestMapping(valor="/usuario/inicio de sesión", método=RequestMethod.GET) público Objeto inicio de sesión(@RequestParam Cadena usuario, @RequestParam Cadena contraseña) { } @RequestMapping(valor="/usuario/inicio de sesión", método=RequestMethod.POST) público Objeto crearInicio de sesión(@RequestBody Cadena json) { } @RequestMapping(valor="/usuario/vuelos", método=RequestMethod.POST) público Objeto Libro(@RequestBody Cadena json) { } @RequestMapping(valor="/usuario/vuelos", método=RequestMethod.GET) público Objeto reservado(@RequestParam Cadena nombre de usuario) { } |
Esencialmente, tendremos puntos finales para el registro e inicio de sesión de los usuarios, la reserva y búsqueda de vuelos, así como la búsqueda de información sobre vuelos.
La lógica detrás de estos puntos finales aparecerá en otra clase para la limpieza.
Creación de una clase de base de datos
Acabamos de configurar los puntos finales de nuestra aplicación Spring Boot, pero ahora es el momento de echar un vistazo a la lógica detrás de la interacción con la base de datos.
La clase de base de datos para este proyecto será Base de datos.java y pueden crearse haciendo clic con el botón derecho del ratón en el icono trycb del árbol de proyectos de IntelliJ IDEA y seleccionando Nuevo -> Clase Java.
Añade lo siguiente para obtener la clase para un bonito esqueleto de a dónde vamos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
paquete trycb; público clase Base de datos { privado Base de datos() { } público estático Lista<Mapa<Cadena, Objeto>> findAllAirports(final Cubo cubo, final Cadena parámetros) { } público estático Lista<Mapa<Cadena, Objeto>> findAllFlightPaths(final Cubo cubo, Cadena de, Cadena a, Calendario dejar) { } público estático RespuestaEntidad<Cadena> inicio de sesión(final Cubo cubo, final Cadena nombre de usuario, final Cadena contraseña) { } público estático RespuestaEntidad<Cadena> crearInicio de sesión(final Cubo cubo, final Cadena nombre de usuario, final Cadena contraseña) { } privado estático Lista<Mapa<Cadena, Objeto>> extractResultOrThrow(Resultado de la consulta resultado) { } } |
A partir de aquí, vamos a completar cada uno de estos métodos en el orden en que es probable que el usuario interactúe con ellos.
Crear un nuevo usuario
Cuando el usuario emite un POST al /api/usuario/inicio de sesiónse debe llamar a la siguiente función de la base de datos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
público estático RespuestaEntidad<Cadena> crearInicio de sesión(final Cubo cubo, final Cadena nombre de usuario, final Cadena contraseña) { JsonObject datos = JsonObject.crear() .poner("tipo", "usuario") .poner("nombre", nombre de usuario) .poner("contraseña", BCrypt.hashpw(contraseña, BCrypt.gensalt())); JsonDocument doc = JsonDocument.crear("usuario::" + nombre de usuario, datos); pruebe { cubo.insertar(doc); JsonObject responseData = JsonObject.crear() .poner("éxito", verdadero) .poner("datos", datos); devolver nuevo RespuestaEntidad<Cadena>(responseData.toString(), HttpStatus.OK); } captura (Excepción e) { JsonObject responseData = JsonObject.vacío() .poner("éxito", falso) .poner("fracaso", "Se ha producido un error al crear la cuenta") .poner("excepción", e.getMessage()); devolver nuevo RespuestaEntidad<Cadena>(responseData.toString(), HttpStatus.OK); } } |
El nombre de usuario y la contraseña incluidos en la solicitud se añadirán a un objeto JSON y, a continuación, la contraseña se cifrará con la biblioteca Spring BCrypt. Para realizar un seguimiento de los datos de usuario, los nuevos usuarios terminarán en documentos titulados usuario::{USERNAME_HERE}. Utilización de bucket.insert(doc)se intenta insertar los datos en el bucket. Si no se lanzan excepciones, entonces tuvo éxito y se devuelve una respuesta. Si hay una excepción, entonces la inserción falló y se devolverá el error.
Iniciar sesión como usuario existente
Cuando el usuario emite un GET al mismo /api/usuario/inicio de sesión se debe llamar a la siguiente función de base de datos:
1 2 3 4 5 6 7 8 9 10 11 12 |
público estático RespuestaEntidad<Cadena> inicio de sesión(final Cubo cubo, final Cadena nombre de usuario, final Cadena contraseña) { JsonDocument doc = cubo.consiga("usuario::" + nombre de usuario); JsonObject responseContent; si(BCrypt.checkpw(contraseña, doc.contenido().getString("contraseña"))) { responseContent = JsonObject.crear().poner("éxito", verdadero).poner("datos", doc.contenido()); } si no { responseContent = JsonObject.vacío().poner("éxito", falso).poner("fracaso", "Nombre de usuario o contraseña incorrectos"); } devolver nuevo RespuestaEntidad<Cadena>(responseContent.toString(), HttpStatus.OK); } |
Utilizando bucket.get("usuario::" + nombredeusuario) con el nombre de usuario proporcionado, la aplicación Java obtiene el documento del bucket si existe. La librería Spring BCrypt tiene una gran función para comprobar si la contraseña proporcionada coincide o no con la contraseña encriptada que está almacenada. Si lo hace, entonces devuelve un objeto success, en caso contrario devuelve un objeto login failed.
Cómo extraer el resultado de N1QL y hacerlo legible en Java
N1QL devuelve un Resultado de la consulta que puede ser menos deseable si se devuelven datos a un front-end solicitante. Lo que realmente queremos hacer es convertirlo en un objeto Lista objeto.
1 2 3 4 5 6 7 8 9 10 11 12 |
privado estático Lista<Mapa<Cadena, Objeto>> extractResultOrThrow(Resultado de la consulta resultado) { si (!resultado.finalSuccess()) { tirar nuevo DataRetrievalFailureException("Error de consulta: " + resultado.errores()); } Lista<Mapa<Cadena, Objeto>> contenido = nuevo ArrayList<Mapa<Cadena, Objeto>>(); para (Fila de consulta fila : resultado) { contenido.añada(fila.valor().toMap()); } devolver contenido; } |
Esta función será llamada cada vez que se devuelvan datos N1QL.
Encontrar todos los aeropuertos
Ahora vamos a ver algo de la magia detrás de N1QL cuando se trata de buscar aeropuertos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
público estático Lista<Mapa<Cadena, Objeto>> findAllAirports(final Cubo cubo, final Cadena parámetros) { Declaración consulta; AsPath prefijo = seleccione("nombredeaeropuerto").de(i(cubo.nombre())); si (parámetros.longitud() == 3) { consulta = prefijo.donde(x("faa").eq(s(parámetros.toUpperCase()))); } si no si (parámetros.longitud() == 4 && (parámetros.es igual a(parámetros.toUpperCase()) || parámetros.es igual a(parámetros.toLowerCase()))) { consulta = prefijo.donde(x("icao").eq(s(parámetros.toUpperCase()))); } si no { consulta = prefijo.donde(i("nombredeaeropuerto").como(s(parámetros + "%"))); } Resultado de la consulta resultado = cubo.consulta(Consulta.simple(consulta)); devolver extractResultOrThrow(resultado); } |
Puede ver en el código anterior que estamos utilizando la API Fluent con IntelliJ IDEA para crear nuestra consulta N1QL. Esencialmente, si usted fuera a mirar SQL crudo, se vería así:
1 2 3 |
SELECCIONE nombre del aeropuerto DESDE `viaje-muestra` DONDE faa = {{PARÁMETROS}} |
En el ejemplo anterior, {{PARAMS}} representa un aeropuerto como LAX o similar. Por supuesto, siempre que la longitud de los parámetros sea tres.
Encontrar todas las rutas de vuelo
Por último, nos queda el método responsable de encontrar rutas de vuelo:
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 |
público estático Lista<Mapa<Cadena, Objeto>> findAllFlightPaths(final Cubo cubo, Cadena de, Cadena a, Calendario dejar) { Declaración consulta = seleccione(x("faa").como("desdeAeropuerto")) .de(i(cubo.nombre())) .donde(x("nombredeaeropuerto").eq(s(de))) .unión() .seleccione(x("faa").como("toAirport")) .de(i(cubo.nombre())) .donde(x("nombredeaeropuerto").eq(s(a))); Resultado de la consulta resultado = cubo.consulta(Consulta.simple(consulta)); si (!resultado.finalSuccess()) { tirar nuevo DataRetrievalFailureException("Error de consulta: " + resultado.errores()); } Cadena desdeAeropuerto = null; Cadena toAirport = null; para (Fila de consulta fila : resultado) { si (fila.valor().containsKey("desdeAeropuerto")) { desdeAeropuerto = fila.valor().getString("desdeAeropuerto"); } si (fila.valor().containsKey("toAirport")) { toAirport = fila.valor().getString("toAirport"); } } Declaración joinQuery = seleccione("a.nombre", "s.flight", "s.utc", "r.sourceairport", "r.destinoaeropuerto, "r.equipo") .de(i(cubo.nombre()).como("r")) .sin anestesia("r.schedule AS s") .únase a(i(cubo.nombre()).como("a") + " ON KEYS r.airlineid") .donde(x("r.sourceairport").eq(s(desdeAeropuerto)).y(x("r.destinoaeropuerto).eq(s(toAirport))).y(x("s.día").eq(dejar.consiga(Calendario.DÍA_DEL_MES)))) .orderBy(Ordenar.asc("a.nombre")); Resultado de la consulta otroResultado = cubo.consulta(joinQuery); devolver extractResultOrThrow(otroResultado); } |
Estamos haciendo dos consultas N1QL en este método. La primera se puede traducir fácilmente a lo siguiente:
1 2 3 |
SELECCIONE faa AS desdeAeropuerto DESDE `viaje-muestra` DONDE nombre del aeropuerto = {{PARÁMETROS.DESDE}} UNIÓN SELECCIONE faa AS toAirport DESDE `viaje-muestra` DONDE nombre del aeropuerto = {{PARÁMETROS.A}} |
Por supuesto {{PARAMS}} es lo que se pasó a su punto final. En la declaración, estamos combinando los conjuntos de resultados de todos los de aeropuertos y todos los a aeropuertos.
Tras obtener los dos conjuntos de resultados, los recorremos en bucle para asegurarnos de que el a y de ya que, de lo contrario, los asignaremos por defecto a NULL, lo que impedirá que la siguiente consulta se realice correctamente.
La segunda consulta puede traducirse en la siguiente consulta sin procesar:
1 2 3 |
SELECCIONE a.nombre, s.vuelo, s.utc, r.fuenteaeropuerto, r.destinoaeropuerto, r.equipo DESDE `viaje-muestra` AS r UNNEST r.horario AS s ÚNASE A `viaje-muestra` AS a EN TECLAS r.airlineid DONDE r.fuenteaeropuerto = {{A}} Y r.destinoaeropuerto = {{A}} Y s.día = 3 PEDIR POR a.nombre ASC |
Obtenemos la información de los horarios de los vuelos desanidándola del documento JSON y uniéndola a la clave ahora aplanada.
Finalización de las clases de aplicación y base de datos
Ahora tenemos nuestros endpoints y métodos de base de datos, pero no están conectados entre sí. Ha llegado el momento de volver a examinar el Aplicación.java y añadir algo de código a las funciones que hemos creado anteriormente:
1 2 3 4 5 6 7 8 9 10 11 12 |
@RequestMapping(valor="/usuario/inicio de sesión", método= RequestMethod.GET) público Objeto inicio de sesión(@RequestParam Cadena usuario, @RequestParam Cadena contraseña) { devolver Base de datos.inicio de sesión(cubo(), usuario, contraseña); } @RequestMapping(valor="/usuario/inicio de sesión", método=RequestMethod.POST) público Objeto crearInicio de sesión(@RequestBody Cadena json) { JsonObject jsonData = JsonObject.fromJson(json); devolver Base de datos.crearInicio de sesión(cubo(), jsonData.getString("usuario"), jsonData.getString("contraseña")); } |
Se puede ver que los dos estáticos Base de datos desde cada uno de los endpoints relacionados con las cuentas de usuario. El mismo proceso se puede hacer para los otros endpoints que hemos creado previamente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@RequestMapping("/aeropuerto/encontrarTodo") público Lista<Mapa<Cadena, Objeto>> aeropuertos(@RequestParam Cadena busque en) { devolver Base de datos.findAllAirports(cubo(), busque en); } @RequestMapping("/flightPath/findAll") público Lista<Mapa<Cadena, Objeto>> todos(@RequestParam Cadena de, @RequestParam Cadena a, @RequestParam Cadena dejar) lanza Excepción { Calendario calendario = Calendario.getInstance(Local.US); calendario.setTime(FormatoFecha.getDateInstance(FormatoFecha.CORTO, Local.US).analizar(dejar)); devolver Base de datos.findAllFlightPaths(cubo(), de, a, calendario); } |
Comprobación de los puntos finales de la muestra
Hay algunas maneras de probar los endpoints de la aplicación. En este ejemplo vamos a utilizar cURL, pero sin duda puede utilizar Postman para Google Chrome o algo similar.
Con cURL instalado, abra un Terminal o Símbolo del sistema e introduzca lo siguiente:
1 2 3 |
rizo -X GET http://localhost:8080/api/airport/findAll?search=LAX |
El comando cURL anterior golpeará el api/aeropuerto/findAll y pasar un parámetro de search=LAX. Si tiene éxito, usted debe obtener una respuesta de:
1 |
[{"nombredeaeropuerto":"Los Angeles Intl"}] |
El mismo tipo de prueba puede hacerse para cualquier otro punto final.
Conclusión
Acabamos de ver cómo configurar una aplicación de viajes de ejemplo que utiliza Couchbase Server y Spring Boot para Java. Aunque no configuramos un front-end, es muy posible añadir uno usando lenguajes como AngularJS, jQuery o ReactJS.
Este proyecto completo, junto con un front-end AngularJS, puede obtenerse en la página Couchbase Labs GitHub canal.