Mybatis executes SQL process instance parsing

This article mainly introduces the process instance analysis of mybatis executing SQL. The example code is introduced in great detail, which has certain reference value for everyone's study or work. Friends in need can refer to it

This blog focuses on the SQL execution process of mybatis. Details about caching and dynamic SQL generation during execution are not reflected in this blog. Please write a blog analysis separately later.

Or take the previous query as the column child:

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();
  }

}

As mentioned earlier, you can perform various CRUD operations after you get sqlsession, so we start from sqlsession The getmapper method starts analysis to see how the whole SQL execution process is.

Get mapper

Enter sqlsession Getmapper method, you will find that the getmapper method of the configuration object is called:

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 the 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);
  }
 }

Enter the newinstance method of 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 the dynamic proxy class mapperproxy. All methods calling the mapper interface will first call the invoke method of the proxy class (note that since the mapper interface in mybatis does not implement the class, there is no delegate class in the mapperproxy proxy proxy object, that is, mapperproxy does the proxy class and delegate class). Well, let's focus on the invoke method.

//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,sqlSession.getConfiguration());
   methodCache.put(method,mapperMethod);
  }
  return mapperMethod;
 }

 @UsesJava7
 private Object invokeDefaultMethod(Object proxy,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();
 }
}

Therefore, you need to enter the execute method of mappermethod:

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, through layer by layer calls, we will finally come to the doquery method. Let's find an excutor to see the implementation of the doquery method. I chose simpleexecution:

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 an implementation class of statementhandler, preparedstatementhandler (which is also our most commonly used and encapsulated Preparedstatement), and see how it handles it:

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

At this point, the whole calling process ends.

Simple summary

Here is a brief summary of the process of obtaining sqlsession:

The above is the process of obtaining sqlsession. The following is a summary of the SQL execution process introduced in this blog:

Important category

All configuration information of configuration mybatis is maintained in the configuration object.

The above is the whole content of this article. I hope it will help you in your study, and I hope you will support us a lot.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>