


Detailed explanation of caching and cache usage improvements in Java's MyBatis framework
First-level cache and second-level cache
MyBatis designs the data cache into a two-level structure, divided into first-level cache and second-level cache:
The first-level cache is a session-level cache, located in the primary database. The SqlSession object of the session is also called the local cache. The first-level cache is a feature implemented internally by MyBatis. It cannot be configured by users. It is automatically supported by default and users do not have the right to customize it (but this is not absolute, and it can be modified by developing plug-ins);
The second-level cache is an Application-level cache. It has a long life cycle, which is the same as the Application's declaration cycle. That is to say, its scope is the entire Application application.
The organization of the first-level cache and the second-level cache in MyBatis is as shown below:
The working mechanism of the first-level cache:
The first-level cache is the Session session Level, generally speaking, a SqlSession object will use an Executor object to complete session operations, and the Executor object will maintain a Cache cache to improve query performance.
The working mechanism of the second-level cache:
As mentioned above, a SqlSession object will use an Executor object to complete the session operation. The key to MyBatis's second-level cache mechanism is to make a fuss about this Executor object. If the user configures "cacheEnabled=true", then when MyBatis creates an Executor object for the SqlSession object, it will add a decorator to the Executor object: CachingExecutor. At this time, SqlSession uses the CachingExecutor object to complete the operation request. For a query request, CachingExecutor will first determine whether the query request has a cached result in the application-level secondary cache. If there is a query result, it will directly return the cached result; if there is no cached result, it will then be handed over to the real Executor object to complete the query. After the operation, CachingExecutor will place the query results returned by the real Executor into the cache, and then return them to the user.
MyBatis's second-level cache is designed to be more flexible. You can use MyBatis's own defined second-level cache implementation; you can also customize the cache by implementing the org.apache.ibatis.cache.Cache interface; you can also use third-party memory Caching libraries such as Memcached, etc.
Cache transformation
Problem:
The most common problem is that after the cache is turned on, no matter whether the query is paging Whichever page returns the data of the first page. In addition, when using the SQL automatic generation plug-in to generate the SQL of the get method, the parameters passed in have no effect. No matter how many parameters are passed in, the query result of the first parameter is returned.
Why these problems occur:
When I explained the execution process of Mybatis before, I mentioned that under the premise of turning on the cache, the executor of Mybatis will first read the data from the cache. Go to the database to query. The problem lies here. The execution timing of sql automatic generation plug-in and paging plug-in is in statementhandler, and statementhandler is executed after executor. Both sql automatic generation plug-in and paging plug-in are realized by rewriting sql. The executor is generating When reading the cache key (the key consists of SQL and corresponding parameter values), the original SQL is used, so of course there will be a problem.
Solve the problem:
After you find the cause of the problem, it will be easier to solve it. Just rewrite the key generation method in the executor through the interceptor, and use automatically generated sql (corresponding to the sql automatic generation plug-in) or add paging information (corresponding to the paging plug-in) when the generation is possible.
Interceptor signature:
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})}) public class CacheInterceptor implements Interceptor { ... }
It can be seen from the signature that the target type to be intercepted is Executor (note: type can only be configured as an interface type), interception The method is the method named query.
Intercept implementation:
public Object intercept(Invocation invocation) throws Throwable { Executor executorProxy = (Executor) invocation.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 object = metaExecutor.getValue("target"); metaExecutor = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY); } Object[] args = invocation.getArgs(); return this.query(metaExecutor, args); } public <E> List<E> query(MetaObject metaExecutor, Object[] args) throws SQLException { MappedStatement ms = (MappedStatement) args[0]; Object parameterObject = args[1]; RowBounds rowBounds = (RowBounds) args[2]; ResultHandler resultHandler = (ResultHandler) args[3]; BoundSql boundSql = ms.getBoundSql(parameterObject); // 改写key的生成 CacheKey cacheKey = createCacheKey(ms, parameterObject, rowBounds, boundSql); Executor executor = (Executor) metaExecutor.getOriginalObject(); return executor.query(ms, parameterObject, rowBounds, resultHandler, cacheKey, boundSql); } private CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) { Configuration configuration = ms.getConfiguration(); pageSqlId = configuration.getVariables().getProperty("pageSqlId"); if (null == pageSqlId || "".equals(pageSqlId)) { logger.warn("Property pageSqlId is not setted,use default '.*Page$' "); pageSqlId = defaultPageSqlId; } CacheKey cacheKey = new CacheKey(); cacheKey.update(ms.getId()); cacheKey.update(rowBounds.getOffset()); cacheKey.update(rowBounds.getLimit()); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); // 解决自动生成SQL,SQL语句为空导致key生成错误的bug if (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("Update cacheKey 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 typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry(); if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { cacheKey.update(parameterObject); } else { for (ParameterMapping parameterMapping : parameterMappings) { String propertyName = parameterMapping.getProperty(); if (metaObject.hasGetter(propertyName)) { cacheKey.update(metaObject.getValue(propertyName)); } else if (boundSql.hasAdditionalParameter(propertyName)) { cacheKey.update(boundSql.getAdditionalParameter(propertyName)); } } } } // 当需要分页查询时,将page参数里的当前页和每页数加到cachekey里 if (ms.getId().matches(pageSqlId) && metaObject.hasGetter("page")) { PageParameter page = (PageParameter) metaObject.getValue("page"); if (null != page) { cacheKey.update(page.getCurrentPage()); cacheKey.update(page.getPageSize()); } } return cacheKey; }
plugin implementation:
public Object plugin(Object target) { // 当目标类是CachingExecutor类型时,才包装目标类,否者直接返回目标本身,减少目标被代理的 // 次数 if (target instanceof CachingExecutor) { return Plugin.wrap(target, this); } else { return target; } }
More detailed explanation of caching in Java's MyBatis framework For articles related to improving the use of cache, please pay attention to the PHP Chinese website!

The class loader ensures the consistency and compatibility of Java programs on different platforms through unified class file format, dynamic loading, parent delegation model and platform-independent bytecode, and achieves platform independence.

The code generated by the Java compiler is platform-independent, but the code that is ultimately executed is platform-specific. 1. Java source code is compiled into platform-independent bytecode. 2. The JVM converts bytecode into machine code for a specific platform, ensuring cross-platform operation but performance may be different.

Multithreading is important in modern programming because it can improve program responsiveness and resource utilization and handle complex concurrent tasks. JVM ensures the consistency and efficiency of multithreads on different operating systems through thread mapping, scheduling mechanism and synchronization lock mechanism.

Java's platform independence means that the code written can run on any platform with JVM installed without modification. 1) Java source code is compiled into bytecode, 2) Bytecode is interpreted and executed by the JVM, 3) The JVM provides memory management and garbage collection functions to ensure that the program runs on different operating systems.

Javaapplicationscanindeedencounterplatform-specificissuesdespitetheJVM'sabstraction.Reasonsinclude:1)Nativecodeandlibraries,2)Operatingsystemdifferences,3)JVMimplementationvariations,and4)Hardwaredependencies.Tomitigatethese,developersshould:1)Conduc

Cloud computing significantly improves Java's platform independence. 1) Java code is compiled into bytecode and executed by the JVM on different operating systems to ensure cross-platform operation. 2) Use Docker and Kubernetes to deploy Java applications to improve portability and scalability.

Java'splatformindependenceallowsdeveloperstowritecodeonceandrunitonanydeviceorOSwithaJVM.Thisisachievedthroughcompilingtobytecode,whichtheJVMinterpretsorcompilesatruntime.ThisfeaturehassignificantlyboostedJava'sadoptionduetocross-platformdeployment,s

Containerization technologies such as Docker enhance rather than replace Java's platform independence. 1) Ensure consistency across environments, 2) Manage dependencies, including specific JVM versions, 3) Simplify the deployment process to make Java applications more adaptable and manageable.


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Atom editor mac version download
The most popular open source editor

Dreamweaver Mac version
Visual web development tools

PhpStorm Mac version
The latest (2018.2.1) professional PHP integrated development tool

mPDF
mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

EditPlus Chinese cracked version
Small size, syntax highlighting, does not support code prompt function