MyBatis09-"General Source Code Guide: Detailed Explanation of MyBatis Source Code" notes-cursor package

This series of articles is my notes and summary from the book "General Source Code Guide: Detailed Explanation of MyBatis Source Code
" . This book is based on MyBatis-3.5.2 version. The author of the book is Brother Yi . The link is Brother Yi's Weibo in CSDN. But among all the articles I read, there was only one that briefly introduced this book. It doesn’t reveal too much about the charm of the book. Next, I will record my learning summary. If the author thinks that I have infringed the copyright, please contact me to delete it. Thanks again to Brother Yi for providing learning materials. This explanation will accompany the entire series of articles. Respect the originality . I have purchased the revised book on WeChat Reading.
Copyright statement: This article is an original article by CSDN blogger "Architect Yi Ge" and follows the CC 4.0 BY-SA copyright agreement. Please attach the original source link and this statement when reprinting.
Original link: https://blog.csdn.net/onlinedct/article/details/107306041

1.cursor package

The Iterable interface and the Iterator interface are two interfaces that everyone often comes into contact with. They both represent capabilities related to iterative operations.
Iterator means "iterator" and Iterable means "iterable". If a class is an iterator, iteration operations can be implemented based on it; and if a class can give an iterator that iterates the elements within itself, it is iterable.

Therefore, the Iterable interface is very simple. It mainly defines an Iterator<T>iterator abstract method to return an Iterator object (forEach method and spliterator method were added in Jdk 1.8).

The Iterator interface represents an iterator over a collection, and the Iterator interface defines the most important methods of the iterator.

  • boolean hasNext: Determines whether there are any un-iterated elements in the current iteration.
  • E next: Returns the next element in the iteration.
  • default void remove: Removes the last element returned by the iterator from the collection pointed to by the iterator. This operation is not supported by default as it can easily mess up the iteration.

In programming development, the Iterable interface and Iterator interface are often used. Our commonly used for-each is implemented based on these two interfaces.

List<User> userList = new ArrayList<>();
for(User user : userList){
    
    
	System.out.println(user);
}

After compilation it will become:

List<User> userList = new ArrayList<>();
Iterator var2 = userList.iterator();
while(var2.hasNext()){
    
    
	User user = (User)var2.next();
	System.out.println(user);
}

The code can be converted after compilation. This is because for-each is an 语法糖operation and will be converted into basic syntax by the compiler during the compilation stage. Therefore, when we use the for-each operation to traverse the elements in the List, List, as a subclass of the Iterable interface, first gives an Iterator object through the iterator method, and then implements traversal of all elements in the List based on the Iterator object.

To check the true form of a piece of Java code in a class file, the easiest way is to use integrated development software to find the corresponding class file in the target directory and view it. You can also use the javac command to compile it yourself and then open the corresponding class through related tools. File view.

Finally, let us summarize that the Iterable interface represents a class that is iterable, and the Iterator interface represents a class that is an iterator.

  • If a class can provide an iterator (through the iterator method) for iterating over the elements in a collection, then the class can inherit the Iterable interface.
  • If a class itself is an iterator and can iterate over a collection, then the class can inherit the Iterator interface.

2. The use of cursors in MyBatis

When using MyBatis for database queries, a large number of results are often queried. In the example shown below, we query a large number of User objects and use List to receive these objects.

List<User> userList = userMapper.queryUserBySchoolName(userParam);

But sometimes, we want to read and process the query results one by one instead of reading the entire result set at once. Because the former can reduce memory usage, it is very necessary when processing large amounts of data. Cursors can help us achieve this purpose. They allow us to take out one result from the result set at a time.

It is very simple to use cursors to query in MyBatis. The mapping file does not require any changes. You only need to indicate that the return value type is Cursor in the mapping interface:

 Cursor<User> queryUserBySchoolName(User user);

Then, you can use the following code to receive and process the results.

 UserMapper userMapper = session.getMapper(UserMapper.class);
 User userParam = new User();
 userParam.setSchoolName("Sunny School");
 Cursor<User> userCursor = userMapper.queryUserBySchoolName(userParam);
 for (User user : userCursor) {
    
    
     System.out.println("name : " + user.getName() + " ;  email : " + user.getEmail());
 }

3. Cursor interface

The source code in the cursor package is very simple, with only one Cursor interface and the default implementation class DefaultCursor.
The Cursor interface inherits the java.io.Closeable interface and the java.lang.Iterable interface. The Closeable interface indicates that a class can be closed. Calling the close method in the Closeable interface can release the resources held by the object of the class. The Iterable interface represents a class that can be iterated, so that the for-each operation can be used on objects of this class.

public interface Cursor<T> extends Closeable, Iterable<T> {
    
    
  /**
   * 游标是否开启
   * @return 是否开启
   */
  boolean isOpen();
  /**
   * 是否已经完成了所有遍历
   * @return 是否完成了所有遍历
   */
  boolean isConsumed();
  /**
   * 返回当前元素的索引
   * @return 当前元素的索引
   */
  int getCurrentIndex();
}

4.Default cursor

The DefaultCursor class is the default cursor. The following figure is a class diagram of the DefaultCursor related class. As can be seen from the class diagram, the DefaultCursor class directly or indirectly inherits the three interfaces Cursor, Closeable, and Iterable, which means that it must implement all methods defined by these three interfaces.
Insert image description here

4.1 CursorStatus internal class

The CursorStatus internal class is very simple and is an enumeration class that represents the cursor status.

  private enum CursorStatus {
    
    
    /**
     * A freshly created cursor, database ResultSet consuming has not started.
     */
    CREATED, // 表征游标新创建,结果集尚未消费
    /**
     * A cursor currently in use, database ResultSet consuming has started.
     */
    OPEN, // 表征游标正在被使用中,结果集正在被消费
    /**
     * A closed cursor, not fully consumed.
     */
    CLOSED, // 表征游标已经被关闭,但其中的结果集未被完全消费
    /**
     * A fully consumed cursor, a consumed cursor is always closed.
     */
    CONSUMED // 表征游标已经被关闭,其中的结果集已经被完全消费
  }

4.2 ObjectWrapperResultHandler inner class

The ObjectWrapperResultHandler class inherits the ResultHandler interface and is a simple result processor.
The ResultHandler interface is in the session package. The ResultHandler interface defines a handleResult method for processing a single result. The input parameter of this method is a ResultContext object. The ResultContext class is the result context from which a result can be retrieved.

  private static class ObjectWrapperResultHandler<T> implements ResultHandler<T> {
    
    
    private T result;
    /**
     * 从结果上下文中取出并处理结果
     * @param context 结果上下文
     */
    @Override
    public void handleResult(ResultContext<? extends T> context) {
    
    
      // 取出结果上下文中的一条结果
      this.result = context.getResultObject();
      // 关闭结果上下文
      context.stop();
    }
  }

The ObjectWrapperResultHandler internal class just takes out a result in the result context and puts it into its own result attribute without further processing.

4.3 CursorIterator inner class

The CursorIterator class inherits the Iterator interface and is an iterator class.
The DefaultCursor class indirectly inherits the Iterable interface, which means that it must return an Iterator object through the iterator method. The Iterator object returned by the DefaultCursor class is the CursorIterator object.
As an iterator, the CursorIterator class implements the hasNext method to determine whether the next element exists and the next method to return the next element.

  private class CursorIterator implements Iterator<T> {
    
    
    // 缓存下一个要返回的对象,在next操作中完成写入
    T object;
    // next方法中返回的对象的索引
    int iteratorIndex = -1;
    /**
     * 判断是否还有下一个元素,如果有则顺便写入object中
     * @return 是否还有下一个元素
     */
    @Override
    public boolean hasNext() {
    
    
      // 如果object!=null,则显然有下一个对象,就是object本身
      if (object == null) {
    
    
        // 判断是否还能获取到新的,顺便放到object中
        object = fetchNextUsingRowBound();
      }
      return object != null;
    }
    /**
     * 返回下一个元素
     * @return 下一个元素
     */
    @Override
    public T next() {
    
    
      T next = object;

      if (next == null) {
    
     // object中无对象
        // 尝试去获取一个
        next = fetchNextUsingRowBound();
      }
      if (next != null) {
    
    
        // 此时,next中是这次要返回的对象。object要么本来为null,要么已经取到next中。故清空
        object = null;
        iteratorIndex++;
        // 返回next中的对象
        return next;
      }
      throw new NoSuchElementException();
    }
    /**
     * 删除当前的元素。不允许该操作,故直接抛出异常
     */
    @Override
    public void remove() {
    
    
      throw new UnsupportedOperationException("Cannot remove element from Cursor");
    }
  }

In the CursorIterator class, whether it is the hasNext method to determine whether there is a next element or the next method to get the next element, the fetchNextUsingRowBound method is called. This method is a very important method in the external class DefaultCursor.

4.4 DefaultCursor external class

  // 结果集处理器
  private final DefaultResultSetHandler resultSetHandler;
  // 该结果集对应的ResultMap信息,来源于Mapper中的<ResultMap>节点
  private final ResultMap resultMap;
  // 返回结果的详细信息
  private final ResultSetWrapper rsw;
  // 结果的起止信息
  private final RowBounds rowBounds;
  // ResultHandler的子类,起到暂存结果的作用
  private final ObjectWrapperResultHandler<T> objectWrapperResultHandler = new ObjectWrapperResultHandler<>();
  // 内部迭代器
  private final CursorIterator cursorIterator = new CursorIterator();
  // 迭代器存在标志位
  private boolean iteratorRetrieved;
  // 游标状态
  private CursorStatus status = CursorStatus.CREATED;
  // 记录已经映射的行
  private int indexWithRowBound = -1;

Most methods in the DefaultCursor class are used to implement the three interfaces Cursor, Closeable, and Iterable. Among them, the iterator method defined in the Iterable interface uses the iteratorRetrieved variable in this method to ensure that the iterator can only be given once, preventing access confusion caused by multiple times.

  /**
   * 返回迭代器
   * @return 迭代器
   */
  @Override
  public Iterator<T> iterator() {
    
    
    if (iteratorRetrieved) {
    
     // 如果迭代器已经给出
      throw new IllegalStateException("Cannot open more than one iterator on a Cursor");
    }
    if (isClosed()) {
    
     // 如果游标已经关闭
      throw new IllegalStateException("A Cursor is already closed.");
    }
    // 表明迭代器已经给出
    iteratorRetrieved = true;
    // 返回迭代器
    return cursorIterator;
  }

In addition, the important methods in the DefaultCursor class are the fetchNextUsingRowBound method and its sub-method fetchNextObjectFromDatabase method. The fetchNextObjectFromDatabase method will take out a result from the result set returned by the database query every time it is called, and the fetchNextUsingRowBound method will take into account the boundary constraints during the query. So these two methods jointly complete the function of extracting one result from the result set at a time while satisfying the boundary constraints.

  /**
   * 考虑边界限制(翻页限制),从数据库中获取下一个对象
   * @return 下一个对象
   */
  protected T fetchNextUsingRowBound() {
    
    
    // 从数据库查询结果中取出下一个对象
    T result = fetchNextObjectFromDatabase();
    while (result != null && indexWithRowBound < rowBounds.getOffset()) {
    
     // 如果对象存在但不满足边界限制,则持续读取数据库结果中的下一个,直到边界起始位置
      result = fetchNextObjectFromDatabase();
    }
    return result;
  }

  /**
   * 从数据库获取下一个对象
   * @return 下一个对象
   */
  protected T fetchNextObjectFromDatabase() {
    
    
    if (isClosed()) {
    
    
      return null;
    }
    try {
    
    
      status = CursorStatus.OPEN;
      if (!rsw.getResultSet().isClosed()) {
    
     // 结果集尚未关闭
        // 从结果集中取出一条记录,将其转化为对象,并存入到objectWrapperResultHandler中
        resultSetHandler.handleRowValues(rsw, resultMap, objectWrapperResultHandler, RowBounds.DEFAULT, null);
      }
    } catch (SQLException e) {
    
    
      throw new RuntimeException(e);
    }

    // 获得存入到objectWrapperResultHandler中的对象
    T next = objectWrapperResultHandler.result;
    if (next != null) {
    
     // 读到了新的对象
      // 更改索引,表明记录索引加一
      indexWithRowBound++;
    }

    if (next == null || getReadItemsCount() == rowBounds.getOffset() + rowBounds.getLimit()) {
    
     // 没有新对象或者已经到了rowBounds边界
      // 游标内的数据已经消费完毕
      close();
      status = CursorStatus.CONSUMED;
    }
    // 清除objectWrapperResultHandler中的该对象,已准备迎接下一对象
    objectWrapperResultHandler.result = null;
    return next;
  }

The Chinese meaning of the fetchNextObjectFromDatabase method is "get the next object from the database". Judging from the method name, this method seems to query the next record from the database. But in fact this is not the case, this method does not trigger a database query operation. Because, before this method is called, the result set of the database query has been completely stored in the rsw variable. The fetchNextObjectFromDatabase method only fetches the next record from the result set, rather than actually querying the database for the next record.

Guess you like

Origin blog.csdn.net/d495435207/article/details/131116956