Stufe 1 Cache und Stufe 2 Cache
MyBatis entwirft Datencache in eine zweistufige Struktur, unterteilt in Cache und Cache auf zweiter Ebene:
Level-1-Cache ist ein Cache auf Sitzungsstufeebene, der sich im SQLSession-Objekt befindet, das eine Datenbanksitzung darstellt, und wird auch als lokaler Cache bezeichnet. Level 1 Cache ist eine von MyBatis implementierte Funktion. Benutzer können es nicht konfigurieren und unterstützen es standardmäßig automatisch. Benutzer haben nicht das Recht, es anzupassen (dies ist jedoch nicht absolut und können durch Entwicklungs-Plug-Ins geändert werden).
Der Cache der zweiten Ebene ist ein Cache auf Anwendungsebene, der einen langen Lebenszyklus hat, der dem Deklarationszyklus der Anwendung ist, was bedeutet, dass sein Funktionsumfang die gesamte Anwendungsanwendung ist.
Die Organisation von Cache der ersten Ebene und des Cache auf zweiter Ebene in MyBatis ist in der folgenden Abbildung dargestellt:
Arbeitsmechanismus von Stufe 1 Caching:
Level 1 Cache ist Sitzungsstufe. Im Allgemeinen verwendet ein SQLSession -Objekt ein Executor -Objekt, um Sitzungsvorgänge abzuschließen. Das Executor -Objekt führt einen Cache -Cache bei, um die Abfrageleistung zu verbessern.
Arbeitsmechanismus des sekundären Caching:
Wie oben erwähnt, verwendet ein SQLSession -Objekt ein Ausführungsobjekt, um den Sitzungsvorgang abzuschließen. Der Schlüssel zu MyBatis 'sekundärem Caching -Mechanismus liegt darin, sich um dieses Testamentsvollstrecker zu machen. Wenn der Benutzer "CacheEnabled = true" konfiguriert hat, fügt er, wenn MyBatis ein Executor -Objekt für das SQLSession -Objekt erstellt, dem ausführenden Objekt: CachingExecutor einen Dekorateur hinzu. Zu diesem Zeitpunkt verwendet SQLSession das CachingExecutor -Objekt, um die Betriebsanforderung abzuschließen. Bei Abfragebestimmungen stellt CachingExecutor zunächst fest, ob die Abfrageanforderung die Ergebnisse im sekundären Cache auf Anwendungsebene zwischengespeichert hat. Wenn ein Abfrageergebnis vorliegt, wird die zwischengespeicherten Ergebnisse direkt zurückgegeben. Wenn es keinen Cache gibt, wird es an das Real Executor -Objekt übergeben, um den Abfragebetrieb abzuschließen. Danach wird CachingExecutor das vom echte Testamentsvollstrecker zurückgegebene Abfrageergebnis in den Cache eingeben und es dann an den Benutzer zurückgeben.
MyBatis 'sekundärer Cache ist so konzipiert, dass er flexibler ist. Sie können die von MyBatis definierte sekundäre Cache -Implementierung verwenden. Sie können den Cache auch anpassen, indem Sie die Schnittstelle org.apache.ibatis.cache.cache implementieren. Sie können auch Speicher-Cache-Bibliotheken von Drittanbietern wie Memcached usw. verwenden.
Cache -Transformation
Frage:
Das häufigste Problem ist, dass nach dem Öffnen von Cache die Daten auf der ersten Seite beim Abfragen von Paging auf die Seite zurückgegeben werden. Bei Verwendung des SQL Automatic Generation Plug-Ins, um SQL für die GET-Methode zu generieren, funktionieren die übergebenen Parameter nicht. Unabhängig von den bestandenen Parametern wird das Abfrageergebnis des ersten Parameters zurückgegeben.
Warum diese Probleme auftreten:
Bei der Erläuterung des Ausführungsprozesses von MyBatis zuvor wurde erwähnt, dass MyBatis 'Executor unter der Prämisse, Cache zu aktivieren, zuerst Daten aus dem Cache lesen und nur in die Datenbank gehen, um abzufragen, wenn sie nicht gelesen werden kann. Das Problem liegt hier. Die Ausführungszeit des SQL Automatic Generation Plug-In- und Paging-Plug-Ins befindet sich im Anweisungshandler, und der Anweisungshandler wird nach dem Executor ausgeführt. Unabhängig davon, ob SQL Automatic Generation Plug-In und Paging Plug-In durch SQL umgeschrieben werden, verwendet der Executor beim Generieren und Lesen des Cache-Schlüssels ursprüngliche SQL (der Schlüssel besteht aus SQL und entsprechenden Parameterwerten). Natürlich gibt es ein Problem.
Lösen Sie das Problem:
Sobald die Ursache des Problems gefunden wurde, ist es bequem, es zu lösen. Überschreiben Sie einfach die Methode zum Generieren von Schlüssel im Executor über den Interceptor und verwenden Sie das automatisch generierte SQL (entsprechend dem SQL Automatic Generation Plug-In) oder fügen Sie Paging-Informationen (entsprechend dem Paging-Plug-In entsprechend) hinzu.
Interceptor Signature:
@Intercepts ({@Signature (type = executor.class, method = "query", args = {mapPedStatement.class, Object.class, rowBounds.class, ResultHandler.class})}) öffentliche Klasse cacheInterceptor implements interceptor {...}Wie aus der Signatur ersichtlich ist, ist der Zieltyp abzufangen (Hinweis: Der Typ kann nur als Schnittstellentyp konfiguriert werden), und die Interception -Methode ist eine Methode mit dem Namen Abfrage.
Implementierung von Abfang:
öffentliches Objekt -Intercept (Invocation Invocation) löscht Throwable {Executor ExecutorProxy = (Executor) Invocation.gettarget (); MetaObject metaExecutor = metaObject.forObject (ExecutorProxy, default_object_factory, default_object_wrapper_factory); // die Proxy -Objektkette trennen, während (metaexecutor.hasgers ("h") {Object Object = metaExecutor.getValue ("H"); metaexecutor = metaObject.forObject (Objekt, default_object_factory, default_object_wrapper_factory); } // Die Zielklasse, die das letzte Proxy -Objekt trennen (metaexecutor.hasgers ("Ziel")) {Object Object = metaExecutor.getValue ("Ziel"); metaexecutor = metaObject.forObject (Objekt, default_object_factory, default_object_wrapper_factory); } Objekt [] args = invocation.getArgs (); kehren Sie diese Querie zurück (metaexecutor, args); } public <E> list <e> Abfrage (metaObject metaExecutor, Object [] args) löst SQLEXception aus {MapPortement ms = (MapChedStatement) args [0]; Objekt parameterObject = args [1]; Rowbounds rowbounds = (rowbounds) args [2]; ResultHandler resulthandler = (ResultHandler) Args [3]; BoundSQL boundSQL = FrauGetBoundSQL (ParameterObject); // Schreiben Sie die Erzeugung von wichtigen cachekey cachekey = createCacheKey (MS, ParameterObject, RowBounds, BoundSQL) neu; Executor Executor = (Executor) metaexecutor.getOriginalObject (); return executor.Query (MS, ParameterObject, Rowbounds, ResultHandler, Cachekey, boundSQL); } private cachekey createCacheKey (MADPDStatement MS, Object ParameterObject, Rowbounds RowBounds, boundSQL boundSQL) {configuration configuration = ms.getConfiguration (); PAGESQLID = configuration.getVariables (). getProperty ("pagesqlid"); if (null == PAGESQLID || "" .Equals (pagesqlid)) {logger.warn ("Eigenschaften pagesqlid ist nicht festgelegt, verwenden Sie Standard '.*Seite $'"); PAGESQLID = defaultPagesqlid; } Cachekey cachekey = new CacheKey (); cachekey.update (mssgetId ()); cachekey.update (rowBounds.getOffset ()); cachekey.update (rowBounds.getLimit ()); Liste <PARAMETERMAPPING> ParameterMappings = bodernSQL.getParametermappings (); // Lösen Sie den Fehler, der automatisch SQL generiert, und die SQL -Anweisung ist leer, wodurch der Schlüssel Fehler generiert wird, wenn (null == bodensql.getSql () || "" .Equals (bodensql.getSql ()) {String id = ms.getid (); id = id.substring (id.lastIndexof (".") + 1); String Newsql = null; try {if ("select" .equals (id)) {newsql = sqlbuilder.buildSelectSQL (parameterObject); } SQLSource SQLSource = BuildSQLSource (Konfiguration, Newsql, parameterObject.getClass ()); parameterMappings = sqlSource.getBoundSQL (parameterObject) .getParameterMappings (); cachekey.update (NewsQL); } catch (Ausnahme e) {logger.Error ("Cachekey -Fehler aktualisieren", e); }} else {cachekey.update (boannsql.getSql ()); } MetaObject metaObject = metaObject.forObject (parameterObject, default_object_factory, default_object_wrapper_factory); if (parameterMappings.size ()> 0 && parameterObject! if (typeHandlerregistry.hastypeHandler (parameterObject.getClass ()) {cachekey.update (parameterObject); } else {for (parameterMaping parametermaping: parameterMappings) {String Eigenschaftsname = parametermaping.getProperty (); if (metaObject.hasgertter (PropertyName)) {cachekey.update (metaObject.getValue (PropertyName)); } else if (boundsql.hasadditionalParameter (PropertyName)) {Cachekey.Update (boundsql.getAdDitionalParameter (PropertyName)); }}}} // Wenn eine Paging -Abfrage erforderlich ist, fügen Sie die aktuelle Seite und Anzahl der Seiten pro Seite in den Parameter der Seite zum Cachekey hinzu, wenn (mssgetId () übereinstimmt (Pagesqlid) && metaObject.hasgers ("Seite") {PageParameter Seite = (pageParameter) metaObject.getValue ("PageParameter). if (null! = page) {cachekey.update (page.getCurrentPage ()); cachekey.update (page.getPageSize ()); }} return cachekey; } Implementierung von Plugin:
Öffentliches Objekt -Plugin (Objektziel) {// Wenn die Zielklasse vom Typ CachingExecutor vom Typ ist, wird die Zielklasse eingewickelt, ansonsten kehrt sie direkt zum Ziel selbst zurück und verringert die Anzahl der, mit denen das Ziel proxyiert ist, wenn (Zielinstanz von CachingExecutor) {return plugin.wrap (target, this); } else {return target; }}