Mybatis——初始化过程,基本原理,配置文件详解

目录

 

1、mybatis 基本原理

2、Mybatis 配置文件

2.1、properties 配置

2.2、settings 配置

2.3、typeAliases 别名配置

2.4、typeHandlers 类型处理器配置

2.5、objectFactory 对象工厂

2.6、插件

2.7、environments 数据源配置

2.8、databaseIdProvider 数据库厂商标识

2.9、mappers 配置映射文件


1、mybatis 基本原理

在项目里需要配置 Mybatis.xml 配置文件,里面包含了各种相关的配置信息(比如,数据库url,连接用户名、密码、连接池等等),

1、读取配置文件

InputStream input = Resources.getResourceAsStream("mybatis.xml");  将文件以输入字节流的形式打开,可以通过 input 去以字节流的形式获取文件里的信息。

2、获取sqlsession工厂

SqlSessionFactoryBuilder 类的作用就是用于获取指定的 mybatis 配置信息,然后创建 SqlSessionFactory,因此,SqlSessionFactoryBuilder 的作用仅在于此,所以声明周期短,创建完了,基本就不用了。

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);

SqlSessionFactory 是接口,真正的实现类是 DefaultSqlSessionFactory 类,DefaultSqlSessionFactory 的作用就是用于创建 SqlSession ,DefaultSqlSessionFactory 内部包含了一个 Configuration 对象(final 修饰的),这个对象就是Mybatis.xml 里面的配置信息。 创建出来的 SqlSession 对象都包含了这个 Configuration 对象的引用,所以 SqlSession 对象才知道连接哪个数据库,怎么连接等等。

Configuration 类中还包含了所有 mapper.xml 的信息,将 xml 中的信息封装成代理对象,Configuration 就用一个Map 结构存储(这叫 Mapper 注册),key 是指 mapper 接口, value 就是这个代理对象了,所以虽然我们 mapper 是接口,在调用接口的某个方法时,实际上是在Map 结构中找到对应的代理对象,调用的是代理对象的具体 sql 执行。

SqlSessionFactory 生命周期是整个Mybatis的生命周期,如果项目里有多个数据库,每个数据库应该有一个SqlSessionFactory 对象,且 SqlSessionFactory 是单例模式。   数据库的最大连接数有限,如果一个数据库对应多个 SqlSessionFactory 对象的话,会导致连接不够用(因为这些SqlSessionFactory 创建很多 SqlSession ),而且也不好管理。

3、SqlSession

SqlSession 是一个接口,真正的实现类默认是  DefaultSqlSession 类。 相当于对数据库的一次连接。

DefaultSqlSession 内部包含了 各种 select 方法,update 方法,insert 方法,delete 方法,这些方法都是要指定 sql 字符串的,基本上和 jdbc 差不多。 但是有个 getMapper(Class<T> type)  ,参数是指定 Mapper 接口的,比如 

RoleMapper rolemapper = sqlSession.getMapper(RoleMapper.class);

明明是个接口,怎么就有实例化对象了呢?其实是用 java 反射机制实现的, mapper.xml 文件中定义了各种 sql 语句,而且和 mapper 接口相对应,所以可以根据 mapper.class 的类型,根据 xml 中的 sql 语句动态地生成一个实例化对象,rolemapper 调用相应的方法,就会将相应的 sql 语句发送给数据库了。

SqlSession 的生命周期很短,如果在一个用户线程中,如果将几个sql语句定义为一个事务,那么只需要创建一个 SqlSession 对象,然后利用这个对象将几个sql语句都发送给数据库,然后接收返回结果,用完了之后 SqlSession 会被销毁。但是如果没有定义事务,则调用了多少次 mapper 接口,就创建多少个 SqlSession 对象,用完之后也要销毁。

4、Configuration 类

Configuration 类是非常重要的,基本上 mybatis 配置的各种信息都存储在这个类里面,

public class Configuration {
    protected Environment environment; //数据库环境信息
    protected boolean safeRowBoundsEnabled; 
    protected boolean safeResultHandlerEnabled;
    protected boolean mapUnderscoreToCamelCase;
    protected boolean aggressiveLazyLoading;
    protected boolean multipleResultSetsEnabled;
    protected boolean useGeneratedKeys;
    protected boolean useColumnLabel;
    protected boolean cacheEnabled;
    protected boolean callSettersOnNulls;
    protected String logPrefix;
    protected Class<? extends Log> logImpl;
    protected LocalCacheScope localCacheScope;
    protected JdbcType jdbcTypeForNull;
    protected Set<String> lazyLoadTriggerMethods;
    protected Integer defaultStatementTimeout;
    protected ExecutorType defaultExecutorType;
    protected AutoMappingBehavior autoMappingBehavior;
    protected Properties variables; // 存储了properties 变量信息
    protected ObjectFactory objectFactory;
    protected ObjectWrapperFactory objectWrapperFactory;
    protected MapperRegistry mapperRegistry; // 存储了所有的<mapper接口,mapper代理对象>
    protected boolean lazyLoadingEnabled;
    protected ProxyFactory proxyFactory; // 用于生产mapper接口的代理类
    protected String databaseId;
    protected Class<?> configurationFactory;
    protected final InterceptorChain interceptorChain;
    protected final TypeHandlerRegistry typeHandlerRegistry; // 类型处理器注册器,存储了所有类型处理器,<类型,类型处理器>,类型可以是java类型和JDBC 类型,必须配对,同一个类型处理器,必须对应一个java类型和一个jdbc类型
    protected final TypeAliasRegistry typeAliasRegistry; // 别名注册器,存储了所有的别名,<别名,真实类型>
    protected final LanguageDriverRegistry languageRegistry;
    protected final Map<String, MappedStatement> mappedStatements;
    protected final Map<String, Cache> caches;
    protected final Map<String, ResultMap> resultMaps;
    protected final Map<String, ParameterMap> parameterMaps;
    protected final Map<String, KeyGenerator> keyGenerators;
    protected final Set<String> loadedResources;
    protected final Map<String, XNode> sqlFragments;
    protected final Collection<XMLStatementBuilder> incompleteStatements;
    protected final Collection<CacheRefResolver> incompleteCacheRefs;
    protected final Collection<ResultMapResolver> incompleteResultMaps;
    protected final Collection<MethodResolver> incompleteMethods;
    protected final Map<String, String> cacheRefMap;
}

2、Mybatis 配置文件

基本标签结构如下:

2.1、properties 配置

2.1.1、直接在Mybatis-config.xml 文件中配置变量。

用于声明变量,在配置文件里声明了之后,其它标签就可以直接使用了($符号引用即可)。举个例子。

<properties>
    <property name = "url" value = "jdbc:mysql://localhost:3306/database1"/>
</properties>
<environments>
    <environment id = "database1">
        <transactionManager type = "JDBC"/>
        <dataSource type = "POOLED">
            <property name = "url" value = "${url}"/>
        </dataSource>
    </environment>
</environments>

 2.1.2、在 properties 文件中配置变量。

创建一个文件 mybatis.properties ,在文件中声明变量。举个例子。

文件内容为  url=jdbc:mysql://localhost:3306/database1

然后再 mybatis-config.xml 配置文件中引入,<properties resource = "mybatis.properties"/> 即可,然后再mybatis-config.xml 中的其它标签里就可以直接用 $ 符号引用变量了。 

2.2、settings 配置

cacheEnabled全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。true | falsetrue
lazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。true | falsefalse
aggressiveLazyLoading开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。true | falsefalse (在 3.4.1 及之前的版本中默认为 true)
multipleResultSetsEnabled是否允许单个语句返回多结果集(需要数据库驱动支持)。true | falsetrue
useColumnLabel使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。true | falsetrue
useGeneratedKeys允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。true | falseFalse
autoMappingBehavior指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。NONE, PARTIAL, FULLPARTIAL
autoMappingUnknownColumnBehavior指定发现自动映射目标未知列(或未知属性类型)的行为。
  • NONE: 不做任何反应
  • WARNING: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN)
  • FAILING: 映射失败 (抛出 SqlSessionException)
NONE, WARNING, FAILINGNONE
defaultExecutorType配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。SIMPLE REUSE BATCHSIMPLE
defaultStatementTimeout设置超时时间,它决定数据库驱动等待数据库响应的秒数。任意正整数未设置 (null)
defaultFetchSize为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。任意正整数未设置 (null)
defaultResultSetType指定语句默认的滚动策略。(新增于 3.5.2)FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置)未设置 (null)
safeRowBoundsEnabled是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。true | falseFalse
safeResultHandlerEnabled是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。true | falseTrue
mapUnderscoreToCamelCase是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。true | falseFalse
localCacheScopeMyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。SESSION | STATEMENTSESSION
jdbcTypeForNull当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。OTHER
lazyLoadTriggerMethods指定对象的哪些方法触发一次延迟加载。用逗号分隔的方法列表。equals,clone,hashCode,toString
defaultScriptingLanguage指定动态 SQL 生成使用的默认脚本语言。一个类型别名或全限定类名。org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandler指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5)一个类型别名或全限定类名。org.apache.ibatis.type.EnumTypeHandler
callSettersOnNulls指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。true | falsefalse
returnInstanceForEmptyRow当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2)true | falsefalse
logPrefix指定 MyBatis 增加到日志名称的前缀。任何字符串未设置
logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING未设置
proxyFactory指定 Mybatis 创建可延迟加载对象所用到的代理工具。CGLIB | JAVASSISTJAVASSIST (MyBatis 3.3 以上)
vfsImpl指定 VFS 的实现自定义 VFS 的实现的类全限定名,以逗号分隔。未设置
useActualParamName允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1)true | falsetrue
configurationFactory指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3)一个类型别名或完全限定类名。未设置
shrinkWhitespacesInSqlRemoves extra whitespace characters from the SQL. Note that this also affects literal strings in SQL. (Since 3.5.5)true | falsefalse

2.3、typeAliases 别名配置

之所以有别名这个机制,是因为有些名字或者类需要很长的字符串才能表达清楚,所以非常麻烦,于是有了别名机制,用简单易懂的字符串成为长的且复杂的名字。

别名有2种,一种是Mybatis自带的别名(本身自带的),另一种是自定义的。

Configuration 类中有个 TypeAliasRegistry 对象,这个对象专门用于注册别名,如果使用java代码的方式注册别名的话,就使用 typeAliasRegistry.registerAlias("别名", 真实名字) 语句就可以完成别名注册。

但是一般推荐使用xml配置的方式注册别名,1、单独的<别名,真名> 一对一地配置,2,扫描某个路径,为路径下的所有类默认生成别名,生成的别名就是类名首字母变为小写。下面分别是两种方式:

<typeAliases>
    <typeAlias alias = "user" type = "com.example.domain.User"/>
</typeAliases>
<typeAliases>
    <package name = "com.example.domain"/>
</typeAliases>

扫描的方式来生成别名,有时候容易出现别名重复,可以在某个类名前面用 @Alias("自定义的名字") 来为这个类定一个名字,避免重复。

2.4、typeHandlers 类型处理器配置

我们的后台程序是 java 的,java 的数据类型和 MySQL 数据库的类型不一样,这就需要有一个机制来自动地做类型转换,这个机制就是 typeHandler。

Mybatis 本身自带有很多类型处理器,基本上可以满足平时开发的需要,但是就怕遇到有些特殊场景,比如枚举类型,可能Mybatis 的枚举转换规则不是我们想要的,所以我们还需要自定义一个类型处理器,并注册进 Mybatis 里面,为我们的特殊类型转换做工作。

如何自定义类型处理器呢? 1、自定义的类型处理器要实现 TypeHandler<T> 接口,T 是泛型,对应java类型,2、将自定义的类型处理器注册到 Mybatis 中,怎么注册呢,其实就是在 Mybatis-config.xml 文件中配置好即可,3、按理说,到这个步骤就已经可以使用类型处理器了,因为 Mybatis 会因为 javaType 和 jdbcType 自动匹配对应的处理器,但是我们如果要100%确定使用某个类型处理器,最好在 mapper.xml 中显示指定类型处理器。

如果针对某种类型,Mybatis 自身有一个类型处理器,然后我们再自定义一个类型处理器,Mybatis 会用哪个呢? 用我们自定义的,因为 TypeHandlerRegistry 内部是用 Map 存储<类型, 类型处理器>的,所以后注册的类型处理器会覆盖掉之前的类型处理器。

举个例子,假设数据库的类型为 XXX,java 类型为 YYY。 类型处理器只是个空壳子,什么也没有实现,这里只说明一下怎么使用。

public class YYYTypeHandler implements TypeHandler<YYY> {
    @Override
    public void setParameter(PreparedStatement preparedStatement, int i, YYY yyy, JdbcType jdbcType) throws SQLException {
        
    }

    @Override
    public YYY getResult(ResultSet resultSet, String s) throws SQLException {
        return null;
    }

    @Override
    public YYY getResult(ResultSet resultSet, int i) throws SQLException {
        return null;
    }

    @Override
    public YYY getResult(CallableStatement callableStatement, int i) throws SQLException {
        return null;
    }
}
<typeHandlers>
    <typeHandler javaType = "YYY" jdbcType = "XXX" handler = "com.example.handler.YYYTypeHandler"/>
</typeHandlers>

2.5、objectFactory 对象工厂

准确的说,是数据库返回的结果集的实例对象工厂,不管是查询数据库,还是插入数据库,只要是有返回结果的,都需要有一个 java 对象存储它,但是返回的数据类型多种多样,你怎么知道创建一个什么样类型的对象去存储返回结果呢?这种场景就是该 java 反射机制出场了,因此,objectFactory 的底层原理就是java反射机制,当然,ObjectFactory 是一个接口,默认实现类是 DefaultObjectFactory。一般来讲,我们不需要自定义对象工厂了,默认用 Mybatis 的就可以了。

如果某一天碰到了一个场景,就需要自定义对象工厂,那怎么办呢?

1、自定义一个工厂类,实现 ObjectFactory 接口,或者继承 DefaultObjectFactory 类。

各个方法的实现可以参考 DefaultObjectFactory 类的。 第一个 create 方法是使用默认的构造方法去创建实例化对象,第二个 create 方法是可以设置构造方法的参数,使用有参构造方法去创建实例化对象, isCollection 方法是判断要创建的类型是不是集合类型, setProperties 方法是将 <objectFactory> 标签内的所有 property 内容获取到,即此方法的参数,然后如何处理这些 property 就是我们自己的事了。 Mybatis 并没有实现 setProperties 方法,因此,若要想进行一些额外的扩展,就需要自定义对象工厂,重新实现 setProperties 方法。

public class MyObjectFactory implements ObjectFactory {

    @Override
    public void setProperties(Properties properties) {
        
    }

    @Override
    public <T> T create(Class<T> aClass) {
        return null;
    }

    @Override
    public <T> T create(Class<T> aClass, List<Class<?>> list, List<Object> list1) {
        return null;
    }

    @Override
    public <T> boolean isCollection(Class<T> aClass) {
        return false;
    }
}

2、在 mybatis-config.xml 配置文件中配置好我们的自定义对象工厂

<objectFactory type = "com.example.objectFactory.MyObjectFactory">
    这里面可以定义一些 property
</objectFactory>

2.6、插件

 要想把插件弄透彻点,就需要再深入了解 mybatis 的底层原理。

请阅读博客:

2.7、environments 数据源配置

environments 是配置数据库信息的地方,可以配置多个数据库的信息,但是只能使用其中的一个(除非自己实现了多数据库的代码),default 属性就指定了使用哪一个数据库,default 的值就是下面 environment 标签的 id。

下面只是给了一个简单的例子,每个 environment 可以配置 2 个属性,分别是 transactionManager 和 dataSource,其中 dataSource 还可以有很多属性可以配置的。

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

 transactionManager 是事务管理器,主要作用就是可以提交事务,回滚事务,关闭事务,默认是使用 JDBC 方式,还有另一个方式是 MANAGED,它是将事务交给容器处理。

dataSource 就是配置数据库信息的地方了,type 用于指定数据源的类型,有 3 种类型:

1、POOLED表示这是一个带有连接池的数据源,具体用什么连接池,可以自己定,也可以使用spring提供的默认连接池

2、UNPOOLED表示这是一个没有连接池的数据源,每次都是创建连接,执行完sql,就关闭连接

3、JNDI表示这是使用容器里的数据源,是Tomcat容器中的机制,用于管理数据源,所以mybatis只需要拿着数据源的id就可以了,因为JNDI能知道你要访问的是哪个数据源,因此跟连接池也没有关系,只不过tomcat有默认的数据库连接池,若要更改连接池,可以研究一下。

另外,数据库可以配置的信息非常多,什么连接池相关的配置就一大堆,什么数据库连接超时了,异常了,等等。

2.8、databaseIdProvider 数据库厂商标识

这是针对不同类型的数据库,比如我们的项目在开发之前,已经预料到了项目要能够兼容 MySQL 和 Oracle ,即项目直接与Oracle 相连行,也 MySQL 相连也是可以的,一个是 MySQL,另一个是 Oracle,sql 语法不一样,需要用到的数据库驱动程序也不一样,因此,为了解决这个问题,就有了这个数据库厂商标识。

举个例子,需要用到 MySQL 和 Oracle 数据库,下面就配置上,“Oracle” 和 “MySQL” 是 Mybatis 本身自带的别名,因此 Mybatis 知道这两个代表什么数据库,“oracle” 和 “mysql” 又是这两个的数据库类型的别名,因此我们在指定数据库类型时,只需指定“oracle” 或者 “mysql” 即可。

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

然后在 mapper.xml 映射文件中写sql的时候,只需要指定 sql 标签的 databaseId 属性为目标数据库类型即可,也可以不指定,不指定的话,就会使用默认的数据库,什么是默认的数据库类型呢?environments 标签已经配置了。

Mybatis 在执行某个 sql 的时候,会通过调用 dao 层接口来实现,按照 dao 层的 mapper 接口,又找到对应的 mapper.xml 映射文件,根据 mapper 接口里被调用的方法名,又可以确定执行映射文件里哪个 sql 了,此时,很有可能同一个sql 的 id 会有多条 sql,只是每条 sql 的 databaseId 和 sql 语法不同而已,那么纠结执行哪些 sql 呢? 如果有匹配到 databaseId 的 sql,就只执行它,如果没有匹配到,即只有未指定 databaseId 的 sql,那就只执行它,即按照默认的数据库类型去执行,如果什么都没有匹配到,就抛出异常。

databaseIdProvider 只是解决了应用于不同类型数据库的兼容问题,不是说 Mybatis 可以凭借这个配置同时与多个数据库交互,它仍然是和一个数据库在交互。

2.9、mappers 配置映射文件

配置我们的各种 mappers.xml,配置方式有 4 种。

1、<mappers><mapper resource = "相对路径"/></mappers> 

2、<mappers><mapper url = "绝对路径"/></mappers> 

3、<mappers><package name = "包的相对路径"/></mappers>

4、<mappers><mapper class = "mapper接口的相对路径"/></mappers>   // 这有个条件,就是此 mapper 接口和对应的 mapper.xml 在同一个文件夹下面,且接口名和映射文件名一样。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值