美文网首页
mybatis dao 映射

mybatis dao 映射

作者: 吃花生的小猴子 | 来源:发表于2018-01-09 17:16 被阅读0次

在使用mybatis时,我们通常直接调用接口dao(也就是常说的mapper)中的方法,然后应用会自动调用xml文件中对应标签(select |update| insert|delete)中id与dao中方法名相同的sql,dao通过xml文件中的namespace做映射,调用dao即是调用xml中的sql。是怎么实现的呢?

SqlSessionFactoryBean

mybatis 通常与spring 一起使用,配置SqlSessionFactoryBean

<bean class="org.mybatis.spring.SqlSessionFactoryBean">
                <!-- 实例化sqlSessionFactory时需要使用上述配置好的数据源以及SQL映射文件 -->
                <property name="dataSource" ref="mailaDataSource"/>
                <property name="mapperLocations" value="classpath*:mybatis/maila/**/*.xml"/>
                <property name="configLocation" value="classpath:mybatis/sqlMapConfig.xml"/>
                <property name="typeAliasesPackage" value="com.maila.biz.center.biz.entity,com.maila88.**.entity"/>
            </bean>

首先我们来看一下SqlSessionFactoryBean,该bean 实现了InitializingBean接口,初始化完成时会调用afterPropertiesSet方法

public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
              "Property 'configuration' and 'configLocation' can not specified with together");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }

rotected SqlSessionFactory buildSqlSessionFactory() throws IOException {

    Configuration configuration;

    XMLConfigBuilder xmlConfigBuilder = null;

    ...

}

buildSqlSessionFactory方法内容很多,大致上就是加载xml(xml包含sql的xml和setting设置的xml),解析xml等,包装mybatis的configuration。这里不做过多的介绍,这里我们详细介绍一样dao与xml的映射问题。在buildSqlSessionFactory方法中有这么一段代码:

if (!isEmpty(this.mapperLocations)) {
      for (Resource mapperLocation : this.mapperLocations) {
        if (mapperLocation == null) {
          continue;
        }

        try {
        //mapperLocation路径转换为xmlMappperBuilder
          XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
              configuration, mapperLocation.toString(), configuration.getSqlFragments());
        //解析xml
          xmlMapperBuilder.parse();
        } catch (Exception e) {
          throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
        } finally {
          ErrorContext.instance().reset();
        }

        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
        }
      }
    } else {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
      }
    }

上述代码把<propertyname="mapperLocations"value="classpath*:mybatis/maila/**/*.xml"/>
转化为xmlMapperBuilder,并加以parse();

parse()

public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      //设置xml中的标签元素
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      //mapper 与namespace 绑定
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }

这里我们详细看一下mapper 与namespace的绑定

private void bindMapperForNamespace() {
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
      Class<?> boundType = null;
      try {
        boundType = Resources.classForName(namespace);
      } catch (ClassNotFoundException e) {
        //ignore, bound type is not required
      }
      if (boundType != null) {
        if (!configuration.hasMapper(boundType)) {
          // Spring may not know the real resource name so we set a flag
          // to prevent loading again this resource from the mapper interface
          // look at MapperAnnotationBuilder#loadXmlResource
          configuration.addLoadedResource("namespace:" + namespace);
          configuration.addMapper(boundType);
        }
      }
    }
  }

绑定做了几件事情

  • 取得当前的namaspace(即mapper接口)的全限定路径名
  • 反射得到类
  • 类添加至mybatis configuration -- configuration.addMapper(boundType)

addMapper方法:

protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

  public <T> void addMapper(Class<T> type) {
    mapperRegistry.addMapper(type);
  }

MapperRegistry的addMapper方法:

private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();


public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

knownMappers 以类为key,MapperProxyFactory 为value,也就是说:mybatis 为每个dao接口都创建了一个代理工厂。

public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

}

代理工厂实例化负责实例化每个接口dao,并用一个private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>(); 储存dao中的方法调用,MapperMethod又是一个很大的内容,后续再谈。

至此,我们知道了mybatis configuration中有个mapperRegistry,mapperRegistry中有一个Map,保存了所有dao和他的代理。

相关文章

  • mybatis dao 映射

    在使用mybatis时,我们通常直接调用接口dao(也就是常说的mapper)中的方法,然后应用会自动调用xml文...

  • 介绍两个插件

    1.Mybatis plugins:mybatis dao层的方法直接可以映射到xml文件对应的sql语句中,修改...

  • 通过mybatis工具generatorConfig.xml自动

    通过mybatis工具generatorConfig.xml自动生成实体,DAO,映射文件 github链接 简介...

  • 第4章 店铺注册功能模块

    学习目标 连接数据库 Mybatis数据表映射关系的配置 dao -> service -> controller...

  • MyBatis详解

    MyBatis简介 简介 来源于apache,关注于持久层(数据库层、po层、dao层)的orm(对象-关系-映射...

  • spring和mybatis整合开发

    mybatis和mybatis整合的思路: 一、传统的方式开发dao 编写dao接口 编写dao接口实现类继承Sq...

  • [java]40、MyBatis-dao

    1、使用MyBatis实现dao层 1.1、使用MyBatis实现dao层的几种方式 1、自定义dao实现类,在实...

  • mybatis映射-04-04

    mybatis中的mapping和dao放在一起,这样才能完成方法和查询语句的映射❌只要配置了SqlSession...

  • MyBatis代码自动生成

    利用MyBatis生成器自动生成实体类、DAO接口和Mapping映射文件。这样可以大大节约开发时间,将生成的代码...

  • mybatis框架的学习(xml部分)

    mybatis定义:是支持定制化SQL,存储过程以及高级映射的优秀的持久层框架,也就是dao层(java与数据库连...

网友评论

      本文标题:mybatis dao 映射

      本文链接:https://www.haomeiwen.com/subject/dlfgnxtx.html