Кэш 1 и кэш уровня 2
Mybatis разрабатывает кэш данных в двухуровневую структуру, разделенную на кэш первого уровня и кэш второго уровня:
Кэш 1-го уровня-это кэш на уровне сеанса, расположенный в объекте SQLSession, представляющего сеанс базы данных, а также называется локальным кешем. Кэш уровня 1 - это функция, реализованная внутри Mybatis. Пользователи не могут настроить его и автоматически поддерживать его по умолчанию. Пользователи не имеют права настраивать его (но это не абсолютно, и их можно изменить с помощью плагинов разработки);
Кэш второго уровня-это кэш на уровне приложения, который имеет длительный жизненный цикл, такой же, как и цикл объявления приложения, что означает, что его объем функции является всем приложением.
Организация кеша первого уровня и кэша второго уровня в Mybatis показана на рисунке ниже:
Рабочий механизм кэширования первого уровня:
Кэш 1 -го уровня - уровень сеанса. Вообще говоря, объект SQLSession будет использовать объект исполнителя для завершения операций сеанса. Объект исполнителя будет поддерживать кэш -кэш для повышения производительности запроса.
Рабочий механизм вторичного кэширования:
Как упомянуто выше, объект SQLSession будет использовать объект Executor для завершения операции сеанса. Ключ к механизму вторичного кэширования Mybatis заключается в том, чтобы суетиться об этом объекте исполнителя. Если пользователь настроил "cacheenabled = true", когда Mybatis создает объект Executor для объекта SQLSession, он добавит декоратор в объект Executor: CachingExecutor. В настоящее время SQLSession использует объект CachingExecutor для выполнения запроса на операцию. Для запросов на запросы CachingExecutor сначала определит, имеет ли запрос на запрос кэшированным результатом во вторичном кэше уровня приложения. Если есть результат запроса, он непосредственно вернет кэшированные результаты; Если кеш не будет, он будет передан реальному объекту исполнителя, чтобы завершить операцию запроса. После этого CachingExecutor поместит результат запроса, возвращенный реальным исполнителем в кэш, а затем вернет его пользователю.
Вторичный кэш Mybatis предназначен для того, чтобы быть более гибким. Вы можете использовать реализацию вторичного кэша, определенную Mybatis; Вы также можете настроить кэш, внедрив интерфейс org.apache.ibatis.cache.cache; Вы также можете использовать сторонние библиотеки кеша памяти, такие как Memcached и т. Д.
Преобразование кэша
вопрос:
Наиболее распространенной проблемой является то, что после открытия кэша данные на первой странице будут возвращены на страницу при запросе пейджинг. Кроме того, при использовании подключаемого модуля автоматического генерации SQL для генерации SQL для метода GET, пропущенные параметры не работают. Независимо от пройденных параметров, результат запроса первого параметра возвращается.
Почему эти проблемы возникают:
При объяснении процесса выполнения Mybatis уже упоминалось, что под предпосылкой включения кэша исполнитель Mybatis сначала прочитал данные из кэша и перейдет только в базу данных, чтобы запрашивать, если его нельзя прочитать. Проблема лежит здесь. Время выполнения подключаемого модуля и подключаемого модуля SQL Automatic Generation и подключаемого модуля находится в ApportHandler, а AtractHandler выполняется после исполнителя. Реализованы ли SQL Automatic Generation Plug-In и подключаемого модуля Paging Plugin путем переписывания SQL, исполнитель использует исходный SQL при генерации и чтении ключа кэша (ключ состоит из SQL и соответствующих значений параметров), поэтому, конечно, есть проблема.
Решите проблему:
Как только причина проблемы будет найдена, будет удобно ее решить. Просто переопределите метод генерации клавиш у исполнителя через перехватчик и используйте автоматически сгенерированный SQL (соответствующий плагинту SQL Automatic Generation) или добавленную информацию о пейджинге (соответствует подключающему модуку Paging).
Подпись перехватчика:
@Intercepts ({@Signature (type = executor.class, method = "Query", args = {mapedStatement.class, objКак видно из подписи, целевой тип, который нужно перехватить, является исполнителем (примечание: тип может быть настроен только как тип интерфейса), а метод перехвата - это метод с именем запроса.
Реализация перехвата:
public Object Intercept (vocation inpocation) бросает throwable {executor executorproxy = (исполнитель) velocation.getTarget (); MetaObject metaExeCutor = metaObject.ForObject (executorProxy, default_object_factory, default_object_wrapper_factory); // отделить цепочку прокси -объекта while (metaexecutor.hasgetter ("h")) {object object = metaexecutor.getvalue ("h"); metaexecutor = metaobject.forobject (object, default_object_factory, default_object_wrapper_factory); } // Целевой класс, который разделяет последний прокси -объект while (metaexecutor.hasgetter ("target")) {object = metaexecutor.getvalue ("target"); metaexecutor = metaobject.forobject (object, default_object_factory, default_object_wrapper_factory); } Object [] args = vocation.getargs (); вернуть это. } public <e> list <e> Query (MetaObject MetaExeCutor, Object [] args) Throws SQLexception {mapedStatement ms = (mapedStatement) args [0]; Object ParameterObject = args [1]; Rowbounds Rowbounds = (Rowbounds) args [2]; РЕЗУЛЬТАЛЬНЫЙ РЕЗУЛЬЦОВАЛЬНЫЙ ДОЛЖНЫ = (РЕЗУЛЯННЫЙ) args [3]; Boundsql boundsql = ms.getboundsql (parameterobject); // Переписать генерацию ключа кешкея cachekey = createCachekey (MS, ParameterObject, Rowbounds, BoundSQL); Исполнитель Исполнитель = (Исполнитель) MetaExecutor.getoriginalObject (); return executor.query (ms, parameterobject, rowbounds, resulthandler, cachekey, boundsql); } private cachekey createCachekey (MapedStatement MS, объект ParameterObject, Rowbounds Rowbounds, BoundSQL Boundsql) {Configuration Configuration = ms.getConfiguration (); pagesqlid = configuration.getVariables (). getProperty ("pagesqlid"); if (null == pagesqlid || "". equals (pagesqlid)) {logger.warn ("Property Pagesqlid не нанесено, используйте по умолчанию '.*Page $'"); pagesqlid = defaultPagesQlid; } Cachekey cachekey = new cachekey (); cachekey.update (ms.getid ()); cachekey.update (rowbounds.getoffset ()); cachekey.update (rowbounds.getlimit ()); Список <parametermapping> parametermAppings = boundsql.getParameterMappings (); // Решить ошибку, которая автоматически генерирует SQL, и оператор SQL пуст, что приводит к созданию ошибок, если (null == boundsql.getsql () || "" .equals (boundsql.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 (Configuration, Newsql, ParameterObject.getClass ()); ParameterMappings = sqlSource.getBoundSQL (parameterObject) .getParameterMappings (); cachekey.update (newsql); } catch (Exception e) {logger.Error ("Обновление ошибка кэширования.", e); }} else {cachekey.update (boundsql.getsql ()); } MetaObject metaObject = metaObject.forObject (parameterObject, default_object_factory, default_object_wrapper_factory); if (parametermAppings.size ()> 0 && parameterObject! = null) {typeHandlerRegistry typhandlerRegistry = ms.getConfiguration (). getTypeHandlerRegistry (); if (typehandlerregistry.hastypehandler (parameterobject.getClass ())) {cachekey.update (parameterObject); } else {for (parametermapping parametermapping: parametermappings) {string properationname = parametermapping.getProperty (); if (metaobject.hasgetter (propertyname)) {cachekey.update (metaobject.getValue (PropertyName)); } else if (boundsql.hasadditionalparameter (propertyname)) {cachekey.update (boundsql.getadditionalparameter (propertyname)); }}}} // Когда требуется запрос на пьесу, добавьте текущую страницу и количество страниц на страницу в параметре страницы в CacheKey if (ms.gled (). Matches (pagesqlid) && metaobject.hasgetter ("page")) {pageparameter page = (pageparameter) metaobject.getvalue ("page"); if (null! = page) {cachekey.update (page.getCurrentPage ()); cachekey.update (page.getPagesize ()); }} вернуть CacheKey; } Реализация плагина:
Общественный объект плагин (объект цели) {// Когда целевой класс имеет тип cachingexecutor, целевой класс обернут, в противном случае он напрямую вернется к самой цели, уменьшая количество раз, когда цель обеспечивается, если (Target InstanceOf CachingExeCutor) {return Plugin.Wrap (Target This); } else {return target; }}