Préface
Qu'est-ce que le cache MyBatis Level 2?
Le cache secondaire est partagé par plusieurs SQLSESIONS, et sa portée est le même espace de noms du mappeur.
Autrement dit, dans différentes SQLSessions, sous le même espace de noms, la même instruction SQL et les paramètres du modèle SQL sont également les mêmes, et le cache sera touché.
Après la première exécution, les données interrogées dans la base de données seront écrites dans le cache, et les données seront récupérées du cache ne seront plus interrogées à partir de la base de données, améliorant ainsi l'efficacité de la requête.
MyBatis n'activait pas le cache secondaire par défaut, et il est nécessaire d'activer le cache secondaire dans la configuration globale (MyBatis-Config.xml).
Cet article décrit la méthode d'utilisation de Redis comme un cache pour s'intégrer à Springboot et Mybatis.
1. Dépendance des pom
Utilisez le package d'intégration Springboot Redis pour faciliter l'accès à Redis. Jedis est utilisé sur le client redis.
De plus, les caches KV à lire et à écrire seront sérialisées, donc un package de sérialisation est introduit.
<dependency> <proupId> org.springframework.boot </proupId> <ArtifactId> printemps-boot-starter-redis </retifactid> </pedigency> <dependency> <proupId> redis.clients </proupId> <Ertifactid> jedis </stificid> <version> 2.8.0 </DERNIERSE> <GroupId> com.alibaba </rom grouped> <ArtefactId> FastJson </lefactive> <DERSE> 1.2.19 </ version> </Dependance>
Une fois la dépendance terminée, l'étape suivante consiste à ajuster le client redis.
2. Beans utilisés par Redis Access
Ajoutez une configuration, configurez le bean JedisconnectionFactory et laissez-le utiliser ultérieurement.
D'une manière générale, un haricot Reistemplate sera également généré, mais il n'est pas utilisé dans le scénario suivant.
@Configurationpublic class redisConfig {@value ("$ {printemps.redis.host}") Hôte de chaîne privée; // L'espace est limité, @Bean public JedispoolConfig getReDisConfig () {JedispoolConfig config = new JedispoolConfig (); config.setMaxidle (maxidle); config.setMextotal (maxtotal); config.setMaxWaitMillis (MaxwaitMillis); config.setMinIdle (minidle); return config; } @Bean (name = "JedisconnectionFactory") JedisConnectionFactory public GetConnectionFactory () {JedisConnectionFactory Factory = new JedisconnectionFactory (); JedispoolConfig config = getRedisConfig (); factory.setpoolconfig (config); factory.sethostname (hôte); factory.setport (port); factory.setDatabase (base de données); factory.setpassword (mot de passe); factory.setTimeout (timeout); Retour Factory; } @Bean (name = "Redemplate") public Redistemplate <?,?> GetRedistemplate () {redemplate <? ,?> template = new StringRedIstemplate (getConnectionFactory ()); modèle de retour; }}Ici, @Value est utilisé pour lire la configuration liée à Redis, et il existe une méthode de lecture de configuration plus simple (@configurationProperties (prefix = ...)), que vous pouvez essayer.
Les configurations liées à Redis sont les suivantes
# redesspring.redis.host = 10.93.84.53spring.redis.port = 6379spring.redis.password = bigdata123spring.redis.database = 15spring.redis. timeout = 0spring.redis.pool.mextotal = 8spring.redis.pool.maxwaitmillis = 1000Spring.redis.pool.maxidle = 8spring.redis.pool.minidle = 0
La signification de la configuration du client redis ne sera pas expliquée ici. Le pool est généralement lié aux performances, et ils doivent être définis en fonction de la quantité de poignées de concurrence, de la mémoire et d'autres ressources.
Le client Redis a été configuré et nous commençons à configurer Redis comme cache pour MyBatis.
3. Mybatis Cache
Cette étape est l'étape la plus critique. La méthode d'implémentation consiste à implémenter une interface org.apache.ibatis.cache.cache de mybatis.
Cette interface conçoit le cache d'écriture, la lecture du cache, le cache de détruire et le contrôle d'accès à lire et à écrire des verrous.
La classe que nous implémentons pour implémenter l'interface de cache est MyBatisRediscache.
Mybatisrediscache.java
La classe publique MyBatisRediscache implémente le cache {private statique jedisconnectionfactory jedisconnectionfactory; ID de chaîne finale privée; Private Final ReadWriteLock ReadWriteLock = new ReentRanTreadWriteLock (); public mybatisrediscache (Final String id) {if (id == null) {lancer un nouveau illégalArgumentException ("Les instances de cache nécessitent un id"); } this.id = id; } @Override public void clear () {redisconnection connection = null; try {connection = jedisconnectionfactory.getConnection (); connection.flushdb (); connection.flushall (); } catch (JedisconnectionException e) {e.printStackTrace (); } enfin {if (connection! = null) {connection.close (); }}} @Override public String getID () {return this.id; } @Override public objet getObject (clé d'objet) {objet résultat = null; Connexion de redéconnexion = null; try {connection = jedisconnectionfactory.getConnection (); Redisserializer <Bject> Serializer = new JDKSerializationDisserializer (); result = serializer.deserialize (connection.get (serializer.serialize (key))); } catch (JedisconnectionException e) {e.printStackTrace (); } enfin {if (connection! = null) {connection.close (); }} Retour Résultat; } @Override public readWriteLock getReadWriteLock () {return this.readwriteLock; } @Override public int getSize () {int result = 0; Connexion de redéconnexion = null; try {connection = jedisconnectionfactory.getConnection (); result = Integer.ValueOf (connection.dbSize (). ToString ()); } catch (JedisconnectionException e) {e.printStackTrace (); } enfin {if (connection! = null) {connection.close (); }} Retour Résultat; } @Override public void putObject (clé d'objet, valeur objet) {redisconnection connexion = null; try {connection = jedisconnectionfactory.getConnection (); Redisserializer <Bject> Serializer = new JDKSerializationDisserializer (); connection.set (serializer.serialize (key), serializer.serialize (valeur)); } catch (JedisconnectionException e) {e.printStackTrace (); } enfin {if (connection! = null) {connection.close (); }}} @Override Public Object SupporObject (Key Object) {RedisConnection Connection = NULL; Résultat de l'objet = null; try {connection = jedisconnectionfactory.getConnection (); Redisserializer <Bject> Serializer = new JDKSerializationDisserializer (); result = connection.expire (serializer.serialize (key), 0); } catch (JedisconnectionException e) {e.printStackTrace (); } enfin {if (connection! = null) {connection.close (); }} Retour Résultat; } public static void setJeDisconnectionFactory (JedisconnectionFactory JedisconnectionFactory) {MyBatisRediscache.JeDisconnectionFactory = JedisconnectionFactory; }}Avis:
Comme vous pouvez le voir, cette classe n'est pas une classe gérée par la machine virtuelle Spring, mais il existe une propriété statique JedisconnectionFactory qui nécessite une injection d'un haricot de ressort, c'est-à-dire le haricot généré dans Redisconfig.
Dans une classe normale, Spring ContextAware est généralement utilisé pour utiliser le Springboot introspectif SpringContextaware.
Une autre méthode est utilisée ici, injection statique. Cette méthode est mise en œuvre via RediscachetRansfer.
4. Injection statique
Rediscachetransfer.java
@ComponentPublic Class RedisCachetRansfer {@Autowired public void SetJeDisconnectionFactory (JedisconnectionFactory JedisconnectionFactory) {MyBatisrediscache.SetjeDisconnectionFactory (JedisconnectionFactory); }}Vous pouvez voir que RediscachetRansfer est un haricot Springboot. Lors de l'initialisation du conteneur au début de la création, le bean Jedisconnectionfactory sera injecté dans le paramètre passant de la méthode SetJeDisconnectionFactory.
Le setJeDisconnectionFactory définit la propriété statique de la classe MyBatisRediscache en appelant une méthode statique.
Cela injecte le jedisconnectionfactory géré par le conteneur de ressort dans le domaine statique.
À ce stade, le code a été essentiellement terminé, et les éléments suivants sont quelques configurations. Les principaux sont (1) le commutateur global; (2) commutateur de portée de l'espace de noms; (3) Sérialisation des instances du modèle.
5. Le commutateur global du cache de niveau 2 de MyBatis
Comme mentionné précédemment, le cache de niveau 2 par défaut n'est pas activé et doit être défini sur true. Il s'agit du commutateur du cache global de niveau 2.
Configuration globale de MyBatis.
<? xml version = "1.0" Encoding = "utf-8"?> <! Doctype Configuration public "- // mybatis.org//dtd config 3.0 // en" "http://mybatis.org/dtd/mybatis-3config.dtd"> <configuration> <! activer ou désactiver le cache. -> <setting name = "cacheenabled" value = "true" /> </ settings> </ configuration>
Le chargement des configurations globales dans DataSource peut être comme ça.
bean.setMapperLocations (new PathMatchingResourcePatterNResolver (). getResources ("classPath: mybatis-mapper / *. xml"));
Spécifie le chemin de stockage de Mapper.xml. Sous le chemin MyBatis-Mapper, tous les suffixes avec .xml seront lus.
bean.setConfiglocation (new ClassPathResource ("MyBatis-Config.xml"));
Le chemin de stockage de MyBatis-Config.xml est spécifié et placé directement dans le répertoire des ressources.
@Bean (name = "MoonlightsqLSessionFactory") @primary public sqlSessionFactory MoonLightsQLSessionFactory (@qualifier ("MoonLightData") DataSource DataSource) lance l'exception {SqlSessionFactoryBean Bean = New SqlSessionFactoryBean (); bean.setDataSource (dataSource); bean.setMapperLocations (new PathMatchingResourcePatterNResolver (). getResources ("classPath: mybatis-mapper / *. xml")); bean.setConfiglocation (new ClassPathResource ("MyBatis-Config.xml")); return bean.getObject (); }6. Configurer l'espace de noms de la portée du mappeur
Comme mentionné précédemment, la portée du cache secondaire est l'espace de noms de mappeur, donc cette configuration doit être écrite dans le mappeur.
<mapper namespace = "com.kangaroo.studio.moonlight.dao.mapper.moonlightmapper"> <cache type = "com.kangaroo.studio.moonlight.dao.cache.mybatisrediscache" /> <resultmap id = "geofencest" type = "com.kangaroo.studio.moonlight.dao.model.geofence"> <constructor> <idarg colonnen = "id" javatype = "java.lang.integer" jdbcType = "Integer" /> <arg Column = "name" javatype = "java.lang.string" Column = "Type" javatype = "java.lang.integer" jdbcType = "Integer" /> <arg Column = "Group" javatype = "java.lang.string" jdbcType = "varchar" /> <arg column = "geo" javatype = "java.lang.string" jdbcType = "VaRe Column = "CreateTime" javatype = "java.lang.string" jdbcType = "varchar" /> <arg column = "updateTime" javatype = "java.lang.string" jdbcType = "varchar" /> </pirtor> </sultMap> <select id = "QuerygeoFence" paramètreType = "com.kangaroo.studio.moonlight.dao.model.geofeSeryParam" resultMap = "GeoFenest"> SELECT <inclure refid = "base_column" /> from geofence where 1 = 1 <if test = "type! = null"> et type = # {type} </ if> <if Test = "name! = nul" concat ('%', # {name}, '%') </ if> <if test = "groupe! = null"> et `groupe` like concat ('%', # {groupe}, '%') </ if> <if test =" starttime! = null "> and createtime> = # {starttime} </ if> <if test =" finm! = null "> et crareetime <= # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #) </ if> </lect> </ mapper>Avis:
La balise de cache sous l'espace de noms est la configuration du cache de chargement, et l'utilisation du cache est officiellement implémentée par MyBatisRediscache que nous venons d'implémenter.
<cache type = "com.kangaroo.studio.moonlight.dao.cache.mybatisrediscache" />
Seule une question de requête est mise en œuvre ici. Vous pouvez activer ou désactiver le cache de ce SQL dans la balise de sélection. Utilisez la valeur de la propriété USECache = true / false.
7. Mappeur et modèle
Le modèle de cache de lecture et d'écriture doit être sérialisé: il n'a qu'à implémenter l'interface sériazable lorsque la classe est déclarée.
classe publique GeoFence implémente Serializable {// setter et getter omis} public class GeofeSenparam implémente Serializable {// setter et getter omis}Le mappeur est toujours le même qu'auparavant. Lorsque vous utilisez Mapper.xml, il vous suffit de définir des fonctions abstraites.
@MapperPublic Interface MoonLightMapper {list <efence> QueryGeoFence (GeofeenceQueryParam GeofeSomeRyParam);}À ce stade, tout le code et la configuration sont terminés, le testons-le ci-dessous.
8. Testez-le
Un tel post d'interface est implémenté dans le contrôleur.
@Requestmapping (value = "/ clôture / query", méthode = requestMethod.post) @ResponseBody Public Responsentity <Sponse> QueryFence (@Requestbody GeofeSemeryParam GeofeShenqueryparam) {Try {Integer Pageninum = GeofeSegenryParam.getPagenum ()! = Null? GeoferenceQueram.Getpagannum (): un; Integer pagesize = geofeSqueryParam.getPageSize ()! = NULL? GeofeSemeryParam.getPageSize (): 10; PageHelper.StartPage (Pagenum, PageSize); List <GeoFence> list = MoonLightMapper.QueryGeoFence (GeoFenceQueryParam); return new ResponseNtity <> (nouvelle réponse (resultCode.success, "requête GeoFence Success", liste), httpstatus.ok); } catch (exception e) {Logger.Error ("Query GeoFence a échoué", e); return new ResponseNtity <> (nouvelle réponse (resultCode.Exception, "Query GeoFence a échoué", null), httpstatus.internal_server_error); }Utilisez Curl pour envoyer des demandes, note
1) -H - Type de contenu: méthode d'application / JSON
2) -D - Ce qui suit est le package de paramètres au format JSON
Curl -H "Content-Type: Application / JSON" -xpost http: //. . . / Moonlight / Fence / Query -D '{"Name": "Test", "Group": "Test", "Type": 1, "Startime": "2017-12-06 00:00:00", "Budtime": "2017-12-06 16:00:00", "Pagenenum": 1, "PageSize": 8Demande trois fois, le journal est imprimé comme suit
Comme vous pouvez le voir, la première fois que la requête du modèle SQL a été exécutée et le cache a été touché.
Dans notre environnement de test, en raison de la quantité relativement faible de données, l'optimisation du cache de la vitesse de requête n'est pas évidente. Je n'expliquerai pas beaucoup ici.