Mybatis - 核心配置文件Mybatis-config.xml标签详解(下)

前言

Mybatis-config.xml是Mybatis的核心配置文件,需要仔细琢磨

前面介绍了约束、properties、settings、typeAliases、typeHandlers

继续学习
Mybatis - 核心配置文件Mybatis-config.xml标签详解(下)_第1张图片

一切以官网为准,作补充、扩展


对象工厂(objectFactory)

每次 MyBatis 创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作


默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认无参构造方法,要么通过存在的参数映射来调用带有参数的构造方法。 如果想覆盖对象工厂的默认行为,可以通过创建自己的对象工厂来实现

什么意思呢?

我们前面配置了mapper.xml的结果集类型resultType
当sql语句返回执行结果时,映射到 resultType 或其他处理结果集的参数配置对应的 Java 类型,objectFactory 对象工厂就是用来创建实体对象的类

<select id="getUserList" resultType="User">
        select * from mybatis.user
</select>

前面resultType返回的是User实体类对象,测试类List userList = mapper.getUserList();直接就获得了对象,那么对象到底是谁创建的?

就是这个ObjectFactory

默认调用目标类的无参构造方法创建,也可以通过存在的参数映射来调用带有参数的构造方法

默认的ObjectFactory是DefaultObjectFactory 类,有兴趣可以查看源码,从中大致可以知道结果集映射为实体类对象的流程:

  • create(Class type, List> constructorArgTypes, List constructorArgs):这个方法中传了三个参数,分别是返回的结果集类型、此类构造函数参数类型、此类构造函数参数值 的集合。从它的内部实现来看,首先调用了下面的resolveInterface方法获取返回类型,其次调用instantiateClass方法实例化出我们所需的结果集对应的实体类对象
  • instantiateClass(Class type, List> constructorArgTypes, List constructorArgs):此方法是用来实例化一个类(也就是对应的实体类),需要实例化的类型是通过下面的resolveInterface方法决定,从内部实现来看,这个实例化过程是通过反射实现的
  • resolveInterface(Class type):此方法是用来对结果集类型进行处理,即如果我们定义一个resultType为集合类型,那么它就会根据这个类型决定出即将创建的结果集类型
  • isCollection(Class type):这个方法是用来判断我们配置的类型是不是一个集合。比如如果返回多条数据,但是我们配置resultType是个普通类,那么在执行过程中就会报错
  • setProperties(Properties properties):这个方法是用来设置一些配置信息,即objectFactory中的property子元素标签内的数据,就是通过这个方法进行配置的
  • 如何自定义

    可以实现ObjectFactory接口 或者 继承 DefaultObjectFactory 来创建自己的对象工厂,改写相关的4个方法

    package com.learn.util;
    
    import org.apache.ibatis.reflection.factory.ObjectFactory;
    
    import java.util.List;
    import java.util.Properties;
    
    public class MyObjectFactory implements ObjectFactory {
        @Override
        public <T> T create(Class<T> type) {
            return null;
        }
    
        @Override
        public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
            return null;
        }
    
        @Override
        public <T> boolean isCollection(Class<T> type) {
            return false;
        }
    
        @Override
        public void setProperties(Properties properties) {
    
        }
    }
    
    

    然后在mybatis-config.xml中添加配置:

    <objectFactory type="com.learn.util.MyObjectFactory">
        <property name="" value=""/>
    </objectFactory>
    

    子标签 property 中的属性会在加载全局配置文件 mybatis-config.xml 时通过 setProperties 方法被初始化到 MyObjectFactory 中,作为该类的全局参数使用

    可以知道加载流程:

    当 Resource 资源类加载 mybatis-config.xml 文件,并创建出 SqlSessionFactory 时,会加载配置文件中 objectFactory(默认或者自定义),并设置配置标签中的 property 参数


    插件(plugins)

    MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用

    MyBatis 允许使用插件来拦截的方法调用包括:

    • Executor (update, query, flushStatements, commit, rollback,
      getTransaction, close, isClosed):执行器拦截,sql执行的核心,可以处理很多行为,包括commit、rollback、close等等
    • ParameterHandler (getParameterObject, setParameters):参数处理器,可以获取参数、设置参数
    • ResultSetHandler (handleResultSets, handleOutputParameters):结果集处理器,可以将结果集转换List等等
    • StatementHandler (prepare, parameterize, batch, update, query):sql语句处理器,可以处理预编译状态的接口

    这是一种AOP切面思想,在映射语句执行过程中,横切在某处,定义一些方法
    插件使用的场景有:日志记录、权限控制、缓存控制等

    如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为在试图修改或重写已有方法的行为时,很可能会破坏MyBatis 的核心模块。 这些都是更底层的类和方法,所以使用插件的时候要特别当心

    具体使用:继承Interceptor接口,并指定了想要拦截的方法签名,重写方法

    Mybatis - 核心配置文件Mybatis-config.xml标签详解(下)_第2张图片

    package com.learn.util;
    
    import org.apache.ibatis.executor.Executor;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.plugin.Interceptor;
    import org.apache.ibatis.plugin.Intercepts;
    import org.apache.ibatis.plugin.Invocation;
    import org.apache.ibatis.plugin.Signature;
    
    import java.util.Properties;
    
    @Intercepts({@Signature(
            type= Executor.class,
            method = "update",
            args = {MappedStatement.class,Object.class})})
    public class MyInterceptor 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;
        }
    }
    
    

    然后在核心配置文件:

    <plugins>
       <plugin interceptor="com.learn.util.MyInterceptor">
           <property name="someProperty" value="100"/>
       </plugin>
    </plugins>
    

    这个插件就会拦截在Executor 实例中所有的 “update” 方法调用,
    Executor 是负责执行底层映射语句的内部对象(具体的执行器)

    除了用插件,还可以通过完全覆盖配置类来达到目的,继承配置类后覆盖其中的某个方法,再把它传递到SqlSessionFactoryBuilder.build(myConfig) 方法即可

    总之,不懂Mybatis的源码别动这几个方法


    环境配置(environments)

    MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中

    前面我们是直接在环境中写死了

       <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
    

    可以通过前面的properties引入外部配置文件,然后在这里修改,得到配置文件中的属性:

        <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>
    

    注意,这些属性名需要与外部配置文件中的name对应
    在这里插入图片描述

    每个 SqlSessionFactory 实例只能选择一种环境(每个数据库对应一个 SqlSessionFactory 实例,这是Mybatis的工作流程)

    当然,这个环境配置还有一些属性需要注意

    默认使用环境

    默认使用的环境 ID(比如:default=“development”),environments标签通过default选择想要的环境
    Mybatis - 核心配置文件Mybatis-config.xml标签详解(下)_第3张图片


    事务管理器(transactionManager)

    Mybatis只有两种事务管理

    • JDBC :使用 JdbcTransactionFactory 生成的 JdbcTransaction 对象实现。它是以 JDBC 的方式对数据库的提交和回滚进行操作。
    • MANAGED :使用 ManagedTransactionFactory 生成的 ManagedTransactio 对象实现。它的提交和回滚方法不用任何操作,而是把事务交给WEB容器处理(JBOSS,Weblogic)。在默认情况下,它会关闭连接
    • 当然,实现 Transaction 接口也可以自定义(但是事务管理是很复杂的,自定义很难实现)

    设置成MANAGED ,Mybatis就不管理事务了,即使我们update提交,数据库也不会有反应

    Transaction的两个实现类:
    Mybatis - 核心配置文件Mybatis-config.xml标签详解(下)_第4张图片

    基本上都是选择JDBC


    数据源(dataSource)

    dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源
    有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")

    数据源就是实现了 DataSource 接口的数据库连接对象
    在内部配置好连接数据库需要的属性

    • UNPOOLED:非数据库连接池的管理方式,每次请求都会打开一个新的数据库连接
    • POOLED:数据库连接池,使用完连接后该连接在连接池等待
    • JNDI:为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用
    • 当然,也支持自定义的数据库连接池,implements DataSourceFactory,实现DataSourceFactory和对应的DataSource,然后将DataSourceFactory路径写到type里即可

    这3个的配置都在相应的类中(工厂方法:具体实现是对应的DataSource)
    Mybatis - 核心配置文件Mybatis-config.xml标签详解(下)_第5张图片

    数据库连接池类似与线程池,常用的是POOLED

    关于type="[UNPOOLED|POOLED|JNDI]",可以设置一些属性,具体看官网的说明

    Mybatis是支持修改属性的,通过property子标签修改
    Mybatis - 核心配置文件Mybatis-config.xml标签详解(下)_第6张图片

    可以看出,环境标签是这样的:

    <environments default="development">
    
        <environment id="development">
        
            <transactionManager type="JDBC">
            </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>
    

    environments - 》 environment -》 transactionManager 和 dataSource

    这是定义Mybatis与数据库的连接,可以选择环境、选择事务管理器、事务处理器、数据源、数据库连接池

    不是大佬就别想着自定义了,关于数据库、‘池’技术,太复杂了


    数据库厂商标识(databaseIdProvider)

    MyBatis 可以根据不同的数据库厂商执行不同的语句

    因为,不同的数据库的语句会有一些不同,根据需要可以通过这个配置来设置多种数据库

    多厂商的支持是基于映射语句中的 databaseId 属性

    在核心配置文件中加上(这里的name是别名,真正的名字是很长一串的):

    <databaseIdProvider type="DB_VENDOR">
        <property name="MySQL" value="mysql" />
        <property name="Oracle" value="oracle" />
    </databaseIdProvider>
    

    在mapper.xml就可以根据需要选择不同的数据库(mybatis会识别databaseId)

    <select id="getUser" resultType="User" databaseId="mysql">
            SELECT * FROM user
    </select>
    

    具体的操作有点复杂,有空在深入

    可以看MyBatis之databaseIdProvider多数据库支持


    映射器(mappers)

    在前面的使用中已经用过了

    Mybatis - 核心配置文件Mybatis-config.xml标签详解(下)_第7张图片

    具体的mapper.xml必须注册到mybatis,mybatis才能处理

    有3种注册方法:

    类路径的资源引用mapper resource

    也就是上面的使用

    映射器使用类名扫描

    如果使用使用映射器接口实现类的完全限定类名

    
            class="com.learn.dao.UserMapper"/>
    </mappers>
    

    就需要接口和xml文件放在一起且名字相同
    在这里插入图片描述

    才能使用
    Mybatis - 核心配置文件Mybatis-config.xml标签详解(下)_第8张图片

    如果放在不同的位置

    Mybatis - 核心配置文件Mybatis-config.xml标签详解(下)_第9张图片

    就无法设置:
    如果是class设置的是Mapper包

     Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.lang.ClassNotFoundException: Cannot find class: com.learn.dao.Mapper.UserMapper
    

    Mybatis - 核心配置文件Mybatis-config.xml标签详解(下)_第10张图片

    如果找的是接口:

    org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.learn.dao.UserMapper.getUserById
    

    Mybatis - 核心配置文件Mybatis-config.xml标签详解(下)_第11张图片

    映射器使用包扫描

    "com.learn.dao"/>
    

    Mybatis - 核心配置文件Mybatis-config.xml标签详解(下)_第12张图片

    可以把dao下的xml配置都扫描到

    Mybatis - 核心配置文件Mybatis-config.xml标签详解(下)_第13张图片

    但是,接口和xml配置名要相同,且必须在同一包下,和类名扫描一样

    Mybatis - 核心配置文件Mybatis-config.xml标签详解(下)_第14张图片

    一改名字,就会报错

    org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.learn.dao.UserMapper.getUserById
    

    Mybatis - 核心配置文件Mybatis-config.xml标签详解(下)_第15张图片

    看喜好选择哪个都可以,如果想偷懒用包扫描,简单,如果想要把mapper.xml单独拿开可以选择资源引用


    总结

    所有的核心标签大概了解了一遍,很多需要实际查找并且关联到的很多Mybatis的知识
    如:sqlsession等工作流程、日志工厂、不同数据库等后续会深入了解

    其实,所有的Mybatis标签都对应着一系列的Java类,是通过解析XML文件配置,读取到Java类中的,所以,如果想深入,可以看看对应标签的类的源码

    学海无涯苦作舟

    都看到这了,点个赞呗(^_−)☆

    你可能感兴趣的:(Mybatis)