MyBatis 的 DAO 接口跟 XML 文件里面的 SQL 是如何建立关系的

MyBatis 会先解析这些 XML 文件,通过 XML 文件里面的命名空间 (namespace)跟 DAO 建立关系;然后 XML 中的每段 SQL 会有一个id 跟 DAO 中的接口进行关联。

首先我们要知道每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的,SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。

 SqlSessionFactory 是一个接口,它里面其实就两个方法:openSessiongetConfiguration

其中,openSession 方法是为了获取一个 SqlSession 对象,完成必要数据库增删改查功能。但是,SqlSessionFactory 属性太少了,所以需要 getConfiguration 的配合;来配置mapper 映射文件、SQL 参数、返回值类型、缓存等属性。

/**
 * Creates an {@link SqlSession} out of a connection or a DataSource
 * 
 * @author Clinton Begin
 */
public interface SqlSessionFactory {

  SqlSession openSession();

  SqlSession openSession(boolean autoCommit);
  SqlSession openSession(Connection connection);
  SqlSession openSession(TransactionIsolationLevel level);

  SqlSession openSession(ExecutorType execType);
  SqlSession openSession(ExecutorType execType, boolean autoCommit);
  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
  SqlSession openSession(ExecutorType execType, Connection connection);

  Configuration getConfiguration();

}
View Code

可以看到 getConfiguration 是属于 Configuration 类的一个方法。你可以把它当成一个配置管家。MyBatis 所有的配置信息都维持在 Configuration 对象之中,基本每个对象都会持有它的引用。

但日常开发中我们都是将 MyBatis 与 Spring 一起使用的,所以把实例化交给 Spring 处理。

因此我们可以看下 org.MyBatis.spring.SqlSessionFactoryBean,它实现了 InitializingBean 接口。这说明,在这个类被实例化之后会调用到 afterPropertiesSet()。它只有一个方法

public void afterPropertiesSet() throws Exception {
    this.sqlSessionFactory = buildSqlSessionFactory();
}

而这个 afterPropertiesSet 方法只有一个动作,就是 buildSqlSessionFactory。它可以分为两部分来看:

  • 1、从配置文件的 property 属性中加载各种组件,解析配置到 configuration 中
  • 2、加载 mapper 文件,解析 SQL 语句,封装成 MappedStatement 对象,配置到 configuration 中。

mapper 接口方法是怎样被调用到的?

大致有如下两种方式:

  • MyBatis 提供的 API

使用 MyBatis 提供的 API 进行操作,通过获取 SqlSession 对象,然后根据 Statement Id 和参数来操作数据库。

String statement = "com.mmzsblog.business.DAO.MemberMapper.getMemberList";
List<Member> result = sqlsession.selectList(statement);
  • mapper 接口

定义 Mapper 接口,并在里面定义一系列业务数据操作方法。在 Service 层通过注入 mapper 属性,调用其方法就可以执行数据库操作。

public interface MemberMapper {    
    List<Member> getMemberList();
}

@Service
public class MemberServiceImpl implements MemberService{
    @Resource
    private MemberMapper memberMapper;
    
    @Override
    public List<Member> getMemberList() {
        return memberMapper.getMemberList();
    }
}
View Code

Mapper 接口的代理创建过程

首先我们会配置需要扫描的基本包路径

通过注解的方式配置@MapperScan({"com.mmzsblog.business.DAO"})

或者xml的方式配置:

<bean class="org.MyBatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.mmzsblog.business.DAO" />
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
View Code

开始扫描

来到 org.MyBatis.spring.mapper.MapperScannerConfigurer 这个类,可以看到它实现了几个接口

其中的重点是 BeanDefinitionRegistryPostProcessor。它可以动态的注册 Bean 信息,方法为 postProcessBeanDefinitionRegistry()

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        if (this.processPropertyPlaceHolders) {
            this.processPropertyPlaceHolders();
        }
        
        // 创建ClassPath扫描器,设置属性,然后调用扫描方法
        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        scanner.setAddToConfig(this.addToConfig);
        scanner.setAnnotationClass(this.annotationClass);
        scanner.setMarkerInterface(this.markerInterface);
        scanner.setSqlSessionFactory(this.sqlSessionFactory);
        scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
        scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
        scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
        scanner.setResourceLoader(this.applicationContext);
        scanner.setBeanNameGenerator(this.nameGenerator);
        // 创建ClassPath扫描器,设置属性,然后调用扫描方法
        scanner.registerFilters();
        scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
    }
View Code

ClassPathMapperScanner 继承自 Spring 中的类 ClassPathBeanDefinitionScanner,所以它的 scan 方法会调用到父类 ClassPathBeanDefinitionScanner 的 scan 方法

public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
    ……
    public int scan(String... basePackages) {
        // 
        int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
        this.doScan(basePackages);
        if (this.includeAnnotationConfig) {
            AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
        }

        return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
    }
    ……
}
View Code

猜你喜欢

转载自www.cnblogs.com/lingcheng7777/p/12123431.html
今日推荐