MyBatis源码解析

柔光的暖阳◎ 2023-07-04 14:12 86阅读 0赞

文章目录

  • MyBatis源码解析
    • 一、mybatis怎么处理参数以及执行方法?
    • 二、Mybatis工作原理
    • 三、代理对象如何执行增删改查
        1. MapperProxy的invoke
        1. MapperMethod的execute方法
        1. DefaultSqlSession的selectOne方法
        1. Executor的query系列(不是重要方法)
        1. Executor执行方法,默认是SIMPLE
        1. BaseExecutor的queryFromDatabase
        1. SimpleExecutor的doQuery方法
        1. Configuration的newStatementHandler
        1. PreparedStatementHandler的query
        1. handleResultSets处理参数
        1. Resulthandler的getPropertyMappingValue
    • 总结
    • 其他
        • 1、参数值的获取(#、$)
        • 2、 映射文件

MyBatis源码解析

一、mybatis怎么处理参数以及执行方法?

ParamNameResolver解析参数封装map的;

MapperProxy代理对象调用invoke方法:

  • MapperProxyinvoke方法

    1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    2. if (Object.class.equals(method.getDeclaringClass())) {
    3. try {
    4. return method.invoke(this, args);
    5. } catch (Throwable var5) {
    6. throw ExceptionUtil.unwrapThrowable(var5);
    7. }
    8. } else {
    9. MapperMethod mapperMethod = this.cachedMapperMethod(method);
    10. return mapperMethod.execute(this.sqlSession, args);
    11. }
    12. }

    调用mapperMethod.execute()

  • MapperMethodmapperMethod.execute()

    判断是什么类型(Select、Update…),解析参数之后,还是调用的SqlSession的原生方法(sqlSession.insert、sqlSession.update)。

    所以,如何处理参数,就在this.method.convertArgsToSqlCommandParam(args);这个方法又调用了paramNameResolver.getNamedParams(args)

    1. public Object execute(SqlSession sqlSession, Object[] args) {
    2. Object param;
    3. Object result;
    4. switch(this.command.getType()) {
    5. case INSERT:
    6. param = this.method.convertArgsToSqlCommandParam(args);
    7. result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
    8. break;
    9. case UPDATE:
    10. param = this.method.convertArgsToSqlCommandParam(args);
    11. result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
    12. break;
    13. case DELETE:
    14. param = this.method.convertArgsToSqlCommandParam(args);
    15. result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
    16. break;
    17. case SELECT:
    18. if (this.method.returnsVoid() && this.method.hasResultHandler()) {
    19. this.executeWithResultHandler(sqlSession, args);
    20. result = null;
    21. } else if (this.method.returnsMany()) {
    22. result = this.executeForMany(sqlSession, args);
    23. } else if (this.method.returnsMap()) {
    24. result = this.executeForMap(sqlSession, args);
    25. } else if (this.method.returnsCursor()) {
    26. result = this.executeForCursor(sqlSession, args);
    27. } else {
    28. param = this.method.convertArgsToSqlCommandParam(args);
    29. result = sqlSession.selectOne(this.command.getName(), param);
    30. }
    31. break;
  • ParamNameResolverconvertArgsToSqlCommandParam

    1. public Object convertArgsToSqlCommandParam(Object[] args) {
    2. return this.paramNameResolver.getNamedParams(args);
    3. }
  • ParamNameResolvergetNamedParams(args)

    确定流程:

    1. 获取每个标了param注解的参数的@Param的值:id,lastName; 赋值给name;

      1. 每次解析一个参数给map中保存信息:(key:参数索引,value:name的值)
        name的值:
        标注了param注解:注解的值
        没有标注:

        1. 全局配置:useActualParamName(jdk1.8):name=参数名
        2. name=map.size();相当于当前元素的索引
        3. {0=id, 1=lastName,2=2}

      public Object getNamedParams(Object[] args) {
      int paramCount = this.names.size();

      if (args != null && paramCount != 0) {

      1. //1、如果只有一个元素,并且没有Param注解;args[0]:单个参数直接返回
      2. if (!this.hasParamAnnotation && paramCount == 1) {
      3. return args[(Integer)this.names.firstKey()];
      4. //2、多个元素或者有Param标注
      5. } else {
      6. Map<String, Object> param = new ParamMap();
      7. int i = 0;
      8. //3、遍历names集合;{0=id, 1=lastName,2=2}
      9. for(Iterator i$ = this.names.entrySet().iterator(); i$.hasNext(); ++i) {
      10. Entry<Integer, String> entry = (Entry)i$.next();
      11. //names集合的value作为key; names集合的key又作为取值的参考args[0]:args【1,"Tom"】:
      12. //eg:{id=args[0]:1,lastName=args[1]:Tom,2=args[2]}
      13. param.put(entry.getValue(), args[(Integer)entry.getKey()]);
      14. //额外的将每一个参数也保存到map中,使用新的key:param1...paramN
      15. //效果:有Param注解可以#{指定的key},或者#{param1}
      16. String genericParamName = "param" + String.valueOf(i + 1);
      17. if (!this.names.containsValue(genericParamName)) {
      18. param.put(genericParamName, args[(Integer)entry.getKey()]);
      19. }
      20. }
      21. return param;
      22. }
      23. // 参数为null直接返回

      } else {

      1. return null;

      }
      }

总结:参数多时会封装map,为了不混乱,我们可以使用@Param来指定封装时使用的key;

{key}就可以取出map中的值;

二、Mybatis工作原理

Mybatis四大对象:

  1. Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  2. ParameterHandler (getParameterObject, setParameters)
  3. ResultSetHandler (handleResultSets, handleOutputParameters)
  4. StatementHandler (prepare, parameterize, batch, update, query)

混个眼熟,后面会遇到。

Mybatis框架分层:我们关注:数据处理层,其实就对应上面的四大对象!!!
watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNDU2MDY0_size_16_color_FFFFFF_t_70

  1. /** * 1、获取sqlSessionFactory对象: * 解析文件的每一个信息保存在Configuration中,返回包含Configuration的DefaultSqlSession; * 注意:【MappedStatement】:代表一个增删改查的详细信息 * * 2、获取sqlSession对象 * 返回一个DefaultSQlSession对象,包含Executor和Configuration; * 这一步会创建Executor对象; * * 3、获取接口的代理对象(MapperProxy) * getMapper,使用MapperProxyFactory创建一个MapperProxy的代理对象 * 代理对象里面包含了,DefaultSqlSession(Executor) * 4、执行增删改查方法 * * 总结: * 1、根据配置文件(全局,sql映射)初始化出Configuration对象 * 2、创建一个DefaultSqlSession对象, * 他里面包含Configuration以及 * Executor(根据全局配置文件中的defaultExecutorType创建出对应的Executor) * 3、DefaultSqlSession.getMapper():拿到Mapper接口对应的MapperProxy; * 4、MapperProxy里面有(DefaultSqlSession); * 5、执行增删改查方法: * 1)、调用DefaultSqlSession的增删改查(Executor); * 2)、会创建一个StatementHandler对象。 * (同时也会创建出ParameterHandler和ResultSetHandler) * 3)、调用StatementHandler预编译参数以及设置参数值; * 使用ParameterHandler来给sql设置参数 * 4)、调用StatementHandler的增删改查方法; * 5)、ResultSetHandler封装结果 * 注意: * 四大对象每个创建的时候都有一个interceptorChain.pluginAll(parameterHandler); * * @throws IOException */
  2. @Test
  3. public void test02() {
  4. SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
  5. SqlSession sqlSession = sqlSessionFactory.openSession();
  6. try {
  7. EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
  8. Employee employee = mapper.getEmpById(1);
  9. System.out.println(employee);
  10. System.out.println(mapper.getClass());
  11. } catch (Exception e) {
  12. e.printStackTrace();
  13. } finally {
  14. sqlSession.close();
  15. }
  16. }
  1. 获取sqlSessionFactory对象:

    解析文件的每一个信息保存在Configuration中,返回包含Configuration的DefaultSqlSession;

    注意:【MappedStatement】:代表一个增删改查的详细信息

在这里插入图片描述

  1. 获取sqlSession对象

返回一个DefaultSQlSession对象,包含Executor和Configuration;这一步会创建Executor对象

  1. this.openSessionFromConnection(this.configuration.getDefaultExecutorType(), connection);
  2. this.configuration.getDefaultExecutorType():
  3. 配置文件里可以配置Executor的类型(defaultExecutorType):SIMPLEREUSEBATCH。默认SIMPLE
  4. private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  5. Transaction tx = null;
  6. DefaultSqlSession var8;
  7. try {
  8. // 获取当前环境
  9. Environment environment = this.configuration.getEnvironment();
  10. // 创建事务
  11. TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
  12. tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
  13. // *************四大对象之一:Executor在这里创建**************
  14. // Executor就是进行增删改查的
  15. Executor executor = this.configuration.newExecutor(tx, execType);
  16. // 最终返回的SqlSession是DefaultSqlSession,包含Configuration、刚刚创建的executor
  17. var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
  18. } catch (Exception var12) {
  19. this.closeTransaction(tx);
  20. throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
  21. } finally {
  22. ErrorContext.instance().reset();
  23. }
  24. return var8;
  25. }
  26. //创建Executor,【Configuration.java】
  27. public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  28. executorType = executorType == null ? this.defaultExecutorType : executorType;
  29. executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  30. Object executor;
  31. // 根据全局配置中的配置的类型创建Executor(默认SIMPLE)
  32. if (ExecutorType.BATCH == executorType) {
  33. executor = new BatchExecutor(this, transaction);
  34. } else if (ExecutorType.REUSE == executorType) {
  35. executor = new ReuseExecutor(this, transaction);
  36. } else {
  37. executor = new SimpleExecutor(this, transaction);
  38. }
  39. // 如果配置了二级缓存,利用CachingExecutor进行包装(Executor执行之前,对缓存进行查询)
  40. if (this.cacheEnabled) {
  41. executor = new CachingExecutor((Executor)executor);
  42. }
  43. // 拿到所有的拦截器,执行plugin方法,这一步非常重要,与插件有关
  44. // 使用每一个拦截器重新包装Executor,再返回
  45. Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
  46. return executor;
  47. }
  48. // 最终返回的SqlSession是DefaultSqlSession,包含Configuration、刚刚创建的executor
  49. var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);

最终返回的SqlSession是DefaultSqlSession,包含Configuration、刚刚创建的executor

在这里插入图片描述

  1. 获取接口的代理对象(MapperProxy

    getMapper,使用MapperProxyFactory创建一个MapperProxy的代理对象,代理对象里面包含了,DefaultSqlSession(Executor)

在这里插入图片描述
Configuration里有一个很重要的属性:MapperRegistry ,用于获取接口的代理对象MapperProxy

  1. // MapperRegistry的getMapper方法:
  2. public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  3. // 根据<接口类型>获取MapperProxyFactory
  4. MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
  5. if (mapperProxyFactory == null) {
  6. throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  7. } else {
  8. try {
  9. // 调用MapperProxyFactory的newInstance创建代理对象
  10. return mapperProxyFactory.newInstance(sqlSession);
  11. } catch (Exception var5) {
  12. throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
  13. }
  14. }
  15. }
  16. // MapperProxyFactory的newInstance方法
  17. public T newInstance(SqlSession sqlSession) {
  18. // SqlSession、接口方法
  19. MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
  20. return this.newInstance(mapperProxy);
  21. }
  22. // MapperProxy是一个InvocationHandler类型的对象,可以用来创建动态代理
  23. public class MapperProxy<T> implements InvocationHandler, Serializable {
  24. private static final long serialVersionUID = -6424540398559729838L;
  25. private final SqlSession sqlSession;
  26. private final Class<T> mapperInterface;
  27. // 接口方法映射
  28. private final Map<Method, MapperMethod> methodCache;
  29. }
  30. // 用JDK的API创建代理对象,这个代理对象会一步步的返回,最终拿到的Mapper是一个代理对象
  31. protected T newInstance(MapperProxy<T> mapperProxy) {
  32. return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{ this.mapperInterface}, mapperProxy);
  33. }

最终拿到的mapper:包含sqlSession(MapperProxy有invoke方法,是一个InvocationHandler类型的对象)

在这里插入图片描述

  1. 代理对象执行增删改查

三、代理对象如何执行增删改查

在这里插入图片描述
在这里插入图片描述

1. MapperProxy的invoke

  1. // MapperProxy的invoke
  2. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  3. // 有些方法是Object的方法,例如toString、hashCode等等,这些方法直接执行就行了
  4. if (Object.class.equals(method.getDeclaringClass())) {
  5. try {
  6. return method.invoke(this, args);
  7. } catch (Throwable var5) {
  8. throw ExceptionUtil.unwrapThrowable(var5);
  9. }
  10. } else {
  11. MapperMethod mapperMethod = this.cachedMapperMethod(method);
  12. // invoke的时候调用MapperMethod的execute方法
  13. return mapperMethod.execute(this.sqlSession, args);
  14. }
  15. }

2. MapperMethod的execute方法

invoke的时候调用MapperMethod的execute方法,传入SqlSession以及需要的参数args

  1. // MapperMethod的execute方法
  2. public Object execute(SqlSession sqlSession, Object[] args) {
  3. Object param;
  4. Object result;
  5. switch(this.command.getType()) {
  6. // 判断当前执行的方法是哪种类型,执行方法之前都会解析参数(解析方法名)
  7. case INSERT:
  8. param = this.method.convertArgsToSqlCommandParam(args);
  9. result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
  10. break;
  11. case UPDATE:
  12. param = this.method.convertArgsToSqlCommandParam(args);
  13. result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
  14. break;
  15. case DELETE:
  16. param = this.method.convertArgsToSqlCommandParam(args);
  17. result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
  18. break;
  19. case SELECT:
  20. // 如果是SELECT,还会判断返回的数量以及返回类型
  21. if (this.method.returnsVoid() && this.method.hasResultHandler()) {
  22. this.executeWithResultHandler(sqlSession, args);
  23. result = null;
  24. } else if (this.method.returnsMany()) {
  25. result = this.executeForMany(sqlSession, args);
  26. } else if (this.method.returnsMap()) {
  27. result = this.executeForMap(sqlSession, args);
  28. } else if (this.method.returnsCursor()) {
  29. result = this.executeForCursor(sqlSession, args);
  30. } else {
  31. // 其他情况,如果只有一个返回值,先解析参数
  32. param = this.method.convertArgsToSqlCommandParam(args);
  33. result = sqlSession.selectOne(this.command.getName(), param);
  34. }
  35. break;
  36. case FLUSH:
  37. result = sqlSession.flushStatements();
  38. break;
  39. default:
  40. throw new BindingException("Unknown execution method for: " + this.command.getName());
  41. }
  42. if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
  43. throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
  44. } else {
  45. return result;
  46. }
  47. }

3. DefaultSqlSession的selectOne方法

调用sqlSession.selectOne(this.command.getName(), param),SqlSession的原生方法:

  1. // DefaultSqlSession的selectOne方法
  2. public <T> T selectOne(String statement, Object parameter) {
  3. List<T> list = this.selectList(statement, parameter);
  4. if (list.size() == 1) {
  5. return list.get(0);
  6. } else if (list.size() > 1) {
  7. throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
  8. } else {
  9. return null;
  10. }
  11. }

就算是查询单个,最后也是调用selectList,返回第一个元素

  1. // DefaultSqlSession的selectOne方法
  2. public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  3. List var5;
  4. try {
  5. // 从全局配置中获取statement对应的MappedStatement信息
  6. MappedStatement ms = this.configuration.getMappedStatement(statement);
  7. // 调用Executor的增删改查
  8. var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
  9. } catch (Exception var9) {
  10. throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9);
  11. } finally {
  12. ErrorContext.instance().reset();
  13. }
  14. return var5;
  15. }

调用Executor的增删改查:this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

  1. //Collection array list 参数名称逻辑,了解即可
  2. private Object wrapCollection(Object object) {
  3. DefaultSqlSession.StrictMap map;
  4. if (object instanceof Collection) {
  5. map = new DefaultSqlSession.StrictMap();
  6. map.put("collection", object);
  7. if (object instanceof List) {
  8. map.put("list", object);
  9. }
  10. return map;
  11. } else if (object != null && object.getClass().isArray()) {
  12. map = new DefaultSqlSession.StrictMap();
  13. map.put("array", object);
  14. return map;
  15. } else {
  16. return object;
  17. }
  18. }

4. Executor的query系列(不是重要方法)

  1. // Executor的query 不是重要方法
  2. public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  3. // BoundSql:Sql语句相关的信息,参数等
  4. BoundSql boundSql = ms.getBoundSql(parameterObject);
  5. // 缓存相关,了解即可
  6. CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
  7. return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  8. }
  9. // this.query() 5个参数的重载方法,不是重要方法
  10. public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  11. // 缓存相关, 从缓存中拿
  12. // 这里是二级缓存
  13. Cache cache = ms.getCache();
  14. if (cache != null) {
  15. this.flushCacheIfRequired(ms);
  16. if (ms.isUseCache() && resultHandler == null) {
  17. this.ensureNoOutParams(ms, parameterObject, boundSql);
  18. List<E> list = (List)this.tcm.getObject(cache, key);
  19. if (list == null) {
  20. list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  21. this.tcm.putObject(cache, key, list);
  22. }
  23. return list;
  24. }
  25. }
  26. // 真正的Executor进行执行方法
  27. return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  28. }

5. Executor执行方法,默认是SIMPLE

  1. // Executor执行方法,默认是SIMPLE
  2. public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  3. ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
  4. if (this.closed) {
  5. throw new ExecutorException("Executor was closed.");
  6. } else {
  7. if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
  8. this.clearLocalCache();
  9. }
  10. List list;
  11. try {
  12. ++this.queryStack;
  13. // 缓存相关,一级缓存在这里
  14. list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
  15. if (list != null) {
  16. this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
  17. } else {
  18. //*********************主要方法*********************
  19. list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
  20. //************************************************
  21. }
  22. } finally {
  23. --this.queryStack;
  24. }
  25. if (this.queryStack == 0) {
  26. Iterator i$ = this.deferredLoads.iterator();
  27. while(i$.hasNext()) {
  28. BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)i$.next();
  29. deferredLoad.load();
  30. }
  31. this.deferredLoads.clear();
  32. if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
  33. this.clearLocalCache();
  34. }
  35. }
  36. return list;
  37. }
  38. }

6. BaseExecutor的queryFromDatabase

  1. //BaseExecutor的queryFromDatabase
  2. private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  3. // 缓存中放一个占位符
  4. this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);
  5. List list;
  6. try {
  7. list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
  8. } finally {
  9. this.localCache.removeObject(key);
  10. }
  11. // 数据保存在缓存中(一级缓存)
  12. this.localCache.putObject(key, list);
  13. if (ms.getStatementType() == StatementType.CALLABLE) {
  14. this.localOutputParameterCache.putObject(key, parameter);
  15. }
  16. return list;
  17. }

7. SimpleExecutor的doQuery方法

  1. //SimpleExecutor的doQuery方法
  2. //传入参数:MappedStatement、parameter参数、rowBounds(数据数量限制,不重要,Mybatis做逻辑分页的)、resultHandler(到这里还是null)、boundSql(Sql语句信息)
  3. public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  4. // 这个Statement就是原生JDBC的Statement
  5. Statement stmt = null;
  6. List var9;
  7. try {
  8. Configuration configuration = ms.getConfiguration();
  9. // 四大对象之一,StatementHandler可以创建出Statement对象
  10. // 创建了一个PreparedStatement对象,Prepared是默认值,也可以是Callable等
  11. StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
  12. stmt = this.prepareStatement(handler, ms.getStatementLog());
  13. var9 = handler.query(stmt, resultHandler);
  14. } finally {
  15. this.closeStatement(stmt);
  16. }
  17. return var9;
  18. }

8. Configuration的newStatementHandler

  1. // Configuration的newStatementHandler
  2. public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  3. StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
  4. // 这里创建了StatementHandler之后,也会执行所有拦截器的方法
  5. StatementHandler statementHandler = (StatementHandler)this.interceptorChain.pluginAll(statementHandler);
  6. return statementHandler;
  7. }

9. PreparedStatementHandler的query

预编译SQL:PreparedStatement,预编译使用ParamenterHandler设置参数

  1. // PreparedStatementHandler的query
  2. public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  3. PreparedStatement ps = (PreparedStatement)statement;
  4. ps.execute();
  5. // 数据查出来后,使用resultSetHandler封装数据
  6. return this.resultSetHandler.handleResultSets(ps);
  7. }

10. handleResultSets处理参数

  1. // handleResultSets处理参数
  2. // 还是使用到了原生的JDBC
  3. public List<Object> handleResultSets(Statement stmt) throws SQLException {
  4. ErrorContext.instance().activity("handling results").object(this.mappedStatement.getId());
  5. List<Object> multipleResults = new ArrayList();
  6. int resultSetCount = 0;
  7. ResultSetWrapper rsw = this.getFirstResultSet(stmt);
  8. List<ResultMap> resultMaps = this.mappedStatement.getResultMaps();
  9. int resultMapCount = resultMaps.size();
  10. this.validateResultMapsCount(rsw, resultMapCount);
  11. while(rsw != null && resultMapCount > resultSetCount) {
  12. ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount);
  13. this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);
  14. rsw = this.getNextResultSet(stmt);
  15. this.cleanUpAfterHandlingResultSet();
  16. ++resultSetCount;
  17. }
  18. String[] resultSets = this.mappedStatement.getResultSets();
  19. if (resultSets != null) {
  20. while(rsw != null && resultSetCount < resultSets.length) {
  21. ResultMapping parentMapping = (ResultMapping)this.nextResultMaps.get(resultSets[resultSetCount]);
  22. if (parentMapping != null) {
  23. String nestedResultMapId = parentMapping.getNestedResultMapId();
  24. ResultMap resultMap = this.configuration.getResultMap(nestedResultMapId);
  25. this.handleResultSet(rsw, resultMap, (List)null, parentMapping);
  26. }
  27. rsw = this.getNextResultSet(stmt);
  28. this.cleanUpAfterHandlingResultSet();
  29. ++resultSetCount;
  30. }
  31. }
  32. return this.collapseSingleResultList(multipleResults);
  33. }

11. Resulthandler的getPropertyMappingValue

  1. // Resulthandler的getPropertyMappingValue,
  2. // 将查询的值和属性值映射起来
  3. private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
  4. if (propertyMapping.getNestedQueryId() != null) {
  5. return this.getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
  6. } else if (propertyMapping.getResultSet() != null) {
  7. this.addPendingChildRelation(rs, metaResultObject, propertyMapping);
  8. return DEFERED;
  9. } else {
  10. TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
  11. String column = this.prependPrefix(propertyMapping.getColumn(), columnPrefix);
  12. return typeHandler.getResult(rs, column);
  13. }
  14. }

总结

四大对象,代理对象使用里面的SqlSessionSqlSession又使用里面的Executor。使用StatementHandler设置Sql预编译(创建StatementHandler的同时,会创建ParameterhandlerResultSetHandler),使用Parameterhandler设置参数,使用ResultSetHandler处理结果,这两个都涉及到TypeHandler
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

其他

1、参数值的获取(#、$)

  1. #{}:可以获取map中的值或者pojo对象属性的值;
  2. ${}:可以获取map中的值或者pojo对象属性的值;
  3. select * from tbl_employee where id=${id} and last_name=#{lastName}
  4. Preparing: select * from tbl_employee where id=2 and last_name=?
  5. 区别:
  6. #{}:是以预编译的形式,将参数设置到sql语句中;PreparedStatement;防止sql注入
  7. ${}:取出的值直接拼装在sql语句中;会有安全问题;
  8. 大多情况下,我们去参数的值都应该去使用#{};
  9. 原生jdbc不支持占位符的地方我们就可以使用${}进行取值
  10. 比如分表、排序。。。;按照年份分表拆分
  11. select * from ${
  12. year}_salary where xxx;
  13. select * from tbl_employee order by ${f_name} ${
  14. order}
  15. #{}:更丰富的用法:
  16. 规定参数的一些规则:
  17. javaType jdbcType mode(存储过程)、 numericScale
  18. resultMap typeHandler jdbcTypeName expression(未来准备支持的功能);
  19. jdbcType通常需要在某种特定的条件下被设置:
  20. 在我们数据为null的时候,有些数据库可能不能识别mybatisnull的默认处理。比如Oracle(报错);
  21. JdbcType OTHER:无效的类型;因为mybatis对所有的null都映射的是原生JdbcOTHER类型,oracle不能正确处理;
  22. 由于全局配置中:jdbcTypeForNull=OTHERoracle不支持;两种办法
  23. 1、#{email,jdbcType=OTHER};
  24. 2jdbcTypeForNull=NULL
  25. <setting name="jdbcTypeForNull" value="NULL"/>

2、 映射文件

  • 返回list:resultMap填list元素类型就可以
  • 返回一个Map

    可能遇到这样的需求:查出来一个对象,但是没有定义这个对象的实体类。单条记录:resultMap=“map”。

    多条记录封装Map:resultMap还是填元素类型就可以,但是在接口方法处使用一个注解告诉返回的Map使用哪一个属性作为Key:@MapKey("id")

  • 自定义属性封装规则:(resultType

    resultTyperesultMap只能用一个

    1. <resultMap type="com.atguigu.mybatis.bean.Employee" id="MySimpleEmp">
    2. <!--指定主键列的封装规则 id定义主键会底层有优化; column:指定哪一列 property:指定对应的javaBean属性 -->
    3. <id column="id" property="id"/>
    4. <!-- 定义普通列封装规则 -->
    5. <result column="last_name" property="lastName"/>
    6. <!-- 其他不指定的列会自动封装:我们只要写resultMap就把全部的映射规则都写上。 -->
    7. <result column="email" property="email"/>
    8. <result column="gender" property="gender"/>
    9. </resultMap>
    10. <!-- resultMap:自定义结果集映射规则; -->
    11. <!-- public Employee getEmpById(Integer id); -->
    12. <select id="getEmpById" resultMap="MySimpleEmp">
    13. select * from tbl_employee where id=#{id}
    14. </select>
  • 关联查询

    级联属性封装

    1. <!-- 联合查询:级联属性封装结果集 -->
    2. <resultMap type="com.atguigu.mybatis.bean.Employee" id="MyDifEmp">
    3. <id column="id" property="id"/>
    4. <result column="last_name" property="lastName"/>
    5. <result column="gender" property="gender"/>
    6. <result column="did" property="dept.id"/>
    7. <result column="dept_name" property="dept.departmentName"/>
    8. </resultMap>
    9. SQL:
    10. <select id="getEmpAndDept" resultMap="MyDifEmp">
    11. SELECT e.id id,e.last_name last_name,e.gender gender,e.d_id d_id,
    12. d.id did,d.dept_name dept_name FROM tbl_employee e,tbl_dept d
    13. WHERE e.d_id=d.id AND e.id=#{id}
    14. </select>

    另一种办法association

    1. <!-- 使用association定义关联的单个对象的封装规则; -->
    2. <resultMap type="com.atguigu.mybatis.bean.Employee" id="MyDifEmp2">
    3. <id column="id" property="id"/>
    4. <result column="last_name" property="lastName"/>
    5. <result column="gender" property="gender"/>
    6. <!-- association可以指定联合的javaBean对象 property="dept":指定哪个属性是联合的对象 javaType:指定这个属性对象的类型[不能省略] -->
    7. <association property="dept" javaType="com.atguigu.mybatis.bean.Department">
    8. <id column="did" property="id"/>
    9. <result column="dept_name" property="departmentName"/>
    10. </association>
    11. </resultMap>

    association可以分步查询(需要定义两条SQL)

    1. <!-- 使用association进行分步查询: 1、先按照员工id查询员工信息 2、根据查询员工信息中的d_id值去部门表查出部门信息 3、部门设置到员工中; -->
    2. <!-- id last_name email gender d_id -->
    3. <resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmpByStep">
    4. <id column="id" property="id"/>
    5. <result column="last_name" property="lastName"/>
    6. <result column="email" property="email"/>
    7. <result column="gender" property="gender"/>
    8. <!-- association定义关联对象的封装规则 select:表明当前属性是调用select指定的方法查出的结果 column:指定将哪一列的值传给这个方法 流程:使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性 -->
    9. <association property="dept" select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById" column="d_id">
    10. </association>
    11. </resultMap>
  1. <!-- public Employee getEmpByIdStep(Integer id);-->
  2. <select id="getEmpByIdStep" resultMap="MyEmpByStep">
  3. select * from tbl_employee where id=#{id}
  4. </select>
  5. 分布查询可以支持延迟加载:
  6. <!-- 可以使用延迟加载(懒加载);(按需加载) Employee==>Dept: 我们每次查询Employee对象的时候,都将一起查询出来。 部门信息在我们使用的时候再去查询; 分段查询的基础之上加上两个配置: -->
  7. 加上两个配置:
  8. setting中,加:lazyLoading=true、aggressiveLazyLoading=fasle
  9. 关联集合查询:查询部门的时候,找到所有的部门员工:
  10. <!-- 场景二: 查询部门的时候将部门对应的所有员工信息也查询出来:注释在DepartmentMapper.xml中 -->
  11. <!-- public class Department { private Integer id; private String departmentName; private List<Employee> emps; did dept_name || eid last_name email gender -->
  12. <!--嵌套结果集的方式,使用collection标签定义关联的集合类型的属性封装规则 -->
  13. <resultMap type="com.atguigu.mybatis.bean.Department" id="MyDept">
  14. <id column="did" property="id"/>
  15. <result column="dept_name" property="departmentName"/>
  16. <!-- collection定义关联集合类型的属性的封装规则 ofType:指定集合里面元素的类型 -->
  17. <collection property="emps" ofType="com.atguigu.mybatis.bean.Employee">
  18. <!-- 定义这个集合中元素的封装规则 -->
  19. <id column="eid" property="id"/>
  20. <result column="last_name" property="lastName"/>
  21. <result column="email" property="email"/>
  22. <result column="gender" property="gender"/>
  23. </collection>
  24. </resultMap>
  25. <!-- public Department getDeptByIdPlus(Integer id); -->
  26. <select id="getDeptByIdPlus" resultMap="MyDept">
  27. SELECT d.id did,d.dept_name dept_name,
  28. e.id eid,e.last_name last_name,e.email email,e.gender gender
  29. FROM tbl_dept d
  30. LEFT JOIN tbl_employee e
  31. ON d.id=e.d_id
  32. WHERE d.id=#{id}
  33. </select>

发表评论

表情:
评论列表 (有 0 条评论,86人围观)

还没有评论,来说两句吧...

相关阅读

    相关 MyBatis框架

    MyBatis是一个优秀的持久层框架,通过简洁的XML配置方式就能消除以前传统 使用JDBC出现大量重复的代码,以及参数的设置和结果集的映射. 如果能够懂的底层代码和原理,