Prefacio
¿Qué es el caché de MyBatis Nivel 2?
El caché secundario es compartido por múltiples SQLSessions, y su alcance es el mismo espacio de nombres del mapeador.
Es decir, en diferentes SQLSessions, en el mismo espacio de nombres, la misma declaración SQL, y los parámetros en la plantilla SQL también son los mismos, y el caché se alcanzará.
Después de la primera ejecución, los datos consultados en la base de datos se escribirán en el caché, y los datos se recuperarán de la memoria caché ya no se consultarán de la base de datos, mejorando así la eficiencia de la consulta.
MyBatis no habilita el caché secundario de forma predeterminada, y es necesario habilitar el caché secundario en la configuración global (mybatis-config.xml).
Este artículo describe el método de usar Redis como un caché para integrarse con SpringBoot y MyBatis.
1. Dependencia de POM
Use el paquete de integración SpringBoot Redis para facilitar el acceso a Redis. JEDIS se usa en el cliente Redis.
Además, los cachés de lectura y escritura de KV serán serializados, por lo que se introduce un paquete de serialización.
<Spendency> <ProupId> org.springframework.boot </groupid> <artifactID> spring-boot-sharter-redis </arfactid> </pepertency> <pendency> <grupoid> redis.clients </proupid> <artifactid> jedis </artifactid> <preonsion> 2.8.0 </versión> </spendency> <fependency> <MoupRid> com.alibaba </groupid> <artifactid> fastjson </artifactid> <versión> 1.2.19 </versión> </pendency>
Después de completar la dependencia, el siguiente paso es ajustar el cliente Redis.
2. Frijoles utilizados por Redis Access
Agregue la configuración, configure el jeDisconnectionFactory Bean y deje que se use más tarde.
En términos generales, también se generará un frijol redistén, pero no se usa en el siguiente escenario.
@ConfigurationPublic Class Redisconfig {@Value ("$ {Spring.Redis.host}") Private String Host; // El espacio es limitado, @Bean public JedispoolConfig getRedisconfig () {JEDISpoolConfig config = new JediscoolConfig (); config.setMaxidle (maxidle); config.setMaxtotal (Maxtotal); config.setMaxWaitMillis (maxWaitMillis); config.setminidle (minidle); return config; } @Bean (name = "JedisconnectionFactory") public JedisconnectionFactory getConnectionFactory () {JedisconnectionFactory factory = new JEdisconnectionFactory (); Jedispoolconfig config = getRedisconfig (); factory.setPoolConfig (config); fábrica.sethostName (host); fábrica.setport (puerto); factory.setDatabase (base de datos); Factory.setPassword (contraseña); fábrica.setTimeOut (tiempo de espera); Return Factory; } @Bean (name = "redistemplate") público redistemplate <?,?> GetRedistEmplate () {redistemplate <?,?> Template = new StringRedistEmplate (getConnectionFactory ()); plantilla de retorno; }}Aquí, @Value se utiliza para leer la configuración relacionada con Redis, y hay un método de lectura de configuración más simple (@ConfigurationProperties (prefix = ...)), que puede probar.
Las configuraciones relacionadas con Redis son las siguientes
#redisspring.redis.host = 10.93.84.53spring.redis.port = 6379spring.redis.password = bigData123spring.redis.database = 15spring.redis. Timeout = 0spring.redis.pool.maxtotal = 8spring.redis.pool.maxwaitmillis = 1000spring.redis.pool.maxidle = 8spring.redis.pool.minidle = 0
El significado de configuración del cliente redis no se explicará aquí. Los grupos están relacionados con el rendimiento, y deben establecerse en función de la cantidad de mangos, memoria y otros recursos.
Se ha configurado el cliente Redis, y comenzamos a configurar Redis como caché para MyBatis.
3. Mybatis caché
Este paso es el paso más crítico. El método de implementación es implementar una interfaz org.apache.ibatis.cache.cache de mybatis.
Esta interfaz diseña escriba caché, lee caché, destruye caché y accesas controles de lectura y escritura.
La clase que implementamos para implementar la interfaz de caché es mybatisrediscache.
Mybatisrediscache.java
clase pública mybatisrediscache implementa caché {private static jedisconnectionFactory jedisconnectionFactory; ID de cadena final privada; ReadWriteRelock privado final ReadWriteLock = new ReEntantReadWriteLock (); public myBatisrediscache (ID de cadena final) {if (id == null) {throw newleArGumentException ("Las instancias de caché requieren una ID"); } this.id = id; } @Override public void clear () {redisConnection Connection = null; intente {Connection = JEdisconnectionFactory.getConnection (); Connection.FlushDB (); Connection.Flushall (); } Catch (JEdisconnectionException e) {E.PrintStackTrace (); } finalmente {if (conexión! = null) {Connection.close (); }}} @Override public String getId () {return this.id; } @Override Public Object getObject (clave de objeto) {objeto resultado = null; Redisconnection Connection = null; intente {Connection = JEdisconnectionFactory.getConnection (); RedisSerializer <S Object> Serializer = new JDKSerializationRedisSerializer (); resultado = serializer.deserialize (conexión.get (serializer.serialize (key))); } Catch (JEdisconnectionException e) {E.PrintStackTrace (); } finalmente {if (conexión! = null) {Connection.close (); }} Resultado de retorno; } @Override publicwriteReLock getReadwriteLock () {return this.readwriteLock; } @Override public int getsize () {int result = 0; Redisconnection Connection = null; intente {Connection = JEdisconnectionFactory.getConnection (); resultado = Integer.ValueOf (Connection.DBSize (). ToString ()); } Catch (JEdisconnectionException e) {E.PrintStackTrace (); } finalmente {if (conexión! = null) {Connection.close (); }} Resultado de retorno; } @Override public void putObject (clave de objeto, valor de objeto) {redisConnection Connection = null; intente {Connection = JEdisconnectionFactory.getConnection (); RedisSerializer <S Object> Serializer = new JDKSerializationRedisSerializer (); Connection.set (Serializer.Serialize (Key), Serializer.Serialize (valor)); } Catch (JEdisconnectionException e) {E.PrintStackTrace (); } finalmente {if (conexión! = null) {Connection.close (); }}} @Override Public Object RemoBject (clave de objeto) {redisConnection Connection = null; Resultado del objeto = nulo; intente {Connection = JEdisconnectionFactory.getConnection (); RedisSerializer <S Object> Serializer = new JDKSerializationRedisSerializer (); resultado = conexión } Catch (JEdisconnectionException e) {E.PrintStackTrace (); } finalmente {if (conexión! = null) {Connection.close (); }} Resultado de retorno; } public static void setJedisconnectionFactory (jedisconnectionFactory jedisconnectionFactory) {mybatisrediscache.jedisconnectionFactory = jedisconnectionFactory; }}Aviso:
Como puede ver, esta clase no es una clase administrada por la máquina virtual Spring, pero hay una propiedad estática JEDISCONNECTIONFATORY que requiere inyectar un frijol de resorte, es decir, el frijol generado en Redisconfig.
En una clase normal, Spring ContextAware generalmente se usa para usar SpringBoot Introspectivo SpringContextAware.
Aquí se usa otro método, inyección estática. Este método se implementa a través de redisCachetransfer.
4. Inyección estática
Rediscoquetransfer.java
@ComponentPublic Class RediscAchEtRansfer {@aUtowired public void setJedisconnectionFactory (JEdisconnectionFactory JedisconnectionFactory) {mybatisrediscache.setjedisconnectionFactory (jedisconnectionFactory); }}Puedes ver que RediscAchEtransfer es un frijol springboot. Al inicializar el contenedor al comienzo de la creación, el bean JedisconnectionFactory se inyectará en el paso del parámetro del método SetJedisconnectionFactory.
El SetJedisconnectionFactory establece la propiedad estática de la clase mybatisrediscache llamando a un método estático.
Esto inyecta el JedisconnectionFactory administrado por el contenedor de resorte en el dominio estático.
En este punto, el código se ha completado básicamente, y los siguientes son algunas configuraciones. Los principales son (1) Switch global; (2) interruptor de alcance del espacio de nombres; (3) Serialización de la instancia del modelo.
5. El interruptor global de la memoria caché de MyBatis Nivel 2
Como se mencionó anteriormente, el caché de nivel 2 predeterminado no se enciende y debe establecerse en True. Este es el interruptor para el caché global de nivel 2.
Configuración global de MyBatis.
<? xml versión = "1.0" encoding = "utf-8"?> < Habilitar o deshabilitar el caché. -> <setting name = "cacheenabled" value = "true"/> </etnetings> </figuration>
La carga de configuraciones globales en DataSource puede ser así.
Bean.SetMapperLocations (New PathMatchingResourcePternResolver (). GetResources ("ClassPath: MyBatis-Mapper/*. Xml"));
Especifica la ruta de almacenamiento de mapper.xml. Bajo el camino mybatis-mapper, se leerán todos los sufijos con .xml.
bean.setConfigLocation (nuevo classpathResource ("mybatis-config.xml"));
La ruta de almacenamiento de mybatis-config.xml se especifica y se coloca directamente en el directorio de recursos.
@Bean (name = "MoonlightSqlSessionFactory") @Primary Public SQLSessionFactory MoonlightSqlSessionFactory (@Qualifier ("MoonlightData") DataSource DataSource) lanza una excepción {sqlSessionFactoryBean Bean = Nueva sqlSessionFactoryBean (); Bean.SetDataSource (DataSource); Bean.SetMapperLocations (New PathMatchingResourcePternResolver (). GetResources ("ClassPath: MyBatis-Mapper/*. Xml")); bean.setConfigLocation (nuevo classpathResource ("mybatis-config.xml")); return bean.getObject (); }6. Configure el espacio de nombres de alcance mapper
Como se mencionó anteriormente, el alcance del caché secundario es el espacio de nombres mapeador, por lo que esta configuración debe escribirse en el mapeador.
<mapper namespace = "com.kangaroo.studio.moonlight.dao.mapper.moonlightmapper"> <cache type = "com.kangaroo.studio.moonlight.dao.cache.mybatisrediscache"/> <resultmap id = "geofencelist" type = "com.kangaroo.studio.moonlight.dao.model.geofence"> <constructor> <darg column = "id" javatype = "java.lang.integer" jdbctype = "entero" /> <arg column = "nombre" javatipo = "java.lang.string" jdbctypee = "varchar" /"arghar" /"arghar" varcharm columna = "type" javatype = "java.lang.integer" jdbctype = "entero" /> <arg columna = "grupo" javatype = "java.lang.string" jdbctype = "varcar" /> <arg column = "geo" javatype = "java.lang.string" jdbctype = "varcar" columna = "CreateTime" javatype = "java.lang.string" jdbctype = "varchar" /> <arg columna = "updateTime" javatype = "java.lang.string" jdbctype = "varchar" /> < /constructor> < /denteMap> <Select id = "QueryGeofence" parameterType="com.kangaroo.studio.moonlight.dao.model.GeoFenceQueryParam" resultMap="geoFenceList"> select <include refid="base_column"/> from geoFence where 1=1 <if test="type != null"> and type = #{type} </if> <if test="name != null"> and name like concat ('%', #{name}, '%') </if> <if test = "group! = null"> y `group` me gusta concat ('%', #{group}, '%' ') </if> <if test =" starttime! = null "> y createTime> = #{tiempo de inicio} </if> <if test =" endtime! = null>> y createMe> { { ifi </if> </select> </mapper>Aviso:
La etiqueta de caché en el espacio de nombres es la configuración de la carga caché, y el uso del caché es implementado oficialmente por MyBatisrediscache que acabamos de implementar.
<cache type = "com.kangaroo.studio.moonlight.dao.cache.mybatisrediscache"/>
Solo se implementa una consulta de consulta aquí. Puede encender o desactivar el caché de este SQL en la etiqueta Seleccionar. Use el valor de la propiedad useCache = true/false.
7. Mapeador y modelo
El modelo de caché de lectura y escritura debe ser serializado: solo necesita implementar la interfaz seriaziable cuando se declara la clase.
La clase pública Geofence implementa serializable {// setter y getter omitido} public class geofenceParam implementa serializable {// setter y getter omitido}Mapper sigue siendo el mismo que antes. Cuando usa mapper.xml, solo necesita definir funciones abstractas.
@MapperPublic Interface MoonlightMapper {List <Seofence> QueryGeofence (GeofenceQueryParam GeofenceQueryParam);}En este punto, se completan todo el código y la configuración, probemos a continuación.
8. Pruébelo
Una publicación de interfaz de este tipo se implementa en el controlador.
@RequestMapping (value = "/Fence/Query", Method = requestMethod.post) @ResponseBody Public ResponseEntity <Spuesta> Queryfence (@RequestBody GeofenceQueryParam GeofenceQueryParam) {try {Integer Pagenum = GeofenceQueryParam.getPageNum ()! = NULL? Entero pagesize = geofenceQueryparam.getPageSize ()! = NULL? GeofenceQueryParam.getPageSize (): 10; PageHelper.StartPage (Pagenum, PageSize); List <Beofence> list = MoonlightMapper.QueryGeofence (GeofenceQueryParam); devuelve la nueva respuesta de respuesta <> (nueva respuesta (resultCode.Success, "Consulta Geofence Success", List), httpstatus.ok); } catch (excepción e) {logger.error ("consulta geofence fallida", e); return New ResponseEntity <> (nueva respuesta (resultCode.Exception, "Geofence de consulta fallida", nulo), httpstatus.internal_server_error); }Use curl para enviar solicitudes, nota
1) -H -Tipo de contenido: Método Aplicación/JSON
2) -d - El siguiente es el paquete de parámetros en formato JSON
CURL -H "Content -Type: Application/JSON" -XPOST http: //. . . /Moonlight/Fence/Query -d '{"Nombre": "Test", "Grupo": "Test", "Tipo": 1, "Tiempo de inicio": "2017-12-06 00:00:00", "Tiempo de end": "2017-12-06 16:00:00", "Pagnum": 1, "PageSize": 8Solicitado tres veces, el registro se imprime de la siguiente manera
Como puede ver, solo la primera vez que se ejecutó la consulta de plantilla SQL y el caché fue golpeado.
En nuestro entorno de prueba, debido a la cantidad relativamente pequeña de datos, la optimización del caché de la velocidad de consulta no es obvia. No explicaré mucho aquí.