在项目中,目录结构有:src/main/java和src/main/resources,前者是用来存放java源代码的,后者是用来存放一些资源文件,一般情况下,我们用到的资源文件(各种xml,properites等)都放在该目录下面,在打包时,把这些资源文件打包到相应的jar或者war里。
有时候,比如mybatis的mapper.xml文件,我们习惯把它和Mapper.java放一起,都放在src/main/java下面,这样在用打包时,就需要修改pom.xml文件,把mapper.xml文件一起打包进jar或者war里了**(因为在默认的情况下,打包的时候,对于src/main/java目录只打包源代码,而不会打包其他文件。)**,否则,这些文件不会被打包的。(src/main/java只是java的源代码路径)。此种方式需要在pom.xml文件中添加如下配置(也可以使用其他的配置方式,如build-helper-maven-plugin、maven-resources-plugin插件):
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
<finalName>Driving_school</finalName>
</build>
Mybatis中接口和对应的mapper文件不一定要放在同一个包下。如果放在一起,目的是为了Mybatis进行自动扫描,并且需要注意的是,此时Java接口的名称和mapper文件的名称要相同,否则会报异常,由于此时Mybatis会自动解析对应的接口和相应的配置文件,所以就不需要配置mapper文件的位置了。
由于在默认的情况下,对于src/main/java目录,只打包源代码,而不会打包其他文件。所以此时如果把对应的mapper文件放到src/main/java目录下时,不会打包到最终的jar文件夹中,也不会输出到target文件夹中,由于在进行单元测试的时候执行的是/target/test-classes下的代码,所以在测试的时候也不会成功。
为了实现在默认环境下打包时,Mybatis的接口和mapper文件在同一包中,可以通过将接口文件(java文件)放在src/main/java某个包中,而在目录中建立同样的包,这是一种约定优于配置的方式,这样在打包的时候就会将src/main/java和src/main/resources相同包下的文件合并到同一包中。
Dao接口和XML文件里的SQL是如何一一对应的?
一句话讲完就是: mybatis会先解析这些xml文件,通过Xml文件里面的命名空间( namespac
e)跟dao建立关系;然后xm中的每段sql会有个id跟dao中的接口进行关联。
“如果我有两个这个xml文件都跟这个dao建立关系了,那不是就是冲突了?
带着这个疑问我们就要开始下面的正题了!
一、初始化
首先我们要知道每个基于 MyBatis的应用都是以一个Sq| Session Factory的实例为中心的,Sq|Session Factory的实例可以通过Sq| Session FactoryBmilder获得。但Sql Session Factory是一个接口,它里面其实就两个方法opensesslon,recOnfiguration其中, opensession方法是为了获取一个 Sql session对象,完成必要数据库增删改查功能。但是,Sql Session Factory属性太少了,所以需要 getConfiguration的配合;来配置 mapper映射文件、SQL参数、返回值类型、缓存等属性。
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();}
可以看到 get Configuration是属于 Configuration类的一个方法。你可以把它当成一个配置管家。MyBatis所有的配置信息都维持在 Configuration对象之中,基本每个对象都会持有它的引用。
但日常开发中我们都是将 Mybatis与 Spring起使用的,所以把实例化交给 Spring处理。
因此我们可以看下。 org. my batis. spring.SqlSessioFactory Bean,它实现了 nitializingBean接口。这说明,在这个类被实例化之后会调用到 after Properties set()。它只有一个方法代码:
public void afterPropertiesSet() throws Exception {
this.sqlSessionFactory = buildSqlSessionFactory(); }
而这个 afterproperties set方法只有一个动作,就是 builds1 Sessionfactory它可以分为两部分来看:
1、从配置文件的 property属性中加载各种组件,解析配置到 configuration中。
2、加载 mapper文件,解析SQL语句,封装成MappedStatement对象,配置到 configuration
中。
mapper 接口方法是怎样被调用到的MyBatis 提供的 API
使用 MyBatis 提供的 API 进行操作,通过获取 Sql Session 对象,然后根据 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(); }}
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>
开始扫描来到 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);
// 创建ClassPath扫描器,设置属性,然后调用扫描方法
scanner.registerFilters();
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));}
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; }}
小结:Mapper接口的代理创建过程大致如下1、扫描 mapper接口基本包路径下的所有对
象,将其注册为 Bean Definition对象。
设置 Bean Definition的对象的 bean class和sqISession Factory属性(而其中获取 Bean Definition对象的时候,调用其工厂方法 getobject,返回 mapper接口的代理类)
设置sq| Session Factory属性的时候,会调用
Sql session Template的构造方法,创建SqlSession接口的代理类最后我们在 Service层,通过REsourceprivate MemberMapper memberdao注入属性的时候,返回的就是代理类。执行memberdao的方法的时候,实际调用的也是代理类的jinVoke方法。Mybatis在初始化 SqlSession Factory Bean的时候找到配置需要扫描的基本包路径去解析里面所有的
XML文件。重点就在如下两个地方:
1、创建 SqlSource Mybatis会把每个SQL标签封装成 SqlSource对象
然后根据SQL语句的不同,又分为动态SQL和静态sQL。其中,静态SQL包含一段 String类型的sq|语句;而动态SQL则是由一个个 SqINode组成。
2、创建 MappedStatementXML文件中的每一个SQL标签就对应一个 MappedStatement对象,这里面有两个属性很重要。全限定类名+方法名组成的ID。
sqlSource当前SQL标签对应的Sql Source对象。创建完 Mappedstatement对象,会将它缓存到configuration#mappedstatements中前面初始化中提到的 Configuration对象,我们知道
它就是 Mybatis中的配置大管家,基本所有的配置信息都维护在这里。把所有的XML都解析完成之后, Configuration就包含了所有的SQL信息。