fondo
SpringBoot lo ha convertido en uno de los marcos de desarrollo web Java más convencionales de la actualidad porque proporciona una variedad de complementos listos para usar. MyBatis es un marco ORM muy liviano y fácil de usar. Redis es una base de datos de valor clave muy general muy general hoy. En el desarrollo web, a menudo lo usamos para almacenar en caché los resultados de la consulta de la base de datos.
Este blog presentará cómo construir rápidamente una aplicación web utilizando SpringBoot y usar MyBatis como nuestro marco ORM. Para mejorar el rendimiento, usamos Redis como el caché de segundo nivel para mybatis. Para probar nuestro código, escribimos pruebas unitarias y utilizamos la base de datos H2 en memoria para generar nuestros datos de prueba. A través de este proyecto, esperamos que los lectores puedan dominar rápidamente las habilidades y las mejores prácticas del desarrollo web moderno de Java.
El código de muestra para este artículo se puede descargar en Github: https://github.com/lovelcp/spring-boot-mybatis-with-redis/tree/master
ambiente
Entorno de desarrollo: Mac 10.11
IDE: IntelliJ 2017.1
JDK: 1.8
Boot de primavera: 1.5.3.llease
Redis: 3.2.9
Mysql: 5.7
Botín de primavera
Crea un nuevo proyecto
Primero, necesitamos inicializar nuestro proyecto Spring-Boot. A través del inicializador de primavera de IntelliJ, es muy fácil crear un nuevo proyecto Spring-Boot. Primero, seleccionamos un nuevo proyecto en IntelliJ:
Luego, en la interfaz para seleccionar dependencias, verifique Web, MyBatis, Redis, MySQL, H2:
Después de que el nuevo proyecto sea exitoso, podemos ver la estructura inicial del proyecto como se muestra en la figura a continuación:
El inicializador de primavera nos ha ayudado a generar automáticamente una clase de inicio: SpringBootMyBatisWithredisApplication. El código de esta clase es muy simple:
@SpringBootApplicationPublic Clase SpringBootMyBatisWithredisApplication {public static void main (string [] args) {springapplication.run (springbootmybatiswithredisapplication.class, args); }}@SpringBootApplication Annotation significa habilitar la función de configuración automática del arranque de Spring. De acuerdo, nuestro esqueleto de proyecto se ha construido con éxito para que los lectores interesados puedan comenzar los resultados a través de IntelliJ.
Crear una nueva interfaz API
A continuación, escribiremos una API web. Supongamos que nuestra ingeniería web es responsable de manejar los productos del comerciante (producto). Necesitamos proporcionar una interfaz GET que devuelva la información del producto en función de la ID del producto y una interfaz PUT que actualiza la información del producto. Primero definimos la clase de producto, que incluye la identificación del producto, el nombre del producto y el precio:
El producto de clase pública implementa serializable {private static final long SerialVersionUid = 14355159952762555188l; ID de larga prudente; nombre de cadena privada; Precio Precio Precio; // getters setters}Entonces necesitamos definir la clase del controlador. Dado que Spring Boot usa Spring MVC como su componente web internamente, podemos desarrollar rápidamente nuestra clase de interfaz a través de la anotación:
@RestController @requestMapping ("/Product") Public Class ProductController {@getMapping ("/{id}") Producto público GetProductInfo (@PathVariable ("Id") Long ProductId) {// TODO Return Null; } @PutMapping ("/{id}") Public Product UpdateProductInfo (@PathVariable ("id") Long ProductID, @RequestBody Product NewProduct) {// TODO return null; }}Presentemos brevemente las funciones de las anotaciones utilizadas en el código anterior:
@RestController: significa que la clase es un controlador y proporciona una interfaz REST, es decir, los valores de todas las interfaces se devuelven en formato JSON. Esta anotación es en realidad una anotación combinada de @Controller y @ResponseBody, que nos facilita desarrollar la API REST.
@RequestMapping, @getMapping, @putMapping: representa la dirección de URL de la interfaz. La anotación @RequestMapping anotada en la clase significa que las URL de todas las interfaces bajo la clase comienzan con /producto. @GetMapping significa que esta es una interfaz HTTP, @putMapping significa que esta es una interfaz HTTP.
@PathVariable, @RequestBody: representa la relación de mapeo de los parámetros. Suponiendo que una solicitud GET accesos /producto /123, la solicitud será procesada por el método GetProductinfo, donde 123 en la URL se asignará al ProductID. Del mismo modo, si se trata de una solicitud de put, el cuerpo solicitado se asignará al objeto NewProduct.
Aquí solo definimos la interfaz, y la lógica de procesamiento real aún no se ha completado, porque la información del producto está presente en la base de datos. A continuación, integraremos mybatis en el proyecto e interactuaremos con la base de datos.
Integración de mybatis
Configurar la fuente de datos
Primero necesitamos configurar nuestra fuente de datos en el archivo de configuración. Usamos MySQL como nuestra base de datos. Aquí usamos YAML como formato de nuestro archivo de configuración. Creamos un nuevo archivo de aplicación.yml en el directorio de recursos:
Primavera:# Configuración de la base de datos DataSource: URL: JDBC: mySQL: // {Your_Host}/{Your_DB} UserName: {Your_Username} contraseña: {your_password} controlador-class-class: org.gjt.mm.mysql.driverDado que Spring Boot tiene la función de configuración automática, no necesitamos crear una nueva clase de configuración de fuente de datos. Spring Boot cargará automáticamente el archivo de configuración y establecerá un grupo de conexión de base de datos en función de la información del archivo de configuración, que es muy conveniente.
El autor recomienda que use YAML como formato de archivo de configuración. XML parece largo y las propiedades no tienen estructura jerárquica. Yaml solo compensa las deficiencias de ambos. Esta es también la razón por la cual Spring Boot admite el formato YAML de forma predeterminada.
Configurar mybatis
Hemos introducido la biblioteca mybatis-spring-boot-starte en pom.xml a través del inicializador de primavera, que automáticamente nos ayudará a inicializar mybatis. Primero, completamos la configuración relevante de MyBatis en Application.yml:
# myBatis Configurar myBatis: # Configurar el nombre del paquete donde la clase de asignación se encuentra tipo-aliases-paquete: com.wooyoo.learning.dao.domain # Configure la ruta donde se encuentra el archivo XML mapper, aquí está un mapeador de matriz:-Mappers/ProductMapper.xml
Luego, defina la clase ProductMapper en el código:
@MapperPublic Interface ProductMapper {Product Select (@Param ("ID") Long ID); Actualización vacía (producto Producto);}Aquí, siempre y cuando agregemos la anotación @mapper, Spring Boot cargará automáticamente la clase Mapper al inicializar myBatis.
La razón principal por la que Spring Boot es tan popular es su función de configuración automática. Los desarrolladores solo deben prestar atención a la configuración de componentes (como la información de conexión de la base de datos) sin preocuparse por cómo inicializar los componentes individuales, lo que nos permite centrarnos en la implementación del negocio y simplificar el proceso de desarrollo.
Acceder a la base de datos
Después de completar la configuración de MyBatis, podemos acceder a la base de datos en nuestra interfaz. Presentamos la clase Mapper a través de @AUTOWIRED en ProductController y llamamos al método correspondiente para implementar las operaciones de consulta y actualización en el producto. Aquí tomamos la interfaz de consulta como ejemplo:
@RestController @requestMapping ("/Product") Public Class ProductController {@aUtowired ProductMapper ProductMapper; @GetMapping ("/{id}") Producto público GetProductInfo (@PathVariable ("id") Long ProductId) {return productMapper.Select (productId); } // Evite demasiado tiempo y omita el código de updateProductinfo}Luego inserte algunas información del producto en su MySQL y puede ejecutar el proyecto para ver si la consulta es exitosa.
Hasta ahora, hemos integrado con éxito mybatis en nuestro proyecto, agregando la capacidad de interactuar con la base de datos. Pero eso no es suficiente. Un proyecto web moderno definitivamente acelerará nuestra consulta de base de datos en caché. A continuación, presentaremos cómo integrar científicamente Redis en el caché secundario de MyBatis para realizar el caché automático de consultas de bases de datos.
Redis integrado
Configurar Redis
Al igual que acceder a una base de datos, necesitamos configurar la información de conexión Redis. Agregue la siguiente configuración al archivo Application.yml:
Spring: Redis: # Redis Database Index (el valor predeterminado es 0). Utilizamos una base de datos con el índice 3 para evitar conflictos con otras bases de datos Base de datos: 3 # Dirección del servidor Redis (el valor predeterminado es localhost) Host: localhost # puerto redis (predeterminado es 6379) puerto: 6379 # contraseña de acceso redis (predeterminado es nulo) Contraseña: # Redis Connection TimeOut (Unidad es MilliseConds) Tiempo de tiempo: 0 # Redis Connection Fun Fun Pool: # Máximo Número de Máximo de la Conexión de la Conexión de la Conexión de la Conexión de la Conexión de la Conexión de la Conexión de la Conexión de la Conexión Número de Máxima. infinito) Máx-activo: 8 # Número máximo de conexiones inactivas (el valor predeterminado es 8, los números negativos representan Infinite) Max-Idle: 8 # Número mínimo de conexiones inactivas (el valor predeterminado es 0, este valor solo es efectivo) Min-Iidle: 0 # Obtenga el tiempo máximo de espera de espera del grupo de conexión (predeterminado es -1, la unidad es Millisegunds, Números negativos indica Infinite) Max-Wait: -1111
Todos los enumerados anteriormente son configuraciones comúnmente utilizadas, y los lectores pueden comprender el papel específico de cada elemento de configuración a través de la información de comentarios. Dado que hemos introducido la biblioteca Spring-Boot-Starter-Data-Redis en Pom.xml, Spring Boot nos ayudará a cargar automáticamente las conexiones Redis y las clases de configuración específicas
org.springframework.boot.autoconfigure.data.redis.redisautoconfiguration. A través de esta clase de configuración, podemos encontrar que la capa subyacente usa la biblioteca JEDIS de forma predeterminada y proporciona redistemplate y stringTemplate fuera del cuadro.
Use Redis como un caché de nivel 2
El principio del almacenamiento en caché secundario de MyBatis no se describirá en este artículo. Los lectores solo necesitan saber que el almacenamiento en caché secundario de MyBatis puede almacenar automáticamente consultas en la base de datos y puede actualizar automáticamente el caché al actualizar los datos.
Implementar el almacenamiento en caché secundario de MyBatis es muy simple. Solo necesita crear una nueva clase para implementar la interfaz org.apache.ibatis.cache.cache.
Hay cinco métodos para esta interfaz:
String getId (): el identificador del objeto de operación de caché mybatis. Un mapeador corresponde a un objeto de operación de caché myBatis.
void putobject (clave de objeto, valor de objeto): rellene los resultados de la consulta en el caché.
Object getObject (clave de objeto): obtenga el resultado de la consulta en caché del caché.
Objeto RemoLEbject (clave de objeto): elimine la clave y el valor correspondiente del caché. Solo disparó al rodar hacia atrás. En general, no necesitamos implementarlo. Para obtener métodos de uso específicos, consulte: org.apache.ibatis.cache.decorators.transactionalcache.
Void Clear (): Clare Cache cuando ocurre una actualización.
int getsize (): implementación opcional. Devuelve el número de cachés.
ReadWriteLock getReadWriteLock (): implementación opcional. Utilizado para implementar operaciones de caché atómico.
A continuación, creamos una nueva clase Rediscache para implementar la interfaz de caché:
La clase pública Rediscache implementa caché {private estático final de logger logger = loggerFactory.getLogger (redisCache.class); ReadWriteRelock privado final ReadWriteLock = new ReEntantReadWriteLock (); ID de cadena final privada; // ID de instancia de caché Redistemplate Redistemplate; Private estático final Long expire_time_in_minutes = 30; // Tiempo de vencimiento de Redis public Rediscache (ID de cadena) {if (id == NULL) {arrojar nueva IllegalArGumentException ("Las instancias de caché requieren una ID"); } this.id = id; } @Override public String getId () {return id; } / ** * Pon resultado de la consulta a redis * * @param clave * @param valor * / @override @suppleswarnings ("sin verificar") public void putObject (clave de objeto, valor de objeto) {redistemplate redistEmplate = getredistEmplate (); ValueOperations OpsforValue = redistemplate.opsforValue (); OPSFORVALUE.SET (clave, valor, expire_time_in_minutes, timeunit.minutes); logger.debug ("Pon resultado de la consulta a Redis"); } / ** * Get Cached Consult Result of Redis * * @param Key * @return * / @Override Public Object getObject (clave de objeto) {redistemplate redistemplate = getRedistEmplate (); ValueOperations OpsforValue = redistemplate.opsforValue (); logger.debug ("Resultado de la consulta en caché de Redis"); return opsforValue.get (clave); } / ** * Retire el resultado de la consulta en caché de Redis * * @param Key * @return * / @Override @SupplesSwarnings ("sin verificar") Objeto público RemoBject (clave de objeto) {redistemplate RedistEmplate = getREDIsPlate (); redistemplate.delete (clave); logger.debug ("Retire el resultado de la consulta en caché de Redis"); regresar nulo; } / ** * Borra esta instancia de caché * / @Override public void clear () {redistemplate redistemplate = getRedistEmplate (); redistemplate.execute ((rediscallback) Connection -> {Connection.FlushDB (); return null;}); logger.debug ("Borre todo el resultado de la consulta en caché de Redis"); } @Override public int getsize () {return 0; } @Override public writeReLock getReadwriteLock () {return readWriteLock; } private redistemplate getRedistEmplate () {if (redistemplate == null) {redistemplate = applicationContexTholder.getBean ("redistemplate"); } return redistemplate; }}Permítanme explicar algunos puntos clave en el código anterior:
El caché de segundo nivel que implementa debe tener un constructor con ID, de lo contrario se informará un error.
Utilizamos la plantilla redista encapsulada de resorte para operar Redis. Todos los artículos en línea que presentan a Redis al caché secundario del Nivel 2 usan la Biblioteca JEDIS directamente, pero el autor cree que este no es suficiente estilo de primavera. Además, Redistemplate encapsula la implementación subyacente. Si no usamos JEDIS en el futuro, podemos reemplazar directamente la biblioteca subyacente sin modificar el código superior. Lo que es más conveniente es que al usar redistemplate, no tenemos que preocuparnos por la liberación de Redis Connections, de lo contrario, será fácil para los novatos olvidar la conexión y hacer que la aplicación se atasque.
Cabe señalar que no se puede hacer referencia a Redistemplate a través de Autowire, porque Rediscache no es un frijol en el contenedor de primavera. Por lo tanto, necesitamos llamar manualmente el método GetBean del contenedor para obtener este frijol. Para obtener métodos de implementación específicos, consulte el código en GitHub.
El método de serialización Redis que utilizamos es la serialización JDK predeterminada. Por lo tanto, el objeto de consulta de la base de datos (como la clase de producto) necesita implementar la interfaz serializable.
De esta manera, implementamos una clase de caché elegante, científica y redis con estilo de primavera.
Encienda el caché de nivel 2
A continuación, necesitamos habilitar la memoria caché de nivel 2 en ProductMapper.xml:
<? xml versión = "1.0" encoding = "utf-8"?> <! Doctype mapper public "-// mybatis.org//dtd mapper 3.0 // en" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace = "com.wooyoo.learning.dao.mapper.productmapper"> <!-enable enable cache secundario basado en redis-> <cache type = "com.wooyoo.learning.util.rediscache"/> <select id = "seleccionar" Seleccionar "ACTUALO =" PRODUCTO "SELECT * de Products Where ID = #{Id} Limit 1 </Select> <Update =" SELECTY "Update". parametertype = "Product" FlushCache = "True"> Update Products set name = #{name}, precio = #{precio} donde id = #{id} Límite 1 </palation> </mapper><cache type = "com.wooyoo.learning.util.rediscache"/> significa habilitar la memoria caché secundaria basada en Redis, y en la declaración de actualización, establecemos FlushCache en verdadero, de modo que al actualizar la información del producto, el caché se puede invalidar automáticamente (esencialmente, se llama el método claro).
prueba
Configurar la base de datos de memoria H2
En este punto, hemos completado todo el desarrollo del código, y luego necesitamos escribir un código de prueba unitario para probar la calidad de nuestro código. En el proceso de desarrollo, utilizamos la base de datos MySQL, y generalmente a menudo utilizamos bases de datos en memoria durante las pruebas. Aquí usamos H2 como la base de datos utilizada en nuestro escenario de prueba.
También es muy simple usar H2, solo necesita configurarlo al usar MySQL. En el archivo Application.yml:
--- Spring: Perfiles: Test # Configuración de la base de datos Configuración de datos: URL: JDBC: H2: MEM: Prueba Nombre de usuario: Root Password: 123456-Class-class-Name: Org.h2.Driver Schema: classpath: schema.sql Datos: classpath: data.sql
Para evitar conflictos con la configuración predeterminada, usamos --- para iniciar un nuevo párrafo y usar perfiles: prueba para indicar que esta es la configuración en el entorno de prueba. Luego, simplemente agregue la anotación @activeprofiles (perfiles = "prueba") a nuestra clase de prueba para habilitar la configuración en el entorno de prueba, para que pueda cambiar de la base de datos MySQL a la base de datos H2 con un solo clic.
En la configuración anterior, Schema.sql se utiliza para almacenar nuestra instrucción de creación de tabla, y Data.sql se utiliza para almacenar datos de inserción. De esta manera, cuando probamos, H2 leerá estos dos archivos, inicializará la estructura de la tabla y los datos que necesitamos, y luego lo destruirá al final de la prueba, lo que no tendrá ningún impacto en nuestra base de datos MySQL. Este es el beneficio de las bases de datos en memoria. Además, no olvide establecer el alcance de la dependencia de H2 para probar en pom.xml.
El uso de Spring Boot es tan simple, puede cambiar fácilmente las bases de datos en diferentes entornos sin modificar ningún código.
Código de prueba de redacción
Debido a que se inicializa a través del inicializador de primavera, ya tenemos una clase de prueba: SpringBootMybatisWithredisApplicationTests.
Spring Boot proporciona algunas clases de herramientas que nos facilitan realizar pruebas de interfaz web, como TestRestTemplate. Luego, en el archivo de configuración, ajustamos el nivel de registro para depurar para facilitar la observación de los registros de depuración. El código de prueba específico es el siguiente:
@RunWith (SpringRunner.class) @SpringBoottest (webEnVironment = SpringBoottest.WebenVironment.random_port) @activeprofiles (perfiles = "test") clase pública SpringBootMybatiswithredisApplicationTests {@LocalServerport Private int port; @AutoWired Private TestRestTemplate RestTemplate; @Test public void test () {long productID = 1; Producto Producto = RestTemplate.getForObject ("http: // localhost:" + puerto + "/producto/" + productid, producto.class); afirmar que (producto.getPrice ()). IEqualto (200); Producto newProduct = new Product (); long newPrice = new Random (). NextLong (); newProduct.SetName ("nuevo nombre"); newProduct.SetPrice (NewPrice); RestTemplate.put ("http: // localhost:" + puerto + "/producto/" + productid, newProduct); Producto testProduct = RestTemplate.getForObject ("http: // localhost:" + puerto + "/producto/" + productid, producto.class); afirmar que (testProduct.getPrice ()). IEqualto (NewPrice); }}En el código de prueba anterior:
Primero llamamos a la interfaz Get y usamos la declaración Afirmar para determinar si se ha obtenido el objeto esperado. En este momento, el objeto del producto se almacenará en Redis.
Luego llamamos a la interfaz Put para actualizar el objeto del producto, y el caché redis se invalidará.
Finalmente, llamamos a la interfaz Get nuevamente para determinar si hemos obtenido un nuevo objeto de producto. Si se obtiene un objeto antiguo, significa que el código no válido de caché no pudo ejecutarse y hay un error en el código, de lo contrario, significa que nuestro código está bien.
Escribir la prueba de unidades es un buen hábito de programación. Aunque tomará una cierta cantidad de tiempo para usted, cuando necesite hacer algún trabajo de refactorización en el futuro, estará agradecido con usted mismo que haya escrito pruebas unitarias en el pasado.
Ver los resultados de la prueba
Hacemos clic para ejecutar el caso de prueba en IntelliJ, y los resultados de la prueba son los siguientes:
Se muestra el verde, lo que indica que el caso de prueba se ha ejecutado con éxito.
Resumir
Este artículo presenta cómo construir rápidamente un proyecto web moderno con Spring Boot, MyBatis y Redis, y también presenta cómo escribir pruebas unitarias con gracia bajo el arranque de primavera para garantizar la calidad de nuestro código. Por supuesto, hay otro problema con este proyecto, es decir, el caché de nivel 2 de MyBatis solo puede almacenarse en caché invalidado al enjuagar todo el DB. En este momento, algunos cachés que no necesitan ser invalidados también pueden ser invalidados, por lo que tiene ciertas limitaciones.