Mybatis principle analysis learning record, mybatis dynamic sql learning record

Posted by ScratchyAnt on Sat, 11 May 2019 16:37:13 +0200

The following personal learning notes are for reference only. You are welcome to make corrections.

MyBatis is a persistent layer framework that supports customized SQL, stored procedures, and advanced mapping. It mainly accomplishes two things:

  • Encapsulating JDBC operations

  • Using Reflection to Interchange Java Classes and SQL Statements

The main design goal of MyBatis is to make it easier for us to manage input and output data when executing SQL statements, so it is the core competitiveness of MyBatis to write SQL conveniently and obtain the execution results of SQL conveniently.

Configuration of MyBatis

spring integrates mybatis (druid data source)

  1 (1)Integrating ideas: MyBatis The core components involved in the framework are configured to Spring Container
  2 (2)Steps:
  3 -->1.Add to pom Depend on mybatis-spring,spring-tx,spring-jdbc
  4 -->2.Create entity classes( entity)
  5 -->3.Creating Data Access Interface( dao Layer interface)
  6 -->4.To configure SQL Mapping file( resource Lower mapper.xml Document)
  7 -->5.To configure mybatis Configuration file( resource Lower spring Lower mybatis-config.xml)
  8 -->6.Wherever annotations are used, services defined by scanning annotations need to be configured Bean:  <context:component-scan base-package="com.one.ssm.dao"/>
  9 and<context:annotation-config/>(Used to activate those already in place spring Registered in containers bean)
 10 
 11 
 12 <?xml version="1.0" encoding="UTF-8" ?>
 13 <!DOCTYPE configuration
 14         PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 15         "http://mybatis.org/dtd/mybatis-3-config.dtd">
 16 <configuration>
 17 <!--Configure global properties-->
 18 <settings>
 19     <!--Use jdbc Of getGeneratedKeys Getting database self-increasing primary key values-->
 20     <setting name="useGeneratedKeys" value="true"/>
 21     <!--Replace column names with column aliases by default true, eg: select name as title from table-->
 22     <setting name="useColumnLabel" value="true"/>
 23     <!--Open Hump Naming Conversion table(create_time)-->entity(createTime)
 24     <setting name="mapUnderscoreToCamelCase" value="true"/>
 25 </settings>
 26 </configuration>
 27 
 28 (3)Achieving Integration( spring-dao.xml Operation)
 29 -->1.To configure dataSource data source
 30 jdbc.properties Content:
 31     jdbc.driver=com.mysql.jdbc.Driver
 32     jdbc.url=jdbc:mysql://localhost:3306/smbms?useUnicode=true&characterEncoding=UTF-8
 33     jdbc.username=root
 34     jdbc.password=123456
 35 
 36 <!--properties File Configuration Data Source-->
 37 <context:property-placeholder location="classpath:spring/jdbc.properties"/>
 38     <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
 39         <!--Configure connection pool properties-->
 40         <property name="driverClassName" value="${jdbc.driver}"/>
 41         <property name="url" value="${jdbc.url}"/>
 42         <property name="username" value="${jdbc.username}"/>
 43         <property name="password" value="${jdbc.password}"/>
 44     </bean>
 45 
 46 -->2.To configure SqlSessionFactoryBean
 47    <!--SqlSession Contains all execution databases SQL Method of statement. Able to pass directly SqlSession Instance Execution Mapping SQL-->
 48     <!--Lack sqlSessionFactory: No bean named 'sqlSessionFactory' available    Complete reading configuration files-->
 49     <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
 50         <!--Injecting database connection pool--><!--Otherwise, it will happen. java.lang.IllegalArgumentException: Property 'dataSource' is required-->
 51         <property name="dataSource" ref="dataSource"/>
 52 
 53         <!--scanning entity Package, using aliases, set this later Mapper In the configuration file, the parameterType
 54         No need to write the full path name for the value of ____________-->
 55         <property name="typeAliasesPackage" value="com.one.ssm.entity"/>
 56 
 57         <!--scanning mapper Needed xml file-->
 58         <property name="mapperLocations" value="classpath:mapper/*.xml"/>
 59     </bean>
 60 
 61     <!-- Configuration scan Dao Interface package,Dynamic implementation Dao Interface,Injection into spring Container -->
 62     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
 63         <!-- injection sqlSessionFactory -->
 64         <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
 65         <!-- Give the need to scan Dao Interface package -->
 66         <property name="basePackage" value="com.one.ssm.dao"/>
 67     </bean>
 68 
 69 (4)Two ways of injecting mappers: using mapper injection, you can write without writing dao Implementation of Layer Interface
 70 -->1.To configure MapperFactoryBean Generate mappers and inject them into business components
 71  <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
 72         <!--mapperInterface Property specifies the mapper, which can only be an interface type-->
 73         <property name="mapperInterface" value="com.one.ssm.dao.UserDao"/>
 74         <!-- injection sqlSessionFactory -->
 75         <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
 76     </bean>
 77 
 78 -->2.To configure MapperScannerConfiger Generate mappers and inject them into business components: Priority MapperScannerConfiger,Implementation of batch generation mapper
 79 <!--mybatis-spring Provided MapperScannerConfigurer,
 80     Interfaces in specified packages can be scanned and registered directly as MapperFactoryBean,For simplification MapperFactoryBean Too many mappers lead to additional configuration items-->
 81  <!--mybatis-spring Provided MapperScannerConfigurer,Interfaces in specified packages can be scanned and registered directly as MapperFactoryBean-->
 82     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
 83         <!-- injection sqlSessionFactory -->
 84         <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
 85         <!-- Give the need to scan Dao Interface package -->
 86         <property name="basePackage" value="com.one.ssm.dao"/>
 87     </bean>
 88 
 89 (5)Adding declarative transactions( spring-service.xml Operation)
 90 -->Use xml Configuration Method Configuration Declarative Transactions
 91  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
 92     <property name="url" value="${jdbc.url}" />
 93     <property name="username" value="${jdbc.username}" />
 94     <property name="password" value="${jdbc.password}" />
 95     <property name="driverClassName" value="${jdbc.driver}" />
 96     </bean>
 97     <!--Configuration transaction manager (transaction is a unified commit or rollback operation for a series of database operations)-->
 98 <bean id="transactionManager"
 99           class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
100         <property name="dataSource" ref="dataSource"/>
101     </bean>
102 -->Adding declarative transactions with annotations
103  <!--How to add declarative transaction annotations-->
104     <tx:annotation-driven transaction-manager="transactionManager"/>
spring integrates mybatis

springboot integrates mybatis by adding configuration information to the yml file.

Major Members of MyBatis

Configuration: All configuration information for MyBatis is stored in the Configuration object, where most of the configuration in the configuration file is stored.

SqlSession: As the main top-level API of MyBatis, it represents the session when interacting with the database and completes the necessary database addition, deletion and modification functions.

Executor: MyBatis executor is the core of MyBatis scheduling, responsible for the generation of SQL statements and the maintenance of query cache.

Statement Handler: Encapsulated JDBC Statement operation, responsible for JDBC statement operation, such as setting parameters.

ParameterHandler: Responsible for converting parameters passed by users into data types corresponding to JDBC Statement.

ResultSetHandler: Responsible for converting the ResultSet result set object returned by JDBC into a collection of List type.

TypeHandler: Responsible for mapping and transformation between java data type and jdbc data type (also known as data table column type), for setting specific parameters to the state object, and for fetching specific columns from the result set returned by the state

MappedStatement: MappedStatement maintains the encapsulation of a < select | update | delete | Insert > node.

SqlSource: Responsible for dynamically generating SQL statements according to the parameterObject passed by users, encapsulating information into the BoundSql object and returning it.

BoundSql: Represents dynamically generated SQL statements and corresponding parameter information.

MyBatis hierarchy:

1. sqlSession - > excutor - > statementHander - > parameterHander - > typeHander - > (entering jdbc) statement (divided into: preparedStatement, simpleStatement, calleStatement) > (taking out the results) resultsetSet - > type hander - > resulthandler - > statementHandler - > sqlHandler - > excise

Initialization of MyBatis (the process of parsing configuration files and initializing Configuration)

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

// Construction of the factory of sqlSession
sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

The SqlSessionFactory builder object is created first, and then it is used to create the SqlSessionFactory. The constructor pattern is used here. The simplest understanding of the constructor pattern is not to create new objects manually, but to create objects by other classes.

// SqlSessionFactoryBuilder class
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
//The XMLConfigBuilder object parses the XML configuration file, which is actually the parsing operation of the configuration node.
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        return build(parser.parse()); // It's time to start 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.
        }
    }
}
 1 public Configuration parse() {
 2     if (parsed) {
 3         throw new BuilderException("Each XMLConfigBuilder can only be used once.");
 4     }
 5     parsed = true;
 6     parseConfiguration(parser.evalNode("/configuration"));
 7     return configuration;
 8 }
 9 
10 private void parseConfiguration(XNode root) {
11     try {
12         //issue #117 read properties first
13         propertiesElement(root.evalNode("properties"));
14         Properties settings = settingsAsProperties(root.evalNode("settings"));
15         loadCustomVfs(settings);
16         typeAliasesElement(root.evalNode("typeAliases"));
17         pluginElement(root.evalNode("plugins"));
18         objectFactoryElement(root.evalNode("objectFactory"));
19         objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
20         reflectorFactoryElement(root.evalNode("reflectorFactory"));
21         settingsElement(settings);
22         // read it after objectFactory and objectWrapperFactory issue #631
23 
24         /* Processing Environment Node Data */
25         environmentsElement(root.evalNode("environments"));
26         databaseIdProviderElement(root.evalNode("databaseIdProvider"));
27         typeHandlerElement(root.evalNode("typeHandlers"));
28         mapperElement(root.evalNode("mappers"));
29     } catch (Exception e) {
30         throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
31     }
32 }
XMLConfigBuilder class

Under the configuration node, node configurations such as properties/settings/.../mappers are resolved in turn. When parsing environment nodes, transaction managers are created according to the configuration of transactionManager, and DataSource objects are created according to the configuration of dataSource, which contains information about database login. When parsing a mappers node, all mapper files under that node are read and parsed, and the parsed results are stored in the configuration object.

 1 private void environmentsElement(XNode context) throws Exception {
 2     if (context != null) {
 3         if (environment == null) {
 4             environment = context.getStringAttribute("default");
 5         }
 6         for (XNode child : context.getChildren()) {
 7             String id = child.getStringAttribute("id");
 8             if (isSpecifiedEnvironment(id)) {
 9 
10                 /* Create a transaction manager */
11                 TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
12                 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
13                 DataSource dataSource = dsFactory.getDataSource();
14 
15                 /* Builder pattern design pattern */
16                 Environment.Builder environmentBuilder = new Environment.Builder(id)
17                         .transactionFactory(txFactory)
18                         .dataSource(dataSource);
19                 configuration.setEnvironment(environmentBuilder.build());
20             }
21         }
22     }
23 }
24 
25 // Resolve individual mapper file
26 private void mapperElement(XNode parent) throws Exception {
27     if (parent != null) {
28       for (XNode child : parent.getChildren()) {
29         if ("package".equals(child.getName())) {
30           String mapperPackage = child.getStringAttribute("name");
31           configuration.addMappers(mapperPackage);
32         } else {
33           String resource = child.getStringAttribute("resource");
34           String url = child.getStringAttribute("url");
35           String mapperClass = child.getStringAttribute("class");
36           if (resource != null && url == null && mapperClass == null) {
37             ErrorContext.instance().resource(resource);
38             InputStream inputStream = Resources.getResourceAsStream(resource);
39             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
40             mapperParser.parse(); // Start parsing mapper Papers. :)
41           } else if (resource == null && url != null && mapperClass == null) {
42             ErrorContext.instance().resource(url);
43             InputStream inputStream = Resources.getUrlAsStream(url);
44             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
45             mapperParser.parse();
46           } else if (resource == null && url == null && mapperClass != null) {
47             Class<?> mapperInterface = Resources.classForName(mapperClass);
48             configuration.addMapper(mapperInterface);
49           } else {
50             throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
51           }
52         }
53       }
54     }
55   }
XMLConfigBuilder class

After parsing the MyBatis configuration file, the configuration is initialized, and then the SqlSession is initialized by creating the configuration object.

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

MyBatis's SQL Query Process

By encapsulating JDBC for operation, and then using Java reflection technology to complete the conversion between JavaBean objects and database parameters, this mapping relationship is accomplished by a TypeHandler object. When the corresponding metadata of the data table is obtained, the database types of all columns of the table are saved.

sqlSession = sessionFactory.openSession();

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

Call the selectOne method for SQL query. The selectOne method finally calls the selectList. In the selectList, the MappedStatement object stored in the configuration is queried. The configuration of an SQL statement in the mapper file corresponds to a MappedStatement object, and then call the executor for query operation.

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 query operation, the executor will first query whether the cache has been hit or not, and the hit will be returned directly, otherwise it will be queried from the database.

 1 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
 2     /* Get the sql, boundSql of the correlation parameter */
 3     BoundSql boundSql = ms.getBoundSql(parameterObject);
 4     /* Create a cache key value */
 5     CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
 6     return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
 7 }
 8 
 9 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
10       throws SQLException {
11     /* Get a secondary cache instance */
12     Cache cache = ms.getCache();
13     if (cache != null) {
14         flushCacheIfRequired(ms);
15         if (ms.isUseCache() && resultHandler == null) {
16             ensureNoOutParams(ms, parameterObject, boundSql);
17             @SuppressWarnings("unchecked")
18             List<E> list = (List<E>) tcm.getObject(cache, key);
19             if (list == null) {
20                 list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
21                 tcm.putObject(cache, key, list); // issue #578 and #116
22             }
23             return list;
24         }
25     }
26     return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
27 }
28 
29 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
30     List<E> list;
31     /**
32      * Insert a placeholder in the localCache first, where
33      */
34     localCache.putObject(key, EXECUTION_PLACEHOLDER);
35     try {
36         list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
37     } finally {
38         localCache.removeObject(key);
39     }
40 
41     /* Writing data to the cache, that is, caching query results */
42     localCache.putObject(key, list);
43     if (ms.getStatementType() == StatementType.CALLABLE) {
44         localOutputParameterCache.putObject(key, parameter);
45     }
46     return list;
CachingExecutor class

The real doQuery operation is accomplished by the SimplyExecutor 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.

 1 public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
 2     Statement stmt = null;
 3     try {
 4         Configuration configuration = ms.getConfiguration();
 5         StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
 6 
 7         /* Subflow 1: Setting of SQL Query Parameters */
 8         stmt = prepareStatement(handler, ms.getStatementLog());
 9 
10         /* Subprocess 2: SQL query operations and result set encapsulation */
11         return handler.<E>query(stmt, resultHandler);
12     } finally {
13         closeStatement(stmt);
14     }
15 }
Sub process

Subprocess 1 Setting of SQL query parameters:

 1 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
 2     Statement stmt;
 3     /* Get Connection Connection Connection Connection */
 4     Connection connection = getConnection(statementLog);
 5 
 6     /* Prepare Statement */
 7     stmt = handler.prepare(connection, transaction.getTimeout());
 8 
 9     /* Setting parameter values in SQL queries */
10     handler.parameterize(stmt);
11     return stmt;
12 }
13 
14 // DefaultParameterHandler class
15 public void setParameters(PreparedStatement ps) {
16     /**
17      * Set the SQL parameter values, read the parameter values and types from the Parameter Mapping, and then set them to the SQL statement
18      */
19     ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
20     List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
21     if (parameterMappings != null) {
22         for (int i = 0; i < parameterMappings.size(); i++) {
23             ParameterMapping parameterMapping = parameterMappings.get(i);
24             if (parameterMapping.getMode() != ParameterMode.OUT) {
25                 Object value;
26                 String propertyName = parameterMapping.getProperty();
27                 if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
28                     value = boundSql.getAdditionalParameter(propertyName);
29                 } else if (parameterObject == null) {
30                     value = null;
31                 } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
32                     value = parameterObject;
33                 } else {
34                     MetaObject metaObject = configuration.newMetaObject(parameterObject);
35                     value = metaObject.getValue(propertyName);
36                 }
37                 TypeHandler typeHandler = parameterMapping.getTypeHandler();
38                 JdbcType jdbcType = parameterMapping.getJdbcType();
39                 if (value == null && jdbcType == null) {
40                     jdbcType = configuration.getJdbcTypeForNull();
41                 }
42                 try {
43                     typeHandler.setParameter(ps, i + 1, value, jdbcType);
44                 } catch (TypeException e) {
45                     throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
46                 } catch (SQLException e) {
47                     throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
48                 }
49             }
50         }
51     }
52 }
Subprocess 1 Setting of SQL query parameters:

Subprocess 2 Encapsulation of SQL Query Result Set:

 1 public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
 2     PreparedStatement ps = (PreparedStatement) statement;
 3     // Perform query operations
 4     ps.execute();
 5     // Execution result set encapsulation
 6     return resultSetHandler.<E> handleResultSets(ps);
 7 }
 8 
 9 // DefaultReseltSetHandler class
10 public List<Object> handleResultSets(Statement stmt) throws SQLException {
11     ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
12 
13     final List<Object> multipleResults = new ArrayList<Object>();
14 
15     int resultSetCount = 0;
16     /**
17      * Get the first ResultSet and the MetaData data of the database, including the column name of the data table, the type of the column, the class number, and so on.
18      * This information is stored in ResultSetWrapper
19      */
20     ResultSetWrapper rsw = getFirstResultSet(stmt);
21 
22     List<ResultMap> resultMaps = mappedStatement.getResultMaps();
23     int resultMapCount = resultMaps.size();
24     validateResultMapsCount(rsw, resultMapCount);
25     while (rsw != null && resultMapCount > resultSetCount) {
26       ResultMap resultMap = resultMaps.get(resultSetCount);
27       handleResultSet(rsw, resultMap, multipleResults, null);
28       rsw = getNextResultSet(stmt);
29       cleanUpAfterHandlingResultSet();
30       resultSetCount++;
31     }
32 
33     String[] resultSets = mappedStatement.getResultSets();
34     if (resultSets != null) {
35       while (rsw != null && resultSetCount < resultSets.length) {
36         ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
37         if (parentMapping != null) {
38           String nestedResultMapId = parentMapping.getNestedResultMapId();
39           ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
40           handleResultSet(rsw, resultMap, null, parentMapping);
41         }
42         rsw = getNextResultSet(stmt);
43         cleanUpAfterHandlingResultSet();
44         resultSetCount++;
45       }
46     }
47 
48     return collapseSingleResultList(multipleResults);
49   }
Encapsulation of SQL Query Result Set

ResultSetWrapper is the wrapper class of ResultSet. It calls the getFirstResultSet method to get the first ResultSet. At the same time, it obtains the MetaData data of the database, including the column name, column type, class serial number, etc. The information is stored in the ResultSetWrapper class. The handleResultSet method is then called to encapsulate the result set.

 1 private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
 2     try {
 3         if (parentMapping != null) {
 4             handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
 5         } else {
 6             if (resultHandler == null) {
 7                 DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
 8                 handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
 9                 multipleResults.add(defaultResultHandler.getResultList());
10             } else {
11                 handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
12             }
13         }
14     } finally {
15         // issue #228 (close resultsets)
16         closeResultSet(rsw.getResultSet());
17     }
18 }
DefaultResultSetHandler class

Call the handleRowValues method to set the result value

 1 public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
 2     if (resultMap.hasNestedResultMaps()) {
 3         ensureNoRowBounds();
 4         checkResultHandler();
 5         handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
 6     } else {
 7         // Encapsulated data
 8         handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
 9     }
10 }
11 
12 private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
13         throws SQLException {
14     DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
15     skipRows(rsw.getResultSet(), rowBounds);
16     while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
17         ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
18         Object rowValue = getRowValue(rsw, discriminatedResultMap);
19         storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
20     }
21 }
22 
23 private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
24     final ResultLoaderMap lazyLoader = new ResultLoaderMap();
25     // createResultObject For the newly created object, the class corresponding to the data table
26     Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
27     if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
28         final MetaObject metaObject = configuration.newMetaObject(rowValue);
29         boolean foundValues = this.useConstructorMappings;
30         if (shouldApplyAutomaticMappings(resultMap, false)) {
31             // Fill in the data here. metaObject It contains resultObject information
32             foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
33         }
34         foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
35         foundValues = lazyLoader.size() > 0 || foundValues;
36         rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;
37     }
38     return rowValue;
39 }
40 
41 private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
42     List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
43     boolean foundValues = false;
44     if (autoMapping.size() > 0) {
45         // Carry out here for Loop call, because user There are seven columns in the table, so there are seven calls.
46         for (UnMappedColumnAutoMapping mapping : autoMapping) {
47             // Here will esultSet Converting the query results to the corresponding actual types
48             final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
49             if (value != null) {
50                 foundValues = true;
51             }
52             if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
53                 // gcode issue #377, call setter on nulls (value is not 'found')
54                 metaObject.setValue(mapping.property, value);
55             }
56         }
57     }
58     return foundValues;
59 }
DefaultResultSetHandler class

Map. typeHandler. getResult retrieves the actual type of the query result value, such as the ID field in our user table as int type, which corresponds to the Integer type in Java, and then gets its int value by calling statement.getInt("id"), which is Integer type. The metaObject.setValue method sets the acquired Integer value to the corresponding field in the Java class.

The metaValue.setValue method finally calls the set method of the corresponding data domain in the Java class, thus completing the Java class encapsulation process of the SQL query result set.

 1 public void setValue(String name, Object value) {
 2     PropertyTokenizer prop = new PropertyTokenizer(name);
 3     if (prop.hasNext()) {
 4         MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
 5         if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
 6             if (value == null && prop.getChildren() != null) {
 7                 // don't instantiate child path if value is null
 8                 return;
 9             } else {
10                 metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
11             }
12         }
13         metaValue.setValue(prop.getChildren(), value);
14     } else {
15         objectWrapper.set(prop, value);
16     }
17 }
MetaObject class

MyBatis Cache

MyBatis provides a first-level cache and a second-level cache:

The first level cache is the SqlSession level cache. Each SqlSession object has a hash table for caching data. Caches between different SqlSession objects are not shared. 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 turns on level 1 caching by default.

Secondary cache is mapper level cache. Secondary cache is across SqlSession. Multiple SqlSession objects can share the same secondary cache. Different SqlSession objects execute the same SQL statement twice. The query results will be cached for the first time, and the results of the second query will be returned directly to the secondary cache. MyBatis does not open secondary cache by default. You can use the following configuration in the configuration file to open secondary cache:

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

When an SQL statement is updated (delete/add/update), the corresponding cache is emptied to ensure that all the data stored in the cache is up-to-date.

 

MyBatis Dynamic sql

(1)Multi-Conditional Query
<select id="getUserList" resultMap="userList">
        SELECT u.*,r.roleName FROM smbms_user u,smbms_role r 
        WHERE u.userName LIKE CONCAT('%','#{userName}','%')
        AND u.userRole=#{userRole} 
        AND u.userRole=r.id
    </select>

(2)if-where Usage
<select id="getUserList" resultType="User">
    SELECT * FROM smbms_user
    <where>
        <if test="userName!=null AND userName!=">
            AND userName LIKE CONCAT('%','#{userName}','%')
        </if>
        <if test="userRole!=null">
            AND userRole=#{userRole}
        </if>
    </where>
</select>
 <where>Will automatically remove the first and. 

(3)if-trim
<select id="getUserList" resultType="User">
         SELECT * FROM smbms_user
         <trim prefix="where" prefixOverrides="and|or">
             <if test="userName!=null and userName!=">
                 AND userName LIKE CONCAT('%','#{userName}','%')
             </if>
             <if test="userRole!=null">
                 AND userRole=#{userRole}
             </if>
         </trim>
    </select>
<trim prefix="where" prefixOverrides="and|or">Acts as automatic addition where Or right and|or Automatic Ignorance

(4)if-set Dynamic updates, assuming that those that are not involved or do not need updates do not need to be updated. set Tags can automatically remove commas (,)
<!--parameterType:Property name, if yes select Just write resultMap,Is the path location corresponding to other write-corresponding entities-->
    <update id="modifyXxx" parameterType="User">
        UPDATE smbms_user
        <set>
            <if test="userCode!=NULL">
                userCode=#{userCode},
            </if>
            <if test="userName!=null">
                userName=#{userName},
            </if>
            <if test="phone!=null">
                phone=#{phone},
            </if>
        </set>
        WHERE id=#{id}
    </update>

(5)if-set Medium trim
<update id="modify" parameterType="User">
        UPDATE smbms_user
        <trim prefix="set" prefixOverrides="," suffix="where id=#{id}">
        </trim>
            <if test="userCode!=null">
                userCode=#{userCode},
            </if>
            <if test="userName!=null">
                userName=#{userName},
            </if>
            <if test="phone!=null">
                phone=#{phone},
            </if>
    </update>
<trim suffix="where id=#{id} "> Suffix trim content

(6)foreach iteration collection Array type entry: For sql The statement contains in Statement, then need foreach Label implementation sql Iteration of Conditions
         eg: SELECT u.*   from smbms_user u WHERE userRole in(2,4)
<select id="getUserByRoleId_foreach_array" resultMap="userMapByRole">
        SELECT * FROM smbms_user WHERE userRole IN 
        <foreach collection="array" item="roleIds" open="(" separator="," close=")">
            #{roleIds}
        </foreach>
    </select>
    <resultMap id="userMapByRole" type="User">
        <id property="id" column="id"/>
        <result property="userCode" column="userCode"/>
        <result property="userName" column="userName"/>
    </resultMap>
-->Dao The layer interface method is: LIst<User> getUserByRoleId_foreach_array(Integer[] roleIds)
-->item :An alias for iteration in a set.
-->index :Specify a name to indicate the location of each iteration during the iteration
-->separator:What separators are used for each iteration? in Conditional statements are delimited by commas (,)
-->open: Represents what the statement begins with. in Statement begins with "("
-->close: Represents what symbol the statement ends with. in Statement ends with ")"
-->collection: If the input type is the parameter is LIst,be collection Attribute value is list;Is an array, otherwise array,If it is multi-parameter, it needs to be encapsulated into one Map Processing

(7)foreach iteration list Involvement of type
-->Dao Layer interface method is:LIst<User> getUserByRoleId_foreach_list(List<Integer> roleList);
<select id="getUserByRoleId_foreach_list" resultMap="userMapByRole">
        SELECT * FROM smbms_user WHERE userRole IN 
        <foreach collection="list" item="roleIds" open="(" separator="," close=")">
            #{roleIds}
        </foreach>
    </select>
    <resultMap id="userMapByRole" type="User">
        <id property="id" column="id"/>
        <result property="userCode" column="userCode"/>
        <result property="userName" column="userName"/>
    </resultMap>

(8)foreach iteration Map Involvement of type
//Interface method: public List < User > getUserByRoleId_foreach_map (Map < String, Object > conditionMap);
@Test
    public void getUserListByUserName() throws Exception {
        Map<String,Object> conditionMap=new HashMap<String, Object>();
        List<Integer> roleList=new ArrayList<Integer>();
        roleList.add(2);
        //gender is a finite condition
        conditionMap.put("gender",1);
        //roleIds corresponding collection
        conditionMap.put("roleIds",roleList);
        System.out.println("----------------------------------");
        System.out.println(userDao.getUserByRoleId_foreach_map(conditionMap));
        System.out.println("-------------------------------------");

 <select id="getUserByRoleId_foreach_map" resultMap="userMapByRole">
        SELECT * FROM smbms_user WHERE gender=#{gender} and userRole in
        <foreach collection="roleIds" item="m" open="(" separator="," close=")">
            #{m}
        </foreach>
    </select>

(9)choose(when-otherwise)
//Interface methods: public List < User > getUserList_select (@Param ("userName"), String userName, @Param ("userRole") Integer userRole,
                                     @Param("userCode")String userCode, @Param("creationDate")Date creationDate);
//Test class:
@Test
    public void getUserList_choose() throws Exception {
        List<User> userList =new ArrayList<>();
        String userName="Zhang Ming";
        Integer userRole=2;
        String userCode="";
        Date creationDate=new SimpleDateFormat("yyy-MM-dd").parse("2030-10-26");
        userList=userDao.getUserList_choose(userName,userRole,userCode,creationDate);
        System.out.println(userList);
mapper: 
<select id="getUserList_choose" resultMap="userMapByRole">
        SELECT * FROM smbms_user WHERE 1=1
        <choose>
            <when test="userName!=null and userName!=''">
                AND userName LIKE CONCAT('%',#{userName},'%')
            </when>
            <when test="userCode!=null and userCode!=''">
                AND userCode LIKE CONCAT('%',#{userCode},'%')
            </when>
            <when test="userRole!=null and userRole!=''">
                AND userRole=#{userRole}
            </when>
            <otherwise>
                AND YEAR(creationDate)=YEAR(#{creationDate})
            </otherwise>
        </choose>
    </select>
-->when: When a condition is satisfied, jump out of the loop.
-->otherwise: When all when When they are not satisfied, execute otherwise
-->choose: Amount to switch
-->where 1=1:You don't have to deal with redundancy. and

Topics: Java Mybatis SQL JDBC Spring