Mybatis(二)

Mybatis的config文件配置

  • properties标签
    properties为属性配置文件,它为上下文提供相关的资源,properties有三种配置资源的方式:

方式一:通过properties子元素配置

通过properties子元素property配置username和password变量,然后在properties下文environment节点中引用这些变量

<properties>
   <property name="username" value="root"/>
   <property name="password" value="123456"/>
</properties>

在environments节点中引用username和password变量

<environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

方式二:通过properise的属性配置

<properties resource="dbConfig/db.properties" />

这里我们是在maven的resources下创建的dbConfig/db.properties

方式三:通过属性参数传递配置

即把属性传递到SqlSessionFactoryBuilder.build()中,让初始化Configuration时将值传递进去

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);
//重载
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, props);

Tip:当在mybatis配置文件中同时存在三种方式时,优先级顺序如下:

(1)首先读取properties子元素属性

(2)其次读取properties元素的属性(resource和url)

(3)最后读取作为方法参数传递的属性,并覆盖以读取的同名属性

三种方式,存在优先级,且排在后面的配置覆盖排在前面同名属性的配置,鉴于此,建议在配置时,不要使用混合方式。

  • settings属性
    这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。
  • cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 有效值true | false 默认值:true
  • lazyLoadingEnabled 是否开启懒加载 默认false
  • useGeneratedKeys 是否开启主键自增 默认false
    等等

完整的如下:

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

详细的内容可参考Mybatis的官方文档:https://mybatis.org/mybatis-3/zh/configuration.html#settings

  • typeAliases类别名
    类型别名为Java类型设置一个简短名字,它只与xml有关,用来减少类完全限定名的冗余
<typeAliases>
   <typeAlias type="com.xx.xx.xx(全类名)" alias="自定义别名"/>
</typeAliases>

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

    <typeAliases>
        <package name="com.sang.entity" />
    </typeAliases>

这样在com.sang.entity以及其子包下的所有JavaBean都会使用 JavaBean的首字母小写的非限定类名来作为它的别名,当然,存在问题:当子包中出现类名相同的情况
这时可以使用注解@Alias("自定义别名")加在多个名重复的类上,那么就会分开识别。

  • typeHandlers
    typeHandlers主要将获取的值合理地转化为java类型,可以转换预处理阶段(PreparedStatement)的参数和结果集中的值。mybatis提供了标准的类型处理,详细请参照官网:http://www.mybatis.org/mybatis-3/zh/configuration.html#typeHandlers

  • plugins
    MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)执行器
  • ParameterHandler (getParameterObject, setParameters)参数处理器
  • ResultSetHandler (handleResultSets, handleOutputParameters)结果映射处理器
  • StatementHandler (prepare, parameterize, batch, update, query)sql处理器

通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。

// ExamplePlugin.java
@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  private Properties properties = new Properties();
  public Object intercept(Invocation invocation) throws Throwable {
    // implement pre processing if need
    Object returnObject = invocation.proceed();
    // implement post processing if need
    return returnObject;
  }
  public void setProperties(Properties properties) {
    this.properties = properties;
  }
}
<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

上面的插件将会拦截在 Executor 实例中所有的 “update” 方法调用, 这里的 Executor 是负责执行底层映射语句的内部对象。

  • environments

MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中,
现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的
SQL 映射。还有许多类似的使用场景。

<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
  </environment>
</environments>

注意一些关键点:

默认使用的环境 ID(比如:default="development")。
每个 environment 元素定义的环境 ID(比如:id="development")。
事务管理器的配置(比如:type="JDBC")。
数据源的配置(比如:type="POOLED")。

默认环境和环境 ID 顾名思义。 环境可以随意命名,但务必保证默认的环境 ID 要匹配其中一个环境 ID。
事务管理器(transactionManager)

在 MyBatis 中有两种类型的事务管理器,type的值

  • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
  • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。例如:
<transactionManager type="MANAGED">
  <property name="closeConnection" value="false"/>
</transactionManager>

如果你正在使用 Spring + MyBatis,
则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

这两种事务管理器类型都不需要设置任何属性。它们其实是类型别名,换句话说,你可以用 TransactionFactory
接口实现类的全限定名或类型别名代替它们。

dataSource 的type:

  • UNPOOLED:每次连接都需要开启和关闭连接
  • POOLED:连接池的概念
  • JNDI :这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。这种数据源配置只需要两个属性
  • databaseIdProvider

MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis
会加载带有匹配当前数据库 databaseId 属性和所有不带 databaseId 属性的语句。 如果同时找到带有 databaseId
和不带 databaseId 的相同语句,则后者会被舍弃。 为支持多厂商特性,只要像下面这样在 mybatis-config.xml
文件中加入 databaseIdProvider 即可:

<databaseIdProvider type="DB_VENDOR" />


<databaseIdProvider type="DB_VENDOR">
  <property name="SQL Server" value="sqlserver"/>
  <property name="DB2" value="db2"/>
  <property name="Oracle" value="oracle" />
</databaseIdProvider>
  • mappers
    既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。

第一种:resource:在类路径下的mapper的xml配置文件
这里是在maven环境下,文件名需要/分离

    <mappers>
       <mapper resource="com/sang/dao/StudentDao.xml"/>
    </mappers>

第二种:url:使用完全限定资源定位符
可以是磁盘的文件

    <mappers>
       <mapper url="file:///com/sang/dao/StudentDao.xml"/>
    </mappers>

第三种:class:接口全限定名称

    <mappers>
       <mapper url="com.sang.dao.StudentDao"/>
    </mappers>

第四种:package作为包扫描:接口的包全类名

    <mappers>
        <package name="com.sang.dao"/>
    </mappers>

Mybatis运行原理

初始化Configuration对象

public class MybatisUtil {
    private static SqlSessionFactory sessionFactory =null;
    private static String configPath="mybatisConfig/mybatisConfig.xml";
    static{
        try {
            InputStream inputStream = Resources.getResourceAsStream(configPath);
            sessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static SqlSession getSqlsession(){
        SqlSession sqlSession=null;
        if(sessionFactory!=null){
            sqlSession= sessionFactory.openSession();//openSession(true)表示事务自己控制
        }
        return sqlSession;
    }
}

根据编写好的Mybatis配置文件,创建出流对象,SqlSessionFactoryBuilder根据流对象在底层初始化Configuration对象

点击build()方法进入源码分析:

 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
        	//  XMLConfigBuilder是专门解析mybatis的配置文件的类
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            //这里又调用了一个重载方法。parser.parse()的返回值是Configuration对象
            var5 = this.build(parser.parse());
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
        } finally {
            ErrorContext.instance().reset();

            try {
                inputStream.close();
            } catch (IOException var13) {
            }

        }

        return var5;
    }

这里的XMLConfiguration是根据流对象xml文件中的标签及其属性进行封装,this.build(parser.parse())随后进行调用方法也就是返回Configuration对象

也就是说,初始化配置文件信息的本质就是创建Configuration对象,将解析的xml数据封装到Configuration内部的属性中。

SqlSession对象

SqlSession是一个接口,它有两个实现类:DefaultSqlSession(默认)和SqlSessionManager(弃用,不做介绍)
SqlSession是MyBatis中用于和数据库交互的接触类,通常将它与ThreadLocal绑定,一个连接会话使用一个SqlSession,并且在使用完毕后需要close关闭。

private final Configuration configuration;
private final Executor executor;

SqlSession中的两个最重要的参数,configuration就是上文的流对象创建返回的Configuration对象,Executor为执行器
进入openSession方法中查看源码:

// 进入openSession方法。
  public SqlSession openSession() {
  	//getDefaultExecutorType()传递的是SimpleExecutor
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

//进入openSessionFromDataSource。
//ExecutorType 为Executor的类型,TransactionIsolationLevel为事务隔离级别,autoCommit是否开启事务
//openSession的多个重载方法可以指定获得的SeqSession的Executor类型和事务的处理
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //根据参数创建指定类型的Executor
      final Executor executor = configuration.newExecutor(tx, execType);
      //返回的是DefaultSqlSession
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

Executor

Executor也是一个接口,他有三个常用的实现类BatchExecutor(重用语句并执行批量更新),ReuseExecutor(重用预处理语句prepared statements),SimpleExecutor(普通的执行器,默认)。

SqlSession API方法

点击Sqlsession接口进入源码,发现许多增删改查的方法
举一例子:

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      //根据传入的全限定名+方法名从映射的Map中取出MappedStatement对象
      MappedStatement ms = configuration.getMappedStatement(statement);
      //调用Executor中的方法处理
      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();
    }
 }

关键:

1 根据传入的全限定名+方法名从映射的Map中取出MappedStatement对象
2 调用Executor中的方法处理

MappedStatement

  • 作用: MappedStatement与Mapper配置文件中的一个select/update/insert/delete标签结点相对应。mapper中配置的标签都被封装到了此对象中,主要用途是描述一条SQL语句
  • 初始化过程:回顾刚开始介绍的加载配置文件的过程中,会对mybatis-config.xml中的各个标签都进行解析,其中有 mappers标签用来注册mapper.xml文件或者配置mapper接口的目录。

例如:

<select id="findStudentById" resultType="student">
        select * from student where id=#{id}
</select>

这样的在mapper文件的一个标签,在初始化Configuration对象时,被解析封装成一个MappedStatement对象,然后存储在Configuration对象的mappedStatements属性中,mappedStatements 是一个HashMap,存储时key = 全类名 + 方法名value = 对应的MappedStatement对象

在Configuration对象中MappedStatements属性如下:

 Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");

最后 executor.query()进行执行根据map中根据全类名.方法名查到的sql语句(也就是MappedStatement)对象

接口式执行原理

单元测试如下:

    @Test
    public void Test() throws IOException {
        InputStream inputStream= Resources.getResourceAsStream("mybatisConfig/mybatisConfig.xml");
        SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sessionFactory = factoryBuilder.build(inputStream);
        SqlSession sqlSession = sessionFactory.openSession();
        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
        Student student = studentDao.findStudentById(1);
        System.out.println(student);
        sqlSession.close();
    }

与原生方式在初始化configuration对象到创建sqlsession对象相似,但是
sqlsession对象调用getMapper对象获得接口的字节码对象进行创建代理对象
这是关键!

先说明一下MyBatis初始化时对接口的处理:MapperRegistry是Configuration中的一个属性,它内部拥有一个属性HashMap用于存放mapper接口的工厂类,每个接口对应一个工厂类。mappers中可以配置接口的包路径,或者某个具体的接口类。

<mappers>
        <package name="com.sang.dao"/>
       <!-- <mapper resource="com/sang/dao/StudentDao.xml"/>-->
</mappers>
  • 当解析mappers标签时,它会判断解析到的是mapper配置文件时,会再将对应配置文件中的增删改查标签一 一封装成MappedStatement对象,存入mappedStatements中。
  • 当判断解析到接口时,会创建此接口对应的MapperProxyFactory对象,存入HashMap中,key = 接口的字节码对象,value = 此接口对应的MapperProxyFactory对象。
    动态代理在第一篇已经写有

在动态代理返回了实例后,我们就可以直接调用mapper类中的方法了,说明在MapperProxy中的invoke方法中已经为我们实现了方法。

接口class文件和mapper.xml文件位置分析

我们都知道:在config文件中配置

    <mappers>
        <package name="com.sang.dao"/>
       <!-- <mapper resource="com/sang/dao/StudentDao.xml"/>-->
    </mappers>

然后mybatis根据config.xml配置文件加载后初始化configuration对象,在这里方法中定义了一个while死循环,主要是便利mappers节点下面的所有元素。因为我们采用的是在主配置文件中使用package扫描的方式挂载的mapper映射文件。所以跳入if代码块。在if块中通过获取name属性的值,拿到了mapper文件的所属的包名,通过configuration对象调用addMappers方法把mapper映射文件所在的包传入。如:
在这里插入图片描述
创建ResolverUtil工具类,通过调用find方法把包下面的字节码对象找出来,并存入到Set集合中,通过调用getClasses方法取出,进行遍历。把每一个字节码对象传入addMapper方法。如
在这里插入图片描述

意思就是:在初始化configuration对象的时候,对xml文件进行提取属性,判断用了package标签后,就行添加指定的mapper.xml文件,并且通过mapper.xml文件的位置进行查找相同名字的java字节码文件,将其加载到hashMap中去

所以,mapper文件和接口文件在最后的生成的target目录中必须在同一目录

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值