mybatis原理分析学习记录,mybatis动态sql学习记录

如下我的学习笔记,仅供参考,欢迎指正。java

MyBatis 是支持定制化 SQL、存储过程以及高级映射的持久层框架,其主要就完成2件事情:mysql

  • 封装JDBC操做spring

  • 利用反射打通Java类与SQL语句之间的相互转换sql

MyBatis的主要设计目的就是让咱们对执行SQL语句时对输入输出的数据管理更加方便,因此方便地写出SQL和方便地获取SQL的执行结果才是MyBatis的核心竞争力。数据库

MyBatis的配置

 spring整合mybatis(druid数据源)设计模式

 1 (1)整合思路:把MyBatis框架中使用所涉及的核心组件配置到Spring容器中  2 (2)步骤:  3 -->1.添加pom依赖,mybatis-spring,spring-tx,spring-jdbc  4 -->2.建立实体类(entity)  5 -->3.建立数据访问接口(dao层的接口)  6 -->4.配置SQL映射文件(resource下的mapper.xml文件)  7 -->5.配置mybatis的配置文件(resource下的spring下的mybatis-config.xml)  8 -->6.凡是使用了注解,都须要配置扫描注解定义的业务Bean: <context:component-scan base-package="com.one.ssm.dao"/>
 9 和<context:annotation-config/>(用于激活那些已经在spring容器里注册过的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 <!--配置全局属性-->
 18 <settings>
 19     <!--使用jdbc的getGeneratedKeys获取数据库自增主键值-->
 20     <setting name="useGeneratedKeys" value="true"/>
 21     <!--使用列别名替换列名,默认true, eg:select name as title from table-->
 22     <setting name="useColumnLabel" value="true"/>
 23     <!--开启驼峰命名转换 table(create_time)-->entity(createTime)  24     <setting name="mapUnderscoreToCamelCase" value="true"/>
 25 </settings>
 26 </configuration>
 27 
 28 (3)实现整合(spring-dao.xml操做)  29 -->1.配置dataSource数据源  30 jdbc.properties内容:  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文件配置数据源-->
 37 <context:property-placeholder location="classpath:spring/jdbc.properties"/>
 38     <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
 39         <!--配置链接池属性-->
 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.配置SqlSessionFactoryBean  47    <!--SqlSession 包含了全部执行数据库SQL语句的方法。可以直接地经过SqlSession实例执行映射SQL-->
 48     <!--缺乏sqlSessionFactory:No bean named 'sqlSessionFactory' available    完成对配置文件的读取-->
 49     <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
 50         <!--注入数据库链接池--><!--不然会出现java.lang.IllegalArgumentException: Property 'dataSource' is required-->
 51         <property name="dataSource" ref="dataSource"/>
 52 
 53         <!--扫描entity包,使用别名,设置这个之后再Mapper配置文件中在parameterType  54         的值就不用写成全路径名了-->
 55         <property name="typeAliasesPackage" value="com.one.ssm.entity"/>
 56 
 57         <!--扫描mapper须要的xml文件-->
 58         <property name="mapperLocations" value="classpath:mapper/*.xml"/>
 59     </bean>
 60 
 61     <!-- 配置扫描Dao接口包,动态实现Dao接口,注入到spring容器中 -->
 62     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
 63         <!-- 注入sqlSessionFactory -->
 64         <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
 65         <!-- 给出须要扫描Dao接口包 -->
 66         <property name="basePackage" value="com.one.ssm.dao"/>
 67     </bean>
 68 
 69 (4)注入映射器的两种方式:使用了映射器注入就能够不用写dao层的接口的实现方法  70 -->1.配置MapperFactoryBean生成映射器并注入到业务组件  71  <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
 72         <!--mapperInterface属性指定映射器,只能是某一个接口类型-->
 73         <property name="mapperInterface" value="com.one.ssm.dao.UserDao"/>
 74         <!-- 注入sqlSessionFactory -->
 75         <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
 76     </bean>
 77 
 78 -->2.配置MapperScannerConfiger生成映射器并注入到业务组件:优先使用MapperScannerConfiger,能够批量生成映射器的实现  79 <!--mybatis-spring提供了MapperScannerConfigurer,  80     能够扫描指定包中的接口并将它们直接注册为MapperFactoryBean,为了简化MapperFactoryBean映射器太多而致使多出的配置项-->
 81  <!--mybatis-spring提供了MapperScannerConfigurer,能够扫描指定包中的接口并将它们直接注册为MapperFactoryBean-->
 82     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
 83         <!-- 注入sqlSessionFactory -->
 84         <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
 85         <!-- 给出须要扫描Dao接口包 -->
 86         <property name="basePackage" value="com.one.ssm.dao"/>
 87     </bean>
 88 
 89 (5)添加声明式事务(spring-service.xml操做)  90 -->使用xml配置方法配置声明式事务  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     <!--配置事务(事务就是对一系列的数据库操做进行统一的提交或回滚操做)管理器-->
 98 <bean id="transactionManager"
 99           class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
100         <property name="dataSource" ref="dataSource"/>
101     </bean>
102 -->使用注解添加声明式事务 103  <!--声明式事务注解的添加方式-->
104     <tx:annotation-driven transaction-manager="transactionManager"/>
spring整合mybatis

 springboot整合mybatis,须要在yml文件中添加相应的配置信息。数组

MyBatis的主要成员

Configuration:MyBatis全部的配置信息都保存在Configuration对象之中,配置文件中的大部分配置都会存储到该类中。缓存

SqlSession:做为MyBatis工做的主要顶层API,表示和数据库交互时的会话,完成必要数据库增删改查功能。springboot

Executor:MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护。session

StatementHandler:封装了JDBC Statement操做,负责对JDBC statement 的操做,如设置参数等。

ParameterHandler:负责对用户传递的参数转换成JDBC Statement所对应的数据类型。

ResultSetHandler:负责将JDBC返回的ResultSet结果集对象转换成List类型的集合。

TypeHandler:负责java数据类型和jdbc数据类型(也能够说是数据表列类型)之间的映射和转换,负责对statement对象设定特定的参数,对statement的返回结果result结果集取出特定的列

MappedStatement:MappedStatement维护一条<select|update|delete|insert>节点的封装。

SqlSource:负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回。

BoundSql:表示动态生成的SQL语句以及相应的参数信息。

MyBatis的层次结构:

一、sqlSession -->excutor--> statementHander-->parameterHander--> typeHander-->(进入jdbc)statement(分为:preparedStatement、simpleStatement、callableStatement)-->(取出结果)resultSet--> typeHander-->resultSetHandler-->statementHandler--->excutor-->sqlSession

MyBatis的初始化(解析配置文件和初始化Configuration的过程)

String resource = "mybatis.xml";
// 加载mybatis的配置文件(它也加载关联的映射文件)
InputStream inputStream = null;
try {
    inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
    e.printStackTrace();
}

// 构建sqlSession的工厂
sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

首先会建立SqlSessionFactory建造者对象,而后由它进行建立SqlSessionFactory。这里用到的是建造者模式,建造者模式最简单的理解就是不手动new对象,而是由其余类来进行对象的建立。

// SqlSessionFactoryBuilder类
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
//XMLConfigBuilder对象会进行XML配置文件的解析,实际为configuration节点的解析操做。
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        return build(parser.parse()); // 开始进行解析了 :)
    } 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         /* 处理environments节点数据 */
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类

 在configuration节点下会依次解析properties/settings/.../mappers等节点配置。在解析environments节点时,会根据transactionManager的配置来建立事务管理器,根据dataSource的配置来建立DataSource对象,这里面包含了数据库登陆的相关信息。在解析mappers节点时,会读取该节点下全部的mapper文件,而后进行解析,并将解析后的结果存到configuration对象中。

 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                 /* 建立事务管理器 */
11                 TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); 12                 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); 13                 DataSource dataSource = dsFactory.getDataSource(); 14 
15                 /* 建造者模式 设计模式 */
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 // 解析单独的mapper文件
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(); // 开始解析mapper文件了 :)
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类

解析完MyBatis配置文件后,configuration就初始化完成了,而后根据configuration对象来建立SqlSession就初始化完成了

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

MyBatis的SQL查询流程

经过封装JDBC进行操做,而后使用Java反射技术完成JavaBean对象到数据库参数之间的相互转换,这种映射关系就是有TypeHandler对象来完成的,在获取数据表对应的元数据时,会保存该表全部列的数据库类型。

sqlSession = sessionFactory.openSession();

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

调用selectOne方法进行SQL查询,selectOne方法最后调用的是selectList,在selectList中,会查询configuration中存储的MappedStatement对象,mapper文件中一个sql语句的配置对应一个MappedStatement对象,而后调用执行器进行查询操做。

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

 执行器在query操做中,优先会查询缓存是否命中,命中则直接返回,不然从数据库中查询。

 1 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {  2     /* 获取关联参数的sql,boundSql */
 3     BoundSql boundSql = ms.getBoundSql(parameterObject);  4     /* 建立cache key值 */
 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     /* 获取二级缓存实例 */
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  * 先往localCache中插入一个占位对象,这个地方 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     /* 往缓存中写入数据,也就是缓存查询结果 */
42  localCache.putObject(key, list); 43     if (ms.getStatementType() == StatementType.CALLABLE) { 44  localOutputParameterCache.putObject(key, parameter); 45  } 46     return list;
CachingExecutor类

真正的doQuery操做是由SimplyExecutor代理来完成的,该方法中有2个子流程,一个是SQL参数的设置,另外一个是SQL查询操做和结果集的封装。

 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         /* 子流程1: SQL查询参数的设置 */
 8         stmt = prepareStatement(handler, ms.getStatementLog());  9 
10         /* 子流程2: SQL查询操做和结果集封装 */
11         return handler.<E>query(stmt, resultHandler); 12     } finally { 13  closeStatement(stmt); 14  } 15 }
子流程

子流程1 SQL查询参数的设置:

 1 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {  2  Statement stmt;  3     /* 获取Connection链接 */
 4     Connection connection = getConnection(statementLog);  5 
 6     /* 准备Statement */
 7     stmt = handler.prepare(connection, transaction.getTimeout());  8 
 9     /* 设置SQL查询中的参数值 */
10  handler.parameterize(stmt); 11     return stmt; 12 } 13 
14 // DefaultParameterHandler类
15 public void setParameters(PreparedStatement ps) { 16     /**
17  * 设置SQL参数值,从ParameterMapping中读取参数值和类型,而后设置到SQL语句中 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 }
子流程1 SQL查询参数的设置:

子流程2 SQL查询结果集的封装:

 1 public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {  2     PreparedStatement ps = (PreparedStatement) statement;  3     // 执行查询操做
 4  ps.execute();  5     // 执行结果集封装
 6     return resultSetHandler.<E> handleResultSets(ps);  7 }  8 
 9 // DefaultReseltSetHandler类
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  * 获取第一个ResultSet,同时获取数据库的MetaData数据,包括数据表列名、列的类型、类序号等。 18  * 这些信息都存储在了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   }
SQL查询结果集的封装

ResultSetWrapperResultSet的包装类,调用getFirstResultSet方法获取第一个ResultSet,同时获取数据库的MetaData数据,包括数据表列名、列的类型、类序号等,这些信息都存储在ResultSetWrapper类中了。而后调用handleResultSet方法来来进行结果集的封装。

 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类

调用handleRowValues方法进行结果值的设置

 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         // 封装数据
 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为新建立的对象,数据表对应的类
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             // 这里把数据填充进去,metaObject中包含了resultObject信息
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         // 这里进行for循环调用,由于user表中总共有7列,因此也就调用7次
46         for (UnMappedColumnAutoMapping mapping : autoMapping) { 47             // 这里将esultSet中查询结果转换为对应的实际类型
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类

mapping.typeHandler.getResult会获取查询结果值的实际类型,好比咱们user表中id字段为int类型,那么它就对应Java中的Integer类型,而后经过调用statement.getInt("id")来获取其int值,其类型为IntegermetaObject.setValue方法会把获取到的Integer值设置到Java类中的对应字段。

metaValue.setValue方法最后会调用到Java类中对应数据域的set方法,这样也就完成了SQL查询结果集的Java类封装过程。

 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类

MyBatis缓存

MyBatis提供了一级缓存和二级缓存:

一级缓存是SqlSession级别的缓存,每一个SqlSession对象都有一个哈希表用于缓存数据,不一样SqlSession对象之间缓存不共享。同一个SqlSession对象对象执行2遍相同的SQL查询,在第一次查询执行完毕后将结果缓存起来,这样第二遍查询就不用向数据库查询了,直接返回缓存结果便可。MyBatis默认是开启一级缓存的。

二级缓存是mapper级别的缓存,二级缓存是跨SqlSession的,多个SqlSession对象能够共享同一个二级缓存。不一样的SqlSession对象执行两次相同的SQL语句,第一次会将查询结果进行缓存,第二次查询直接返回二级缓存中的结果便可。MyBatis默认是不开启二级缓存的,能够在配置文件中使用以下配置来开启二级缓存:

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

当SQL语句进行更新操做(删除/添加/更新)时,会清空对应的缓存,保证缓存中存储的都是最新的数据。

 

MyBatis动态sql

(1)多条件查询
<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的用法
<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>会自动去掉第一个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">做用为自动添加where或者对and|or的自动忽略

(4)if-set 动态更新,假设没有涉及到的或者不须要更新的就能够不用更新,set标签能够自动剔除逗号(,)
<!--parameterType:属性名,若是是select就写resultMap,是其余的写对应实体对应的路径位置-->
    <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中的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}">在trim内容的后面加上后缀

(6)foreach迭代collection数组类型的入参:对于sql语句中含有in语句,则须要foreach标签来实现sql条件的迭代
         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层接口方法为:LIst<User> getUserByRoleId_foreach_array(Integer[] roleIds)
-->item :集合中进行迭代时的别名,
-->index :指定一个名称,表示在迭代过程当中每次迭代到的位置
-->separator:每次进行迭代用什么分隔符号,in条件语句用逗号(,)为分隔符
-->open:表示该语句以什么开始的,in语句以 “(”开始
-->close:表示该语句以什么符号结束 ,in语句以“)”结束
-->collection:若是是入参类型是参数是LIst,则collection属性值为list;是一个数组,则为array,若是为多参数,则须要封装成一个Map进行处理

(7)foreach迭代list类型的入参
-->Dao层接口方法为: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迭代Map类型的入参
接口方法: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是一个限定条件
        conditionMap.put("gender",1);
        //roleIds 对应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)
接口方法:public List<User> getUserList_choose(@Param("userName") String userName, @Param("userRole")Integer userRole,
                                     @Param("userCode")String userCode, @Param("creationDate")Date creationDate);
测试类:
@Test
    public void getUserList_choose() throws Exception {
        List<User> userList =new ArrayList<>();
        String userName="张明";
        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:当知足一个条件时跳出循环,
-->otherwise:当全部的when都不知足的时候,执行otherwise
-->choose:至关于switch
-->where 1=1:能够不须要处理多余的and
相关文章
相关标签/搜索