Mybatis finishing

Time:2022-1-9

Mybatis framework and principle analysis

Mybatis is an excellent persistence layer framework that supports customized SQL, stored procedures and advanced mapping. It mainly completes two things:

  1. Encapsulating JDBC operations
  2. Use reflection to get through the mutual conversion between Java classes and SQL statements

The main design purpose of mybatis is to make it more convenient for us to manage the input and output data when executing SQL statements. Therefore, the core competitiveness of mybatis is to write SQL conveniently and obtain the execution results of SQL conveniently.
Configuration of mybatis

Like most other frameworks, mybatis framework requires a configuration file, which is roughly as follows:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <settings>
        <setting name="cacheEnabled" value="true"/>
        <setting name="lazyLoadingEnabled" value="false"/>
        <!--< setting name="logImpl" value="STDOUT_LOGGING"/> <!–  Print log information – > -- >
    </settings>

    <typeAliases>
        <typeAlias type="com.luo.dao.UserDao" alias="User"/>
    </typeAliases>

    <environments default="development">
        <environment>
            <transactionManager type="JDBC"/> <!-- Transaction management type -- >
            <dataSource type="POOLED">
                <property name="username" value="luoxn28"/>
                <property name="password" value="123456"/>
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://192.168.1.150/ssh_study"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="userMapper.xml"/>
    </mappers>

</configuration>

Among the above configurations, the most important is the configuration of database parameters, such as user name and password. If the mapper file corresponding to the data table is configured, it needs to be added to the < mappers > node.

Key members of mybatis

  • All configuration information of configuration mybatis is saved in the configuration object, and most configurations in the configuration file are stored in this class
  • As the main top-level API of mybatis, sqlsession represents the session when interacting with the database and completes the necessary database addition, deletion, modification and query functions
  • Executor mybatis executor is the core of mybatis scheduling and is responsible for generating SQL statements and maintaining query cache
  • Statementhandler encapsulates the JDBC statement operation and is responsible for the operation of JDBC statement, such as setting parameters
  • Parameterhandler is responsible for converting the parameters passed by the user into the data type corresponding to JDBC statement
  • Resultsethandler is responsible for converting the resultset result set object returned by JDBC into a list type collection
  • Typehandler is responsible for the mapping and conversion between Java data types and JDBC data types (also known as data table column types)
  • Mappedstatement mappedstatement maintains the encapsulation of a < select | update | delete | Insert > node
  • Sqlsource is responsible for dynamically generating SQL statements according to the parameterobject passed by the user, encapsulating the information into the boundsql object and returning
  • Boundsql represents dynamically generated SQL statements and corresponding parameter information

The above main members are basically involved in a database operation. In the SQL operation, we should focus on when the SQL parameters are set and how the result set is converted into a JavaBean object. These two processes exactly correspond to the processing logic in the statementhandler and resultsethandler classes.

Mybatis finishing

image.png

Initialization of mybatis

The initialization process of mybatis is actually the process of parsing the configuration file and initializing the configuration. The initialization process of mybatis can be expressed in the following lines of code:

String resource = "mybatis.xml";

//Load the configuration file for mybatis (it also loads the associated mapping file)
InputStream inputStream = null;
try {
    inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
    e.printStackTrace();
}

//Build a factory for sqlsession
sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

The sqlsessionfactory builder object is created first, and then it creates the sqlsessionfactory. The builder mode is used here. The simplest understanding of the builder mode is that instead of manually creating new objects, other classes create objects.

//Sqlsessionfactorybuilder class
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        return build(parser.parse()); //  Started parsing:)
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
        ErrorContext.instance().reset();
        try {
            inputStream.close();
        } catch (IOException e) {
            // Intentionally ignore. Prefer previous error.
        }
    }
}

The xmlconfigbuilder object will parse the XML configuration file, which is actually the parsing operation of the configuration node.

//Xmlconfigbuilder class
public Configuration parse() {
    if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}

private void parseConfiguration(XNode root) {
    try {
        //issue #117 read properties first
        propertiesElement(root.evalNode("properties"));
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        loadCustomVfs(settings);
        typeAliasesElement(root.evalNode("typeAliases"));
        pluginElement(root.evalNode("plugins"));
        objectFactoryElement(root.evalNode("objectFactory"));
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        reflectorFactoryElement(root.evalNode("reflectorFactory"));
        settingsElement(settings);
        // read it after objectFactory and objectWrapperFactory issue #631

        /*Processing environment node data*/
        environmentsElement(root.evalNode("environments"));
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        typeHandlerElement(root.evalNode("typeHandlers"));
        mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}

Under the configuration node, the properties / settings // Mappers and other nodes are configured. When parsing the environments node, the transaction manager will be created according to the configuration of the transactionmanager, and the datasource object will be created according to the configuration of the datasource, which contains the information related to database login. When parsing the mappers node, all mapper files under the node will be read, parsed, and the parsed results will be saved in the configuration object.

//Xmlconfigbuilder class
private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
        if (environment == null) {
            environment = context.getStringAttribute("default");
        }
        for (XNode child : context.getChildren()) {
            String id = child.getStringAttribute("id");
            if (isSpecifiedEnvironment(id)) {

                /*Create transaction manager*/
                TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
                DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
                DataSource dataSource = dsFactory.getDataSource();

                /*Builder pattern design pattern*/
                Environment.Builder environmentBuilder = new Environment.Builder(id)
                        .transactionFactory(txFactory)
                        .dataSource(dataSource);
                configuration.setEnvironment(environmentBuilder.build());
            }
        }
    }
}

//Parsing a separate mapper file
private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser. parse(); //  Started parsing mapper file:)
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }

After parsing the mybatis configuration file, the configuration initialization is completed, and then sqlsession is created according to the configuration object. When you get here, the journey of mybatis initialization has been completed.

//Sqlsessionfactorybuilder class
public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}

SQL query process of mybatis

The execution of SQL statements is an important responsibility of mybatis. This process is to operate by encapsulating JDBC, and then use java reflection technology to complete the mutual conversion between JavaBean objects and database parameters. This mapping relationship is completed by typehandler objects. When obtaining the metadata corresponding to the data table, the database types of all columns of the table will be saved, The general logic is as follows:

/* Get resultSet metadata */
ResultSetMetaData metaData = resultSet.getMetaData();
int column = metaData.getColumnCount();

for (int i = 1; i <= column; i++) {
    JdbcType jdbcType = JdbcType.forCode(metaData.getColumnType(i));
    typeHandlers.add(TypeHandlerRegistry.getTypeHandler(jdbcType));

    columnNames.add(metaData.getColumnName(i));
}

Use the following code for SQL query:

sqlSession = sessionFactory.openSession();

User user = sqlSession.selectOne("com.luo.dao.UserDao.getUserById", 1);
System.out.println(user);

The process of creating sqlsession is actually to create the corresponding class according to the configuration in the configuration, and then return the created sqlsession object. The selectOne method is called for SQL query, and the selectOne method finally calls selectList. In selectList, the MappedStatement object stored in configuration is queried. The configuration of a SQL statement in mapper file corresponds to a MappedStatement object, and then the executor is called to query operation.

//Defaultsqlsession class
public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    List<T> list = this.<T>selectList(statement, parameter);
    if (list.size() == 1) {
        return list.get(0);
    } else if (list.size() > 1) {
        throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
        return null;
    }
}

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
        MappedStatement ms = configuration.getMappedStatement(statement);
        return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}

In the query operation, the executor will first query whether the cache hits. If it hits, it will be returned directly. Otherwise, it will be queried from the database.

//Cachengexecution class
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    /*Get SQL of associated parameters, boundsql*/
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    /*Create cache key value*/
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    /*Get L2 cache instance*/
    Cache cache = ms.getCache();
    if (cache != null) {
        flushCacheIfRequired(ms);
        if (ms.isUseCache() && resultHandler == null) {
            ensureNoOutParams(ms, parameterObject, boundSql);
            @SuppressWarnings("unchecked")
            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);
}

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    /**
     *First, insert a placeholder into the localcache
     */
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
        list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
        localCache.removeObject(key);
    }

    /*Write data to the cache, that is, cache query results*/
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
        localOutputParameterCache.putObject(key, parameter);
    }
    return list;
}

The real doquery operation is completed by the simplyexecution agent. There are two sub processes in this method, one is the setting of SQL parameters, the other is the encapsulation of SQL query operation and result set.

//Simpleexecution class
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();
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);

        /*Sub process 1: setting of SQL query parameters*/
        stmt = prepareStatement(handler, ms.getStatementLog());

        /*Sub process 2: SQL query operation and result set encapsulation*/
        return handler.<E>query(stmt, resultHandler);
    } finally {
        closeStatement(stmt);
    }
}

Setting of SQL query parameters in sub process 1:

First get the database connection, then prepare the statement, and then set the parameter value in the SQL query. When a connection is opened, it will not be closed after use, but will be stored. When the connection needs to be opened next time, it will be returned directly.

//Simpleexecution class
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    /*Get connection*/
    Connection connection = getConnection(statementLog);

    /*Prepare statement*/
    stmt = handler.prepare(connection, transaction.getTimeout());

    /*Set parameter values in SQL query*/
    handler.parameterize(stmt);
    return stmt;
}

//Defaultparameterhandler class
public void setParameters(PreparedStatement ps) {
    /**
     *Set the SQL parameter value, read the parameter value and type from parametermapping, and then set it to the SQL statement
     */
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
        for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            if (parameterMapping.getMode() != ParameterMode.OUT) {
                Object value;
                String propertyName = parameterMapping.getProperty();
                if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                    value = boundSql.getAdditionalParameter(propertyName);
                } else if (parameterObject == null) {
                    value = null;
                } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                    value = parameterObject;
                } else {
                    MetaObject metaObject = configuration.newMetaObject(parameterObject);
                    value = metaObject.getValue(propertyName);
                }
                TypeHandler typeHandler = parameterMapping.getTypeHandler();
                JdbcType jdbcType = parameterMapping.getJdbcType();
                if (value == null && jdbcType == null) {
                    jdbcType = configuration.getJdbcTypeForNull();
                }
                try {
                    typeHandler.setParameter(ps, i + 1, value, jdbcType);
                } catch (TypeException e) {
                    throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                } catch (SQLException e) {
                    throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                }
            }
        }
    }
}

Sub process 2 encapsulation of SQL query result set:

//Simpleexecution class
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    //Execute query operation
    ps.execute();
    //Execute result set encapsulation
    return resultSetHandler.<E> handleResultSets(ps);
}

//Defaultresetsethandler class
public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<Object>();

    int resultSetCount = 0;
    /**
     *Get the first resultset and the metadata data of the database, including data table column name, column type, class sequence number, etc.
     *This information is stored in resultsetwrapper
     */
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }

Resultsetwrapper is the wrapper class of resultset. Call getfirstresultset method to get the first resultset and obtain the metadata data of the database, including data table column name, column type, class serial number, etc. these information are stored in resultsetwrapper class. Then the handleResultSet method is invoked to encapsulate the result set.

//Defaultresultsethandler class
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
    try {
        if (parentMapping != null) {
            handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
        } else {
            if (resultHandler == null) {
                DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
                handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
                multipleResults.add(defaultResultHandler.getResultList());
            } else {
                handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
            }
        }
    } finally {
        // issue #228 (close resultsets)
        closeResultSet(rsw.getResultSet());
    }
}


Here, the handlerowvalues method is called to set the result value.
//Defaultresultsethandler class
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    if (resultMap.hasNestedResultMaps()) {
        ensureNoRowBounds();
        checkResultHandler();
        handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
        //Encapsulate data
        handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
}

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
        throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
    skipRows(rsw.getResultSet(), rowBounds);
    while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
        ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
        Object rowValue = getRowValue(rsw, discriminatedResultMap);
        storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
    }
}

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    //Createresultobject is the newly created object and the class corresponding to the data table
    Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
    if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
        final MetaObject metaObject = configuration.newMetaObject(rowValue);
        boolean foundValues = this.useConstructorMappings;
        if (shouldApplyAutomaticMappings(resultMap, false)) {
            //Here, the data is filled in. The metaobject contains the resultobject information
            foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
        }
        foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
        foundValues = lazyLoader.size() > 0 || foundValues;
        rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;
    }
    return rowValue;
}

private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
    List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
    boolean foundValues = false;
    if (autoMapping.size() > 0) {
        //The for loop is called here. Because there are 7 columns in the user table, it is called 7 times
        for (UnMappedColumnAutoMapping mapping : autoMapping) {
            //Here, the query result in the resultset is converted to the corresponding actual type
            final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
            if (value != null) {
                foundValues = true;
            }
            if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
                // gcode issue #377, call setter on nulls (value is not 'found')
                metaObject.setValue(mapping.property, value);
            }
        }
    }
    return foundValues;
}

mapping. typeHandler. GetResult will get the actual type of the query result value. For example, if the ID field in our user table is of type int, it corresponds to the integer type in Java, and then call statement GetInt (“Id”) to get its int value, which is of type integer. metaObject. The setValue method will set the obtained integer value to the corresponding field in the Java class.

//Metaobject class
public void setValue(String name, Object value) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
        MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
        if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
            if (value == null && prop.getChildren() != null) {
                // don't instantiate child path if value is null
                return;
            } else {
                metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
            }
        }
        metaValue.setValue(prop.getChildren(), value);
    } else {
        objectWrapper.set(prop, value);
    }
}

metaValue. Finally, the setValue method will call the set method of the corresponding data field in the Java class, which completes the Java class encapsulation process of the SQL query result set. Finally, post a snapshot of the call stack reaching the set method of the Java class:

Mybatis finishing

Mybatis cache

Mybatis provides query caching to reduce database pressure and improve performance. Mybatis provides L1 cache and L2 cache.

Mybatis finishing

The first level cache is a sqlsession level cache. Each sqlsession object has a hash table for caching data. The cache is not shared between different sqlsession objects. The same sqlsession object executes the same SQL query twice, and caches the results after the first query is executed, so that the second query does not need to query the database, but directly returns the cached results. Mybatis enables L1 cache by default.

L2 cache is a mapper level cache. L2 cache is cross sqlsession. Multiple sqlsession objects can share the same L2 cache. Different sqlsession objects execute the same SQL statement twice. The first query will cache the query results, and the second query can directly return the results in the secondary cache. Mybatis does not enable L2 cache by default. You can use the following configuration in the configuration file to enable L2 cache:

<pre style="margin: 0px; padding: 0px; overflow: auto; word-wrap: break-word; font-family: &quot;Courier New&quot; !important; font-size: 12px !important;"><settings>
    <setting name="cacheEnabled" value="true"/>
</settings></pre>

When the SQL statement is updated (delete / add / update), the corresponding cache will be emptied to ensure that the latest data is stored in the cache. Mybatis’s secondary cache is not friendly to the implementation of fine-grained data level cache, such as the following requirements: cache commodity information. Due to the large number of commodity information query visits, users are required to query the latest commodity information every time, At this time, if you use the secondary cache of mybatis, you can’t refresh the cache information of a commodity without refreshing the information of other commodities. Because the secondary cache area of mybaits is divided by mapper, when a commodity information changes, the cache data of all commodity information will be cleared. To solve such problems, we need to cache the data according to the needs in the business layer, and the specific business needs to be implemented.

The above content is transferred fromhttps://www.cnblogs.com/luoxn28/p/6417892.htmlReprint is only for learning and has no other intention

Mybatis source code analysis diagram

Mybatis finishing

Mybatis core process source code analysis process diagram png

Source code compilation and download

https://github.com/mybatis/mybatis-3

https://github.com/mybatis/parent(dependent)

Mybatis finishing

image.png
Mybatis finishing

image.png

You can import the idea directly or download it as a zip package (recommended).

Mybatis finishing

image.png

Mybatis source code attacks the parent project. You need to compile the parent project before compiling mybatis, as follows

Solve the problem of parent dependency:

In the process of building, the POM cannot be found The dependent parent module mybatis parent in XML

We need to clone the paren project to the local directory: git clonehttps://github.com/mybatis/parent.gitThen, go to the parent project to perform MVN clean install, download the package that the parent project depends on, and ensure that the parent project is compiled. There will be no problems in this step. In the compiled output information, we will see the version number of the parent project, as shown in the figure:

![image.png](https://upload-images.jianshu.io/upload_images/24028241-abe08f15dimage.png

pom. The version tag of the parent dependency of the XML file, as shown below. Next, modify the POM of the mybatis project Where the parent dependency is identified in the XML file:

|

<parent>

<groupId>org.mybatis</groupId>

<artifactId>mybatis-parent</artifactId>

<version>28-SNAPSHOT</version>

<relativePath>../parent/pom.xml</relativePath>

</parent>

|

Tell us that some plug-ins do not specify the corresponding version number. For the sake of project stability, we need to specify the version number of the plug-ins used, and give the appropriate version number, as shown in the red box in the figure. We just need POM in mybatisg project Add < version > where the corresponding plug-in is found in the XML fileNum represents the specific version number. At this point, we can successfully build the mybatis project by executing the MVN clean install command.

Mybatis finishing

image.png

Firstly, mybatis is an excellent persistence layer ORM framework, which encapsulates the process of JDBC operating the database, so that developers only need to pay attention to SQL itself. No effort is needed to deal with some repetitive and cumbersome steps. Finally, the final executed SQL statement is generated by mapping the Java object with the SQL in the statement. Finally, the MySQL framework executes SQL and maps the results into Java objects and returns them.

ORM: object relational mapping. In short, it is to establish a mapping relationship between tables in the database and objects in Java, which allows us to operate objects to indirectly operate the database.

Semi ORM: when querying Association objects or association collection objects, you need to write SQL manually.

Mybatis programming steps

  • Create sqlsessionfactory
  • Create sqlsession through sqlsessionfactory
  • Execute database operation through sqlsession
  • Call session Commit() commit transaction
  • Call session Close close the session

How mybatis works

  • Read the mybatis configuration file. (obtain the operating environment and other information of mybatis)
  • Load mapping file. (SQL mapping file, in which SQL statements for operating the database are configured)
  • Construct a session factory: build a session factory sqlsessionfactory through mybatis environment and other configuration information
  • Create session object: a session factory creates a sqlsession object, which includes all methods for executing SQL statements
  • Executor executor: dynamically generates the SQL statements to be executed according to the parameters passed by sqlsession, and is responsible for maintaining the query cache
  • Mappedstatement object: used to store the ID, parameters and other information of the SQL statement to be mapped
  • Input parameter mapping: parameter types can be set types such as map and list, or basic data types and POJO types
  • Output result mapping: similar to input.

Advantages of mybatis

  • Programming based on SQL statement is quite flexible. SQL is written in XML to decouple SQL from program code for unified management. Provides XML tags, supports writing dynamic SQL statements, and can be reused
  • It eliminates a large number of redundant codes in JDBC and does not require manual switch connection;
  • Good compatibility with various databases
  • Good integration with spring;
  • Provide mapping labels to support the mapping between objects and ORM fields in the database; Provide object relationship mapping labels to support object relationship component maintenance.

Disadvantages of mybatis framework

  • The workload of writing SQL statements is large, especially when there are many fields and associated tables, which has certain requirements for developers to write SQL statements.
  • SQL statements depend on the database, resulting in poor database portability, and the database cannot be replaced at will.

What are the differences between mybatis and Hibernate

  • Mybatis is a semi ORM framework. You need to write your own SQL statements with high flexibility, but you need to customize multiple sets of SQL mapping files, which is a heavy workload
  • Hibernate has good database independence, saves code and improves efficiency

The difference between {} and ${}

{}: it is a precompiled process, which will replace #{} in SQL with?, Call the set method of preoaredstatement to assign values

Replace {} with the value of the variable.
Using #{} can effectively prevent SQL injection and improve the security of the system.

How to handle when the attribute name of the entity class is inconsistent with the field name in the table

  • By defining the alias of the field name in the SQL statement of the query, the alias of the field name is consistent with the attribute name of the entity class.

  • Map the one-to-one correspondence between field names and entity class attribute names through the < resultmap > class

How to write fuzzy query like

  • Adding SQL wildcards to Java code
  • Splicing wildcards in SQL statements (but it will cause SQL injection problems)

Usually, an XML Mapping file will write a Dao interface corresponding to it. What is the working principle of Dao interface.

Dao interface is mapper interface.

  • The fully qualified name of the interface is the value of the namespace of the mapping file
  • The method name of the interface is the ID value of mapper’s statement in the mapping file
  • The parameters in the interface method are the parameters passed to SQL

Mapper interface has no implementation class. When calling interface methods, the fully qualified name of the interface + method name concatenation string is used as the key value to uniquely locate a mapperstatement. In mybatis, each < Select >, < Insert >, < update >, < delete > tag will be resolved into a mapperstatement object

Can the method of Dao interface be overloaded when the parameters are different

The methods in mapper interface cannot be overloaded because the saving and searching strategy of fully qualified name + method name is used. So it can’t be overloaded.

The working principle of mapper interface is JDK dynamic proxy. Mybatis runtime will use JDK dynamic proxy to generate proxy object proxy for mapper interface. The proxy object will intercept interface methods, execute the SQL represented by mapperstatement, and then return the SQL execution results.

How does mybatis perform paging and what is the principle of the paging plug-in

Mybatis uses the rowbounds object for paging, which is a memory page for the resultset result set, not a physical page. You can directly write parameters with physical paging in SQL to complete the physical paging function, or use the paging plug-in to complete the physical paging

The basic principle of paging plug-in is to use the plug-in interface provided by mybatis to implement a custom plug-in, intercept the SQL to be executed in the plug-in interception method, then rewrite the SQL, and add the corresponding physical paging statements and physical paging parameters according to dialect.

How does mybati encapsulate an SQL actuator as an object and return it? What are the mapping forms

  • Use the < resultmap > tag to define the mapping relationship between database column names and object attribute names one by one.
  • Use the alias function of SQL column to write the column alias as the object attribute name

After having the mapping relationship between column name and attribute name, mybatis creates objects through reflection, and assigns and returns the attributes reflected to the object one by one. Those attributes that cannot find the mapping relationship cannot be assigned

How to batch insert

How to get automatically generated primary key values

  • Using JDBC built-in methods
  • Query after insertion
  • Query to get primary key before inserting

How to pass multiple parameters in mapper

  • Dao layer function
  • Use @ param annotation
  • Multiple parameters are encapsulated into a map

Mybatis dynamic SQL

Mybatis dynamic SQL can write dynamic SQL in the form of tags in the XML Mapping file. The execution principle is to complete logical judgment and dynamically splice SQL functions according to the value of the expression

Mybatis provides nine SQL tags

  • trim
  • where
  • set
  • foreach
  • if
  • choose
  • when
  • otherwise
  • bind

XML file label

  • select
  • insert
  • update
  • delete
  • resultMap
  • parameterMap
  • sql
  • include
  • selectKey

In the XML Mapping file of mybatis, can different XML file IDS be repeated

If the namespace is configured, the ID can be repeated. If the namespace is not configured, the ID cannot be repeated

There are several ways for mybatis to implement one-to-one

  • Joint query

    • Several tables are queried jointly and only once by configuring the collection node
  • nested queries

    • First query a table, and then query data in another table according to the found ID, also by configuring collection, but the query of another table is configured through the select node

Does mybatis support deferred loading and how

Mybatis only supports deferred loading of Association Association objects and Collection Association collection objects. It can be configured by configuring lazyloading enabled.

principle

Use cglib to create the proxy object of the target object. When calling the target method, enter the interceptor method, such as calling a.getb() GetName (), the interceptor invoke () method finds that a.getB () is null value, then it sends the SQL of the saved B query object separately, B queries up, and then a.setB (b) is invoked, so the object B property of a has value, and then completes the B (). Call to getname() method. This is the basic principle of delayed loading.

Mybatis cache

  • L1 cache: the HashMap local cache based on the perpetualcache. Its storage scope is session. When the session is flushed or closed, all caches in the session will be cleared. The L1 cache is opened by default.
  • The second level cache has the same mechanism as the first level cache. By default, it is stored in perpetual cache and HashMap. The difference is that its storage scope is mapper (namespace) and the storage source can be customized, such as ehcache. The L2 cache is not enabled by default. To enable L2 cache, the serializable serialization interface (which can be used to save the state of the object) needs to be implemented by using L2 cache attribute class, and < cache / > can be configured in its mapping file;

Cache update

  • Add, delete and modify
  • Call the purge method
  • Set purge properties

Interface binding and implementation of mybatis

Interface binding: define any interface in mybatis, and then bind the methods in the interface with SQL statements. We can call the interface methods directly when using

Implementation mode

  • Binding through annotations
  • To bind by writing SQL statements in XML, you need to specify that the namespace in XML must be the full pathname of the interface.

What are the requirements for calling the mapper interface of mybatis

  • Mapper interface method name and mapper The ID of SQL in XML is the same
  • Mapper interface method input parameter type and mapper The parametertype of each SQL defined in XML is the same
  • Mapper interface method output parameter type and mapper The resulttype of each SQL defined in XML is the same
  • Mapper. The namespace in the XML file is the classpath of the mapper interface

Mybatis plug-in operation principle, and how to write a plug-in.

Mybatis can only write plug-ins for parameterhandler, resultsethandler, statementhandler and executor. Mybatis uses the dynamic agent of JDK to generate proxy objects for the interfaces to be intercepted to realize the function of interface method interception. Whenever the methods of these four interface objects are executed, it will enter the interception method, Specifically, the invoke () method of invocationhandler, of course, will only intercept those methods you specify to be intercepted.

Write a plug-in: implement the interceptor interface of mybatis and copy the intercept () method, then write comments for the plug-in and specify which methods of which interface to intercept. Remember, don’t forget to configure the plug-in you write in the configuration file.

precompile

definition

SQL precompiling means that the database driver compiles SQL statements before sending SQL statements and parameters to the DBMS, so that the DBMS does not need to reproduce the compilation when executing SQL

Precompiling

You can optimize the execution of SQL. Most precompiled SQL can be run directly, and precompiled statement objects can be reused. It also prevents SQL injection

What are the executors of mybatis, and what are the differences between them

There are three basic executors: simpleexecution, reuseexecution, and batchexecutor

SimpleExecutor

Every time you execute update or select, you open a statement object and close it immediately

ReuseRxecutor

Execute update or select to find the statement object with SQL as the key. To reuse the statement object

BatchExecutor

Execute update, add all SQL to the batch and wait for the same execution. It has more than one statement object.

What happens when the attribute name in the entity class is different from the field name in the table

  • By defining the alias of the field name in the SQL statement of the query, the field name is consistent with the attribute name of the entity class
  • Map the one-to-one correspondence between field names and entity class attribute names through resultmap

Difference between resulttype and resultmap

First of all, when mybatis performs select mapping for query, the return type can be either resulttype or resultmap. Resulttype directly represents the return type, and resultmap is a reference to external resultmap. These two cannot exist at the same time

When mybatis performs query mapping, in fact, each attribute queried is placed in a corresponding map. The key is the attribute name and the value is the corresponding value.

3. How does mybatis page? What is the principle of paging plug-in?

Mybatis uses the rowbounds object for paging, which is a memory paging for the resultset result set, not a physical paging. You can also write SQL directly to implement paging or use paging plug-ins.
Principle of paging plug-in: implement the interface provided by mybatis to implement the custom plug-in, then intercept the SQL to be executed in the plug-in interception method, and finally rewrite the SQL.

4. When the parameters of the method in Dao interface are different, can the method be overloaded?

The method in mapper interface cannot be overloaded because it uses the saving and searching strategy of fully qualified name + method name. The working principle of mapper interface is JDK dynamic proxy. Mybatis runtime will use JDK dynamic proxy to generate proxy object proxy for mapper interface. The proxy object will intercept interface methods, execute the SQL represented by mapperstatement, and then return the SQL execution results.

5. How does mybatis encapsulate SQL execution results as target objects and return them? What are the mapping forms?

Use the resultmap tag to define the mapping relationship between database column names and object attribute names.
Use the alias function of SQL column to write the alias of the column as the object attribute name.
With the mapping relationship between column names and attribute names, mybatis creates objects through reflection, and uses the attributes reflected to the object to assign values one by one and return them.

6. What are the advantages and disadvantages of mybaits?

advantage:
Programming based on SQL statement is quite flexible and will not have any impact on the existing design of application program or database. SQL is written in XML to decouple SQL and program code for unified management; Provides XML tags, supports writing dynamic SQL statements, and can be reused.
Compared with JDBC, it reduces the amount of code by more than 50%, eliminates a large number of redundant codes in JDBC, and does not require manual switch connection;
It is well compatible with various databases and can be well integrated with spring;
Provide mapping labels to support the mapping between objects and ORM fields in the database; Provide object relationship mapping labels to support object relationship component maintenance.
Disadvantages:
The workload of writing SQL statements is large, especially when there are many fields and associated tables, which has certain requirements for developers to write SQL statements.
QL statements depend on the database, resulting in poor database portability, and the database cannot be replaced at will.

7. Talk about your understanding of mybatis?

Mybatis is a persistence layer framework that can customize SQL, stored procedures and advanced mapping. It encapsulates JDBC internally. During development, you only need to pay attention to the SQL statement itself. Programmers directly write the original SQL, which can strictly control the SQL execution performance and has high flexibility.

8. In the XML Mapping file of mybatis, can the IDs of different XML mapping files be repeated?

For different XML mapping files, if namespace is configured, the ID can be repeated. If the namespace is not configured, the ID cannot be repeated. The reason is that namespace + ID is used as the key of map < string, mapperstatement >. If there is no namespace, the duplicate ID will cause the data to overwrite each other. With a namespace, IDS can be repeated. If the namespace is different, the namespace + ID will naturally be different.

9. What is the difference between #{} and ${}?

Is a precompiled process, KAtex parse error: expected ‘EOF’, got ‘#’ at position 19:… String replacement. Mybatis is dealing with# ̲ When #{} in SQL is replaced with…, that is, $is replaced with the value of the variable.

Using # can effectively prevent SQL injection and improve system security.

10. What happens when the attribute name in the entity class is different from the field name in the table?

By defining the alias of the field name in the SQL statement of the query.
The one-to-one correspondence between field names and entity class attribute names is mapped through the resultmap tag.

11. In the mybatis mapping file, if tag a refers to the content of tag B through include, can tag B be defined after tag a, or must it be defined before tag a?

Mybatis parses XML Mapping Files in order, but the referenced B tag definition can be correctly recognized by mybatis anywhere. The principle is that mybatis parses tag a and finds that tag a refers to tag B, but tag B has not been parsed. At this time, tag a will be marked as unresolved, and then continue to parse the remaining tags, including tag B. After all tags are parsed, mybatis will re parse those tags marked as unresolved. At this time, when parsing tag a, tag B already exists, and tag a can be parsed normally.

12. One to one and one to many association queries in mybats?

Define a resultmap and use the association attribute to realize one-to-one association query; Use the collection attribute to implement a one to many association query.

13. How many ways can mybatis implement one-to-one? How did you do it?

Joint query is a joint query of several tables. It can be completed only once by configuring the association node in resultmap and configuring one-to-one classes;
Nested queries first query a table and then query data in another table according to the foreign key ID of the result in this table. It is also configured through association, but the query of another table is configured through the select attribute.

14. What are the benefits of mybatis?

The SQL statement is independent from the Java source program and written in a separate XML file, which brings great convenience to the maintenance of the program.
It can automatically convert the result set into Java Bean objects, which greatly simplifies the repeated work of Java database programming.
Programmers can flexibly control SQL statements combined with the characteristics of the database, and can complete complex queries.

15. Talk about the level 1 and level 2 cache of mybatis (talk about the cache of mybatis)?

The cache of mybatis is divided into level-1 cache and level-2 cache. Level-1 cache is placed in session, which is available by default. The L2 cache is placed in its namespace and is not opened by default. To use the L2 cache attribute class, you need to implement the serializable serialization interface. You can configure the cache tag in its mapping file

16. How many ways can mapper be written?

The interface implementation class inherits sqlsessiondaosupport, and the mapper interface needs to be written. The mapper interface implementation class and mapper XML file.
In sqlmapconfig Configure mapper.xml Location of XML
Define mapper interface
This. Can be used in the sqlsessiondaosupportmapper method to implement class integration Getsqlsession() performs data addition, deletion, modification and query.
Spring configuration
Use org mybatis. spring. mapper. Mapperfactorybean, in sqlmapconfig Configure mapper.xml Define mapper interface according to the location of XML:
mapper. The namespace in XML is the address of the mapper interface
Method name in mapper interface and mapper The ID of the statement defined in XML is consistent
Defined in spring
Third: use mapper scanner:
mapper. XML file writing:
Define the mapper interface. Pay attention to mapper The file name of XML is consistent with the interface name of mapper and placed in the same directory
To configure the mapper scanner:
After using the scanner, get the mapper implementation object from the spring container.

17. How does mybatis pass multiple parameters in mapper?

The parameters are passed directly in the method, and the XML file is obtained with # symbols
Use the @ param annotation or encapsulate multiple parameters into a map

18. Does mybatis support delayed loading? If yes, what is its implementation principle?

Mybatis only supports delayed loading of association objects and Collection Association collection objects. Association refers to one-to-one and Collection refers to one to many queries. In the mybatis configuration file, you can configure whether to enable deferred loading through lazyloading enabled = true / false.
The principle is: use cglib to create the proxy object of the target object. When calling the target method, it will enter the interceptor method, such as calling a.getb() GetName (), the interceptor invoke () method finds that a.getB () is null value, then it sends the SQL of the saved B query object separately, B queries up, and then calls a.setB (), so the object B property of a has value, and then completes the a.getB (). Call to getname() method.

19. In addition to the common select, insert, updae and delete tags in the XML Mapping file, what other tags are there?

Resultmap, parametermap, SQL, include and selectkey are added with 9 labels of dynamic SQL. SQL is the SQL fragment label. SQL fragments are introduced through the include label. Selectkey label is the primary key that does not support self increment to generate policy labels.

20. Usually, an XML Mapping file will write a Dao interface corresponding to it. What is the working principle of this Dao interface and whether it can be overloaded?

It cannot be overloaded, because when searching XML corresponding SQL through Dao, the saving and searching strategy of fully qualified name + method name is used. The working principle of Dao interface is JDK dynamic proxy principle. Proxy will be generated for Dao at runtime, and the proxy object will intercept interface methods to execute the corresponding SQL returned data.

21. What is mybatis interface binding? What are the implementation methods?

Interface binding is to define any interface in mybatis, and then bind the methods in the interface with SQL statements.
Interface binding can be implemented in two ways:
Through annotation binding, add @ select, @ update and other annotations on the interface methods, which contain SQL statements for binding;
Bind by writing SQL in XML. To specify that the namespace in the XML Mapping file must be the full pathname of the interface.
When the SQL statement is relatively simple, use annotation binding; When the SQL statement is complex, it is bound with XML. Generally, it is more bound with XML.

22. How does mybatis get automatically generated (Master) key values?

In the insert tag, use the usegeneratedkeys and keyproperty properties to obtain the automatically generated primary key value.

23. What are the requirements when calling the mapper interface of mybatis?

Mapper interface method name and mapper The ID of each SQL defined in XML is the same;
Mapper interface method input parameter type and mapper The parametertype of each SQL defined in XML is the same;
Mapper interface method output parameter type and mapper The resulttype of each SQL defined in XML is the same;
Mapper. The namespace in the XML file is the classpath of the mapper interface.

24. What is the use of mybatis dynamic SQL? How does it work? What are the dynamic SQL?

Mybatis dynamic SQL can write dynamic SQL in the form of tags in XML mapping files.
The execution principle is to complete logical judgment and dynamically splice SQL according to the value of the expression.
Mybatis provides nine dynamic SQL Tags: trim, where, set, foreach, if, choose, when, otherwise, and bind.

25. Can mybatis map enum classes?

Mybatis can map not only enumeration classes, but also any object to a column of the table. The mapping method is to customize a typehandler and implement the setparameter () and getResult () interface methods of typehandler. Typehandler has two functions: one is to complete the conversion from javatype to jdbctype, and the other is to complete the conversion from jdbctype to javatype, which is embodied in setparameter() and getresult() methods, representing setting SQL question mark placeholder parameters and obtaining column query results respectively.

26. Can mybatis perform one-to-one and one to many association queries? What are the implementation methods and the differences between them?

Mybatis can execute not only one-to-one and one to many association queries, but also many to one and many to many association queries. Many to one query is actually a one-to-one query. You only need to modify selectone() to selectlist(); Many to many query is actually a one to many query. You only need to modify selectone() to selectlist().
There are two ways to query associated objects. One is to send a separate SQL to query the associated object, assign it to the main object, and then return to the main object. The other is to use nested query. Nested query means to use join query. One column is the attribute value of object a and the other column is the attribute value of associated object B. the advantage is that only one SQL query can be issued to find the main object and its associated objects.

27. How to set the dynamic SQL in mybatis? What grammar do you use?

The dynamic SQL in mybatis is generally implemented through the if node and ognl syntax. However, if it is to be written completely, it must cooperate with the where and trim nodes. The where node is to insert the content of the containing node, otherwise it will not be inserted. The trim node is used to judge that if the dynamic statement starts with and or, the and or will be automatically removed.

28. Briefly describe the operation principle of mybatis plug-in and how to write a plug-in?

Operation principle: mybatis can only write plug-ins for parameterhandler, resultsethandler, statementhandler and executor. Mybatis generates proxy objects for the interfaces to be intercepted through dynamic agents to realize the function of interface method interception. Whenever the methods of these four interface objects are executed, it will enter the interception method. Specifically, calling the invoke () method of invocationhandler will only intercept the methods specified to be intercepted.
Write a plug-in: implement the interceptor interface of mybatis and copy the ntercept () method, then write comments for the plug-in, specify which methods of which interface to intercept, and finally configure the written plug-in in in the configuration file.

29. Why is mybatis a semi-automatic ORM mapping tool? What is the difference between it and automatic?

When mybatis queries Association objects or association collection objects, it needs to write SQL manually, so it is called semi-automatic ORM mapping tool.

30. What is the difference between resulttype and resultmap?

If the name of the class is the same as that of the database, you can directly set the resulttype parameter to POJO class. If it is different, you need to set resultmap to convert the result name and POJO name.