Process analysis Sql execution of 02-MyBatis



This blog focuses on MyBatis Sql execution process, and about the cache in the implementation process, dynamic SQl generation and other details are not reflected in this blog, then write back the corresponding content analysis it alone blog.

Or in the previous query as Liezi:

public class UserDaoTest {

    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void setUp() throws Exception{
        ClassPathResource resource = new ClassPathResource("mybatis-config.xml");
        InputStream inputStream = resource.getInputStream();
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void selectUserTest(){
        String id = "{0003CCCA-AEA9-4A1E-A3CC-06D884BA3906}";
        SqlSession sqlSession = sqlSessionFactory.openSession();
        CbondissuerMapper cbondissuerMapper = sqlSession.getMapper(CbondissuerMapper.class);
        Cbondissuer cbondissuer = cbondissuerMapper.selectByPrimaryKey(id);
        System.out.println(cbondissuer);
        sqlSession.close();
    }

}

The previously mentioned various CRUD operations can be carried out after get sqlSession, so we started to analyze the sqlSession.getMapper this approach, the Sql look at the whole execution process is kind of how.

Get Mapper

Enter sqlSession.getMapper method, you will find tune is getMapper method Configration object:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    //mapperRegistry实质上是一个Map,里面注册了启动过程中解析的各种Mapper.xml
    //mapperRegistry的key是接口的全限定名,比如com.csx.demo.spring.boot.dao.SysUserMapper
    //mapperRegistry的Value是MapperProxyFactory,用于生成对应的MapperProxy(动态代理类)
    return mapperRegistry.getMapper(type, sqlSession);
}

Enter getMapper method:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    //如果配置文件中没有配置相关Mapper,直接抛异常
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      //关键方法
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

NewInstance method of entering MapperProxyFactory:

public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }

  //生成Mapper接口的动态代理类MapperProxy
  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }
  
  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

}

The following is a dynamic proxy class MapperProxy, call Mapper interface all methods will first call the method to invoke the proxy class (note due Mybatis Mapper interface is not achieved in the class, so MapperProxy the proxy object not entrusted class, that MapperProxy dry the proxy class and delegate that sort of thing). Well look invoke the focus of the following methods.

//MapperProxy代理类
public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  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);
    }
    //获取MapperMethod,并调用MapperMethod
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

  @UsesJava7
  private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
      throws Throwable {
    final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
        .getDeclaredConstructor(Class.class, int.class);
    if (!constructor.isAccessible()) {
      constructor.setAccessible(true);
    }
    final Class<?> declaringClass = method.getDeclaringClass();
    return constructor
        .newInstance(declaringClass,
            MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
                | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
        .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
  }

  /**
   * Backport of java.lang.reflect.Method#isDefault()
   */
  private boolean isDefaultMethod(Method method) {
    return ((method.getModifiers()
        & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC)
        && method.getDeclaringClass().isInterface();
  }
}

So here you need to enter the execute method MapperMethod of:

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //判断是CRUD那种方法
    switch (command.getType()) {
      case INSERT: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName() 
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }

Then, layer by layer by calling, eventually came to doQuery method, here we look at the implementation to easily find Excutor doQuery approach, and I chose here SimpleExecutor:

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      //内部封装了ParameterHandler和ResultSetHandler
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      //StatementHandler封装了Statement, 让 StatementHandler 去处理
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

Next, let's take a look at StatementHandler an implementation class PreparedStatementHandler (this is our most commonly used, the package is PreparedStatement), take a look at how to deal with it so that the:

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
     //到此,原形毕露, PreparedStatement, 这个大家都已经滚瓜烂熟了吧
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    //结果交给了ResultSetHandler 去处理,处理完之后返回给客户端
    return resultSetHandler.<E> handleResultSets(ps);
  }

This, the entire call process ends.

A brief summary

SqlSession get here in conjunction with the flow, do simple summary:

  • SqlSessionFactoryBuilder analytical profile, including attributes configuration, alias configuration, the interceptor configuration, environment (data sources and transaction managers), Mapper configuration like; After parsing the configuration will generate a Configration object that contains the MyBatis all required configuration, and then creates a SqlSessionFactory this Configration object with the object that contains Configration object;
  • After getting a SqlSessionFactory, calls SqlSessionFactory of openSesison method, which creates a Sql actuator (Executor component contains a Transaction object), the agent will execute Sql interceptor method you configure .
  • After obtaining the above Sql actuators, creates a SqlSession (use DefaultSqlSession default), this SqlSession also contains Executor objects and objects created Configration above, it can get through SqlSession global configuration;
  • You will be able to perform a variety of CRUD methods after obtaining SqlSession object.

These are obtained SqlSession process, the implementation process of this blog Sql described under the following summary:

  • Call SqlSession of getMapper way, a dynamic proxy object Mapper interface MapperProxy, all calls will Mapper interface method calls to MapperProxy invoke method (dynamic proxy mechanism);
  • invoke method MapperProxy the only way to do is to create a MapperMethod object and then call this object execute methods, sqlSession will execute as a method to the Senate;
  • Down, down modulation layer assembly will enter Executor (if dynamic configuration widget will Executor Agent) The query method, this method creates a StatementHandler object that will encapsulate both ParameterHandler ResultSetHandler and objects. Call the compiler parameters and parameter values ​​StatementHandler pre-set, using ParameterHandler to set parameters to sql.

Executor component has two direct implementation class and is BaseExecutor CachingExecutor. CachingExecutor static agents BaseExecutor. Executor component encapsulates Transction assembly, Transction assembly has a dispensing assembly Datasource.

  • Call StatementHandler CRUD method for obtaining the results, ResultSetHandler results encapsulation conversion request ends.

Executor, StatementHandler, ParameterHandler, ResultSetHandler, Mybatis plug above four components will be dynamic agent.

Important class

  • MapperProxyFactory

  • MapperProxy

  • folders Method

  • SqlSession: as the main top-level API MyBatis work, presentation and session database interaction, to complete the necessary database CRUD functions;

  • Executor: MyBatis actuators, is the core of MyBatis scheduling, responsible for generating SQL statements and query cache maintenance;

    StatementHandler JDBC Statement encapsulates the operation, the operation is responsible for the JDBC statement, such as setting parameters, converting the result set to a Statement List collection.
    ParameterHandler user is responsible for passing parameters into parameters required JDBC Statement,
    ResultSetHandler responsible for converting JDBC ResultSet returned result set object into a set of List type;
    the TypeHandler responsible for mapping and data conversion between the java jdbc types and data types
    MappedStatement MappedStatement maintaining a <select | insert update | | delete > package node,
    SqlSource responsible according parameterObject user transmitted dynamically generate SQL statements, the encapsulation information to BoundSql object and returns
    BoundSql represented dynamically generated SQL statements and the corresponding Parameter information

    All Configuration MyBatis configuration information is maintained in the Configuration object.

reference

  • https://www.cnblogs.com/dongying/p/4031382.html
  • https://blog.csdn.net/qq_38409944/article/details/82494187

Guess you like

Origin www.cnblogs.com/54chensongxia/p/11850470.html