Attitude is everything.

Dynamic SQL template 1 MappedStatement (mapper) 2 SqlSession1 3.Executor 4. All roads lead to the same destination. 2. Working Principle 5. Cache 1. Level 1 cache 2. Level 2 Cache 2.1 Basic Information 2.2 How to work 6

After seeing Mybatis, I think Mybatis is small, but all the five organs, and exquisite design.

What is the design behind this black box? What is my understanding

I. Container Configuration

Configuration is like the general manager of Mybatis, where all the Configuration information of Mybatis is stored, in addition, it provides the method of setting the Configuration information. Configuration can be obtained from the Configuration file or set directly through the program.

Configuration is similar to Spring’s container concept, but at the central container level, storing most of what Mybatis needs to run.

Dynamic SQL templates

What do we do most of the time with Mybatis? Write SQL templates in XML, or write SQL templates in interfaces


       

<! DOCTYPE mapper

PUBLIC "- / / mybatis.org//DTD Mapper / 3.0 / EN"

  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


<! -- mapper: root tag, namespace: namespace unique -->

<mapper namespace="UserMapper">

   <select id="selectUser" resultType="com.wqd.model.User">

      select * from user where id= #{id}

   </select>

</mapper>

Copy the code

or

@Mapper

public interface UserMapper {



    @Insert("insert into user( name, age) " +

            "values(#{user.name}, #{user.age})")

    void save(@Param("user") User user);



    @Select("select * from user where id=#{id}")

    User getById(@Param("id")String id);

}

Copy the code

What does this mean inside the Mybatis framework?

1. MappedStatement

  • Just like with Spring, we write a Controller class that defines a BeanDefinition to the Spring framework.

  • When we configure the SQL template in the XML configuration, we are defining the Mybatis field value MappedStatement

An SQL template corresponds to an MappedStatement

When mybatis starts, it parses the SQL template you defined into a unified MappedStatement object and puts it into the container Configuration. Each MappedStatement object has an ID attribute. This id is the same as the id in the mysql database. It is the same as the id in the mysql database. It is the same as the id in the mysql database

Spring’s BeanDefinition, Mybatis’ MappedStatement

2. Parsing process

As with Spring, you can define beans in XML or configure them in Java classes. There are two loading methods involved.

Here is a brief mention of the two methods of parsing entry:

1. XML parsing:

The XMLConfigBuilder component is provided to parse XML files. This process is both the creation of the Configuration container and the parsing of the MappedStatement.

XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);

Configuration config = parser.parse()

Copy the code

2. When used with Spring: A MapperFactoryBean is registered. After the MapperFactoryBean is instantiated and afterPropertiesSet() is executed, the parsing of MappedStatement is triggered

Insert a picture description here

Mybatis will eventually call a MapperAnnotationBuilder component, which, as its name suggests, is an MappedStatement that handles annotations

Describe the two ways are very image, interested can look at the source

Third, SqlSession

1. Basic introduction

With an SQL template, you pass in parameters and get data from the database, which is what SqlSession does.

SqlSession represents a session we have with the database through Mybatis. Using Mybatis, we are using SqlSession and database interaction.

We tell SqlSession the ID of the SQL template, that is, the ID and parameters of the MappedStatement. SqlSession will find the MappedStatement based on the TEMPLATE ID, interact with the data, and return the interaction result

User user = sqlSession.selectOne("com.wqd.dao.UserMapper.selectUser".1);

Copy the code

Classification of 2.

  • DefaultSqlSession: The most basic SQLSession implementation, where all executions end upDefaultSqlSessionOn, threads are not safe
  • SqlSessionManager: a thread-safe SqlsessionThreadLocalImplement thread safety.

3.Executor

A Sqlsession is a facade interface whose internal work is delegated to executors.

public class DefaultSqlSession implements SqlSession {



  private Configuration configuration;

  private Executor executor;/ / is he

 }

Copy the code

The methods we call SqlSession are all done by Executor.

public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {

    try {

      MappedStatement ms = configuration.getMappedStatement(statement);

- to the Executor

      executor.query(ms, wrapCollection(parameter), rowBounds, handler);

    } catch (Exception e) {

      throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);

    } finally {

      ErrorContext.instance().reset();

    }

  }

Copy the code

4. All roads lead to the same destination

1. The meaning of existence

UserMapper userMapper = sqlsession.getMapper(UserMapper.class);

User user = userMapper.getById("51");

Copy the code

The point of Mapper is to allow users to execute SQL as if they were calling methods. In contrast, you need to display the ID of the passed SQL template, and how the SQL is executed.

User user = sqlSession.selectOne("com.wqd.dao.UserMapper.getById".1);

Copy the code

2. Working principle

Agent!! Agent!! Agent!! Mapper implements this process through a proxy mechanism.

MapperProxyFactory: Create a proxy for our Mapper interface.

  • When Mybatis is used alone, Mybatis calls MapperRegistry. AddMapper () to create a new MapperProxyFactory(type) for the UserDao interface

  • When used with Spring, The MapperScannerRegistrar component triggers the doScan method of the ClassPathMapperScanner component to set the BeanClass of the UserDao BeanDefinition to MapperProxyFactory. When instantiating the SpringBean, the UserDao instance object (that is, the proxy object) is obtained from the MapperProxyFactory.

public T newInstance(SqlSession sqlSession) {

    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);

    return newInstance(mapperProxy);

}



protected T newInstance(MapperProxy<T> mapperProxy) {

    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);

}

Copy the code

MapperProxyFactory uses JDK dynamic proxy technology to help us create a proxy class in memory. (You can’t see it, but it’s there.)

2. MapperProxy: is the enhancement of the proxy created above

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    try {

      if (Object.class.equals(method.getDeclaringClass())) {

        return method.invoke(this, args);

      } else if (isDefaultMethod(method)) {

        return invokeDefaultMethod(proxy, method, args);

      }

    } catch (Throwable t) {

      throw ExceptionUtil.unwrapThrowable(t);

    }

   --------------------------

    final MapperMethod mapperMethod = cachedMapperMethod(method);

    return mapperMethod.execute(sqlSession, args);

}

Copy the code

For non-default, non-object methods (that is, our business methods), a MapperMethod is encapsulated and mapperMethod.execute is called

MapperMethod During the execution of a service method, it is encapsulated as MapperMethod. When MapperMethod is executed, Sqlsession is called

public Object execute(SqlSession sqlSession, Object[] args) {

    Object result;

    switch (command.getType()) {

      case SELECT:

.

        result = sqlSession.selectOne(command.getName(), param);

.

        break;

.

}

Copy the code

After a roundabout, we are back to the most basic invocation.

result = sqlSession.selectOne(command.getName(), param);



User user = sqlSession.selectOne("com.wqd.dao.UserMapper.getById".1);

Copy the code

Summary:

  • Sqlsession.selectone (statement.id, parameter)
  • Mapper=User Proxy class getById-"MapperProxy. Invoke method-"MapperMethod.execute()-"Sqlsession.selectone (statement.id, parameter)

Obviously this loop, convenient for developers, but for the system to bring is redundant overhead.

Five, the cache

Mybatis also adds a cache design.

It is divided into level 1 cache and level 2 cache

1. Level 1 cache

See long what appearance first? This is the wrapper of HashMap

public class PerpetualCache implements Cache {

  private String id;

  private Map<Object, Object> cache = new HashMap<Object, Object>();



  public PerpetualCache(String id) {

    this.id = id;

  }

}

Copy the code

Where is it? Exists as an attribute of BaseExecutor.

public abstract class BaseExecutor implements Executor {

 protected BaseExecutor(Configuration configuration, Transaction transaction) {

    this.localCache = new PerpetualCache("LocalCache");

 }



}

Copy the code

Executor mentioned above that Sqlsession capabilities are delegated to the Executor. Executor exists as a property of Sqlsession.

So: MyBatis level 1 cache lifecycle is the same as SqlSession.

2. Level 2 cache

2.1 Basic Information

Level 2 caching is more complex in design than level 1 caching.

Using XML as an example, you need to enable level-2 cache and add it to a namespace.

<setting name="cacheEnabled" value="true"/>

Copy the code
<mapper namespace="mapper.StudentMapper">

    <cache/>

</mapper>

Copy the code


Copy the code

All MappedStatements in a namespace share the same level 2 cache. The life cycle of the level-2 cache follows the life cycle of the entire application. Level-2 cache also shares SqlSession data under the namespace.

When a second-level cache configuration is enabled, its data structure is also PerpetualCache by default. This is the same as level 1 cache.

But to build a two-level cache, MyBatis uses a typical design pattern decorator pattern, enhancing the PerpetualCache layer by layer, making the two-level cache a decorated PerpetualCache with different capabilities for each layer. Level 2 cache is much richer than level 1 cache.

Decoration categories include:

  • LoggingCache: Log function, decoration class, used to record cache hit ratio. If DEBUG mode is enabled, hit ratio logs are generated
  • LruCache: The Cache implementation of the Lru algorithm is used to remove the least recently used Key or Value
  • ScheduledCache: enables the periodic clearing capability
  • BlockingCache: Makes it blocking
Layers of decoration

private Cache setStandardDecorators(Cache cache) {

    try {

      MetaObject metaCache = SystemMetaObject.forObject(cache);

      if(size ! =null && metaCache.hasSetter("size")) {

        metaCache.setValue("size", size);

      }

      if(clearInterval ! =null) {

        cache = new ScheduledCache(cache);

        ((ScheduledCache) cache).setClearInterval(clearInterval);

      }

      if (readWrite) {

        cache = new SerializedCache(cache);

      }

      cache = new LoggingCache(cache);

      cache = new SynchronizedCache(cache);

      if (blocking) {

        cache = new BlockingCache(cache);

      }

      return cache;

    } catch (Exception e) {

      throw new CacheException("Error building standard cache decorators. Cause: " + e, e);

    }

  }

Copy the code
2.2 How to Work

Level 2 caching works in decorator mode again, but this time it’s Executor decorator. Use CachingExecutor to decorate the Executor executing SQL

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {

    executorType = executorType == null ? defaultExecutorType : executorType;

    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;

    Executor executor;

    if (ExecutorType.BATCH == executorType) {

      executor = new BatchExecutor(this, transaction);

    } else if (ExecutorType.REUSE == executorType) {

      executor = new ReuseExecutor(this, transaction);

    } else {

      executor = new SimpleExecutor(this, transaction);

    }

    if (cacheEnabled) {

      executor = new CachingExecutor(executor);/ / decoration

    }

    executor = (Executor) interceptorChain.pluginAll(executor);

    return executor;

  }

Copy the code

When executing a query, the Executor query is executed from the secondary cache first, and executed only when the secondary cache is not available

private Executor delegate;



private TransactionalCacheManager tcm = new TransactionalCacheManager();



public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)

      throws SQLException 
{

        Cache cache = ms.getCache();

.

        List<E> list = (List<E>) tcm.getObject(cache, key);

        if (list == null) {

          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

          tcm.putObject(cache, key, list); // issue #578 and #116

        }

        return list;

      }

    }

    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

  }

Copy the code

Including TransactionalCacheManager properties for the second level cache transaction ability.

public void commit(boolean required) throws SQLException {

    delegate.commit(required);

tcm.commit(); That is, the data is put into the level 2 cache only when the transaction commits

}

Copy the code

To summarize the second level cache

  • The second level cache is a layer of decoration
  • Level 2 caching works by decorating ordinary actuators
  • Decorator actuator is usedTransactionalCacheManagerProvides transaction capability for level 2 caches

Sixth, the plug-in

To sum up the MyBaits plugin in one sentence: Proxy, proxy, proxy, proxy.

Mybatis plug-in principle is also dynamic proxy technology.

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {

.

      executor = new SimpleExecutor(this, transaction);

.

    if (cacheEnabled) {

      executor = new CachingExecutor(executor);

    }

Plug-in entry

    executor = (Executor) interceptorChain.pluginAll(executor);

    return executor;

  }



InterceptorChain

 public Object pluginAll(Object target) 
{

    for (Interceptor interceptor : interceptors) {

      target = interceptor.plugin(target);

    }

    return target;

  }

Copy the code

In the case of the paging plug-in, after the Executor is created, the plugn method of the plug-in is executed, and the plugin.wrap method is called by the plugn method, where we see the JDK dynamic proxy technology for our properties. Create Executor’s proxy class, enhanced with Plugin.

QueryInterceptor

public Object plugin(Object target) 
{

        return Plugin.wrap(target, this);

}





public class Plugin implements InvocationHandler {

public static Object wrap(Object target, Interceptor interceptor) {

Map<Class<? >, Set<Method>> signatureMap = getSignatureMap(interceptor);

Class<? > type = target.getClass();

Class<? >[] interfaces = getAllInterfaces(type, signatureMap);

    if (interfaces.length > 0) {

      return Proxy.newProxyInstance(

          type.getClassLoader(),

          interfaces,

          new Plugin(target, interceptor, signatureMap));

    }

    return target;

  }

}

Copy the code

The final chain of execution: Executor proxy method — Plugin. Invoke method — Plugin. Intercept method — Executor class method

7. Result mapping

Since the resulting mapping is a bit complicated, let’s write another article about it in detail

Eight, summary

  • Mybatis can be said to use the decorator mode, dynamic proxy to the extreme. It is worth our learning.

  • The framework should leave the application with the basic unit of operation, namely the concept of domain value, the application only needs to define the raw material, and then the black box operation.

Such as:

  • Spring’sBeanDefinition
  • The MybatisMappedStatement

Mybatis is a very readable framework, compared to Spring, Mybatis as the first source code learning framework, very very suitable.


If there are any mistakes in this article, please comment, appreciate! If you think the article is good, give it a thumbs up

Long press copy plus my wechat: WQD147 pull you into the partnership, chat source

Add me into the group