1 IDEA 设置核心配置文件模板
20_在 idea 中设置核心配置文件的模板_哔哩哔哩_bilibili
2 全局配置文件
前面我们看到的 Mybatis 全局文件并没有全部列举出来,所以这一章我们来详细的介绍一遍,Mybatis 的全局配置文件并不是很复杂,它的所有元素和代码如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration> <!--配置-->
<properties/> <!--属性-->
<settings/> <!--全局配置参数-->
<typeAliases/> <!--类型别名-->
<typeHandlers/> <!--类型处理器-->
<objectFactory/><!--对象工厂-->
<plugins/><!--创建-->
<environments default=""><!--环境配置-->
<environment id=""><!--环境变量-->
<transactionManager type=""/><!--事务管理器-->
<dataSource type=""/><!--数据源-->
</environment>
</environments>
<databaseIdProvider type=""/><!--数据库厂商标识-->
<mappers/><!--映射器-->
</configuration>注意:Mybatis 的配置文件的顺序是严格按照从上至下的顺序声明,不颠倒顺序,如果颠倒了它们的顺序,那么 Mybatis 在启动阶段就会产生异常,导致程序无法运行。
3 properties 属性
properties 的作用是引用 java 属性文件中的配置信息,比如:加载连接数据库的各种属性的配置文件。
Mybatis 提供了三种方式使用 properties 属性:
- property 子元素(不推荐):就是在 properties 属性中增加子属性 property,从而设置一些配置的 key-value。
- properties 文件:就是直接使用 properties 引入外部配置文件,相当于将子属性抽取成一个独立的外部文件引入,例如 db.properties。
- 程序代码传递参数:就是通过代码的方式设置该配置相关的信息,如数据库配置文件中的用户名和密码一般是密文,但是连接数据库时需要对配置进行解密,此时就只能通过程序代码的方式配置了。
3.1 property 子元素(不推荐)
以上一章的例子为基础,使用 property 子元素将数据库的连接配置信息进行改写,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties>
<property name="database.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="database.url" value="jdbc:mysql://localhost:3306/user?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8"/>
<property name="database.username" value="root"/>
<property name="database.password" value="root"/>
</properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!--配置连接数据库的4个基本信息-->
<property name="driver" value="${database.driver}"/>
<property name="url" value="${database.url}"/>
<property name="username" value="${database.username}"/>
<property name="password" value="${database.password}"/>
</dataSource>
</environment>
</environments>
</configuration>缺点:配置项较多时,配置文件会很庞大。
3.2 properties 文件
使用 properties 文件的方式在我们的开发中是比较常用,主要的这种方式简单,方便日后的维护和修改。首先将上述配置中的所有 property 属性提取到一个叫做 database.properties 的配置文件中,如下代码所示:
#数据库连接配置
database.driver=com.mysql.cj.jdbc.Driver
database.url=jdbc:mysql://localhost:3306/user?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
database.username=root
database.password=root然后在 Mybatis 配置文件中使用<properties>元素的 resource 属性来引入 properties 文件。
<properties resource="database.properties" />使用:${database.username} 的方式引入 properties 文件的属性参数。
缺点:用户名和密码直接暴露出来;否则就在配置文件中保存加密后的内容,然后搭配方案三。
3.3 程序代码传递参数
举个 MyBatis 中获取 SqlSessionFactory 的例子,代码如下:
public static SqlSessionFactory getSqlSessionFactoryByXml() {
synchronized (Lock) {
if (null != sqlSessionFactory) {
return sqlSessionFactory;
}
String resource = "mybatis-config.xml";
InputStream inputStream;
InputStream is = null;
try {
// 加载数据库配置文件
is = Resources.getResourceAsStream("database.properties");
Properties properties = new Properties();
properties.load(is);
// 获取加密信息
String username= properties.getProperty("database.username");
String password= properties.getProperty("database.password");
// 解密用户名和密码,并重置属性
properties.setProperty("database.username", CyperTool.decodeByBase64(username));
properties.setProperty("database.password", CyperTool.decodeByBase64(password));
// 读取mybatis配置文件
inputStream = Resources.getResourceAsStream(resource);
// 通过SqlSessionFactoryBuilder类的builder方法进行构建,并使用程序传递的方式覆盖原有属性
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, properties);
} catch (IOException e) {
e.printStackTrace();
return null;
}
return sqlSessionFactory;
}
}我们为了保证数据的准确性,加了 synchronized 锁。首先使用 Resources 对象读取了 database.properties 配置文件,然后获取了它原来配置的用户和密码,进行解密操作,最后使用 SqlSessionFactoryBuilder 的 build 方法,传递多个 properties 参数来完成。这将覆盖之前加密的配置,这样就可以连接数据库了,同时也能满足因为人员对数据库的用户名和密码的安全要求。
4 settings 属性
settings 是 MyBatis 中最复杂的配置,它能深刻影响 MyBatis 底层的运行,但是大部分情况下使用默认值便可以运行,所以在大部分情况下不需要大量配置,只需要修改一些常用的规则即可。常用规则有自动映射、驼峰命名映射、级联规则、是否启动缓存、执行器类型等。
所有配置可参考 MyBatis 官方文档:https://mybatis.org/mybatis-3/zh/configuration.html#settings。
常见的有关于缓存的 CacheEnabled,关于级联的 lazyLoadingEnabled 和 aggressiveLazyLoading,关于自动映射的 autoMappingBehavior 和 mapUnderscoreToCamelCase,关于执行器类型的 defaultExecutorType 等。本文列出重要的几个配置项及意义,并挑几个常用配置加以说明:
<settings>
<!--缓存配置的全局开关:如果这里设置成false,那么即便在映射器中配置开启也无济于事 -->
<setting name="cacheEnabled" value="true" />
<!--延时加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。默认值false -->
<setting name="lazyLoadingEnabled" value="false" />
<!-- 是否允许单一语句返回多结果集(需要兼容驱动) -->
<setting name="multipleResultSetsEnabled" value="true" />
<!-- 使用列标签代替列名,需要兼容驱动。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。 -->
<setting name="useColumnLabel" value="true" />
<!-- 允许JDBC自动生成主键,需要驱动兼容。如果设置为true,则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍能正常工作 -->
<setting name="useGeneratedKeys" value="false" />
<!-- 指定 MyBatis 该如何自动映射列到字段或属性:NONE 表示取消自动映射;PARTIAL 表示只会自动映射,没有定义嵌套结果集和映射结果集;
FULL 会自动映射任意复杂的结果集,无论是否嵌套 -->
<setting name="autoMappingBehavior" value="PARTIAL" />
<!-- 指定发现自动映射目标未知列(或未知属性类型)的行为。NONE: 不做任何反应WARNING: 输出警告日志FAILING: 映射失败 (抛出 SqlSessionException) -->
<setting name="autoMappingUnknownColumnBehavior" value="WARNING" />
<!-- 配置默认的执行器:SIMPLE是普通的执行器;REUSE会重用预处理语句;BATCH会重用语句并执行批量更新 -->
<setting name="defaultExecutorType" value="SIMPLE" />
<!--设置超时时间:它决定驱动等待数据库响应的秒数,任何正整数-->
<setting name="defaultStatementTimeout" value="25"/>
<!--设置数据库驱动程序默认返回的条数限制,此参数可以重新设置,任何正整数 -->
<setting name="defaultFetchSize" value="100" />
<!-- 允许在嵌套语句中使用分页(RowBounds) -->
<setting name="safeRowBoundsEnabled" value="false" />
<!-- 是否开启自动驼峰命名规则,即从a_example到aExample的映射 -->
<setting name="mapUnderscoreToCamelCase" value="true" />
<!-- 本地缓存机制,防止循环引用和加速重复嵌套循环。
默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。
若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。-->
<setting name="localCacheScope" value="SESSION" />
<!-- 当没有为参数提供特定JDBC类型时,为空值指定JDBC类型。某些驱动需要指定列的JDBC类型,多数情况直接用一般类型即可,如NULL/VARCHAR/OTHER -->
<setting name="jdbcTypeForNull" value="OTHER" />
<!-- 指定触发延迟加载的方法,如equals/clone/hashCode/toString -->
<!-- <setting name="lazyLoadTriggerMethods"
value="equals,clone,hashCode,toString"/> -->
<setting name="lazyLoadTriggerMethods" value="equals" />
</settings>5 typeAliases 属性
5.1 系统默认别名
| 别名 | 映射的类型 | 别名 | 映射的类型 |
|---|---|---|---|
| _byte | byte | byte | Byte |
| _long | long | long | Long |
| _short | short | short | Short |
| _int | int | int | Integer |
| _integer | int | integer | Integer |
| _double | double | double | Double |
| _float | float | float | Float |
| _boolean | boolean | boolean | Boolean |
| string | String | ||
| date | Date | ||
| decimal | BigDecimal | bigdecimal | BigDecimal |
| map | Map/HashMap |
5.2 自定义别名之 typeAlias 标签
自定义别名,主要使用在 <select resultType=”别名”>
typeAlias 可以指定一个类型一个自定义别名
Type: 自定义类型的全限定名称
Alias: 别名(短小,容易记忆的)
<typeAliases>
<typeAlias type="com.bjpowernode.domain.Student" alias="stu" />
<typeAlias type="com.bjpowernode.vo.ViewStudent" alias="vstu" />
</typeAliases>缺点:如果需要定义别名的类比较多,这样太麻烦了
5.3 自定义别名之 package 标签
<package> name 是包名,这个包中的所有类,类名就是别名(类名不区分大小写), 将类名的第一个字母变为小写作为别名。
<typeAliases>
<package name="com.bjpowernode.domain"/>
<package name="com.bjpowernode.vo"/>
</typeAliases>缺点:两个包下出现同名的类,扫描时出现异常(一般不会)。这个时候可以通过注解@Alians (“user1”) 来进行区分。
5.4 自定义别名之注解
package com.thr.pojo1;
import org.apache.ibatis.type.Alias;
@Alias("user1")
public class User {}6 TypeHandlers 属性
TypeHandlers 叫类型处理器。MyBatis 中有 JavaType 和 JDBCType 两种类型,类型处理器就是承担两种类型之间的转换。
MyBatis 中的 typeHandlers 存在系统定义的和自定义两种,MyBatis 会根据 javaType 和 jdbcType 来决定采用哪个 typeHandler 来处理这些转换规则,而且系统定义的能满足大部分需求,但是有些情况是不够用的,比如我们的特殊转换规则,枚举类型,这时我们就需要自定义的 typeHandlers 了。
6.1 系统定义的 typeHandler
Mybatis 内部定义了 39 个 typeHandler,官方文档查看: https://mybatis.org/mybatis-3/zh/configuration.html#typeHandlers ,也可以自己通过程序代码进行打印。
public static void getTypeHandlers() {
//SqlSession 代码省略......
TypeHandlerRegistry typeHandlerRegistry = sqlSession.getConfiguration().getTypeHandlerRegistry();
Collection<TypeHandler<?>> handlers = typeHandlerRegistry.getTypeHandlers();
System.out.println(handlers.size());
int i = 0;
for (TypeHandler<?> typeHandler : handlers) {
System.out.println(++i+"*****"+typeHandler.getClass().getName());
}
}在 Mybatis 中 typeHandler 都需要实现接口 org.apache.ibatis.type.TypeHandler,源码为:
public interface TypeHandler<T> {
void setParameter(PreparedStatement var1, int var2, T var3, JdbcType var4) throws SQLException;
T getResult(ResultSet var1, String var2) throws SQLException;
T getResult(ResultSet var1, int var2) throws SQLException;
T getResult(CallableStatement var1, int var2) throws SQLException;
}简单介绍一下内部定义了内容:
- 其中 T 表示泛型,专指 JavaType,比如我们需要 String 类型的参数,那么实现类就是可以写成 implement
TypeHandler<String>。 - SetParameter 方法,是使用 typeHandler 通过 PreparedStatement 对象进行设置 SQL 参数的时候使用的具体方法,其中 i 是请查收在 SQL 的下标,parameter 是参数,jdbcType 为数据库类型。
- 其中三个 getResult 的方法,它的作用是从 JDBC 结果集中获取数据进行转换,要么使用列名,要么使用下标来获取数据库的数据,其中最后一个方法是存储过程专用的方法。
BaseTypeHandler 实现类源代码(少量):
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
......
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
......
}
public T getResult(ResultSet rs, String columnName) throws SQLException {
......
}
public T getResult(ResultSet rs, int columnIndex) throws SQLException {
......
}
public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
......
}
public abstract void setNonNullParameter(PreparedStatement var1, int var2, T var3, JdbcType var4) throws SQLException;
public abstract T getNullableResult(ResultSet var1, String var2) throws SQLException;
public abstract T getNullableResult(ResultSet var1, int var2) throws SQLException;
public abstract T getNullableResult(CallableStatement var1, int var2) throws SQLException;
}简单分析一下:
- SetParameter 方法,当参数 parameter 和 jdbcType 同时为空时,Mybatis 将抛出异常。如果能目前 jdbcType,则会继续空设置;如果参数不为空,那么他将曹勇 setNonNullParameter 方法设置参数。
- GetResult 方法,非空结果集是通过 getNullableResult 方法获取的。如果判断为空,则返回 null。
- GetNullableResult 方法用于存储过程。
StringTypeHandler 实现类源代码:
public class StringTypeHandler extends BaseTypeHandler<String> {
public StringTypeHandler() {
}
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter);
}
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getString(columnName);
}
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getString(columnIndex);
}
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getString(columnIndex);
}
}从上述代码可以看出它继承了 BaseTypeHandler<String> 类,并且实现了 BaseTypeHandler 的 4 个抽象方法,方法如下:
- SetNonNullParameter:这个方法是用来将 javaType 转换成 jdbcTpe。
- GetNullableResult:这个方法用来将从结果集根据列名称获取到的数据的 jdbcType 转换成 javaType。
- GetNullableResult:这个方法用来将从结果集根据列索引获取到的数据的 jdbcType 转换成 javaType。
- GetNullableResult:这个方法用在存储过程中。
这里 Mybatis 把 JavaType 和 jdbcType 进行互换,那么他们是怎么进行注册的呢?在 Mybatis 中采用 TypeHandlerRegistry 类对象的 register 方法来进行则。
public TypeHandlerRegistry(Configuration configuration) {
......
this.register((Class)Boolean.class, (TypeHandler)(new BooleanTypeHandler()));
this.register((Class)Boolean.TYPE, (TypeHandler)(new BooleanTypeHandler()));
this.register((JdbcType)JdbcType.BOOLEAN, (TypeHandler)(new BooleanTypeHandler()));
this.register((JdbcType)JdbcType.BIT, (TypeHandler)(new BooleanTypeHandler()));
......
}这样就是实现了用代码的形式注册 typeHandler。但是注意,自定义的 typeHandler 一般不会使用代码进行注册,而是通过配置或者扫描。
6.2 自定义 typeHandler
要实现 typeHandler 就需要去实现接口 typeHandler 或者实现 baseTypeHandler。下面创建一个 MyTypeHandler,用来完成 javaType 中的 String 类型与 jdbcType 中的类型之间的转化。
public class MyTypeHandler implements TypeHandler<String> {
Logger log = Logger.getLogger(MyTypeHandler.class);
@Override
public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
log.info("设置 string 参数:"+parameter);
ps.setString(i,parameter);
}
@Override
public String getResult(ResultSet rs, String columnName) throws SQLException {
String result = rs.getString(columnName);
log.info("读取 string 参数 1:"+result);
return result;
}
@Override
public String getResult(ResultSet rs, int columnIndex) throws SQLException {
String result = rs.getString(columnIndex);
log.info("读取 string 参数 2:"+result);
return result;
}
@Override
public String getResult(CallableStatement cs, int columnIndex) throws SQLException {
String result = cs.getString(columnIndex);
log.info("读取 string 参数 3:"+result);
return result;
}
}配置文件进行注册自定义的类型处理器:
<typeHandlers>
<typeHandler jdbcType="VARCHAR" javaType="string"
handler="com.typeHandler.MyTypeHandler"/>
</typeHandlers>当 JavaType 和 jdbcType 能与 MyTypeHandler 对应的时候,它就会启动 MyTypeHandler。
注意,要么指定了与自定义 typeHandler 一致的 jdbcType 和 JavaType,要么直接使用 typeHandler 指定的具体实现类。在一些因为数据库返回为空导致无法判定采用哪个 typeHandler 来处理,而又没有注册对应的 JavaType 的 typeHandler 是,Mybatis 无法找到使用哪个 typeHandler 来转换数据。
有时候类很多的时候,我们还可以采用包扫描的方式。
<typeHandlers>
<package name="com.typeHandler"/>
</typeHandlers>但是这样会无法指定 jdbcType 和 JavaType,不过我们可以通过注解来处理它们,我们把 MyTypeHandler 类修改一些即可。
@MappedTypes(String.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class MyTypeHandler implements TypeHandler<String> {
......
}7 ObjectFacotry 属性
ObjectFacotry 表示为对象工厂。对象工厂我们只需了解即可,因为到时候与 spring 整合后,都会由 spring 来管理。
我们在使用 MyBatis 执行查询语句的时候,通常都会有一个返回类型,这个是在 mapper 文件中给 sql 增加一个 resultType (或 resultMap) 属性进行控制。ResultType 和 resultMap 都能控制返回类型,只要定义了这个配置就能自动返回我想要的结果,于是我就很纳闷这个自动过程的实现原理,想必大多数人刚开始的时候应该也有和我一样的困惑和好奇,那么今天我就把自己的研究分享一下。在 JDBC 中查询的结果会保存在一个结果集中,其实 MyBatis 也是这个原理,只不过 MyBatis 在创建结果集的时候,会使用其定义的对象工厂 DefaultObjectFactory 来完成对应的工作。详细可以参考:链接
8 Plugins 属性
插件是 Mybatis 中最强大和灵活的组件,同时也是最复杂、最难使用的组件,并且它十分的危险,因为它将覆盖 Mybatis 底层对象的核心方法和属性。如果操作不当将产生非常严重的后果,甚至是摧毁 Mybatis 框架,所以我们在不了解 Mybatis 的底层结构的情况下,千万不要去碰这个插件属性。如果你想研究一下插件,那么前提是要清楚掌握 Mybatis 底层的结构和运行原理,否则将难以安全高效的使用它。而我们平时使用 Mybatis 的常规功能完全满足日常的开发,所以这里就不介绍了,有兴趣的可以自行去学习。
9 environments 属性
<environment> 标签中有两个可以配置的元素,<transactionManager> 事务管理器和 <dataSource> 数据源。
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!--配置连接数据库的4个基本信息-->
<property name="driver" value="${database.driver}"/>
<property name="url" value="${database.url}"/>
<property name="username" value="${database.username}"/>
<property name="password" value="${database.password}"/>
</dataSource>
</environment>
</environments>environments 环境配置汇总,default 值表示使用哪个 environment 配置。
environment 单一环境配置,id 值唯一标识 environment。
9.1 TransactionManager 标签
9.1.1 默认事务处理器
在 MyBatis 中,transactionManager 提供了两个实现类,它们都需要实现接口 Transaction。
public interface Transaction {
Connection getConnection() throws SQLException;
void commit() throws SQLException;
void rollback() throws SQLException;
void close() throws SQLException;
Integer getTimeout() throws SQLException;
}MyBatis 中为 Transaction 接口提供了两个实现类,分别是 JdbcTransaction 和 ManagedTransaction。如下图所示:


并且分别对应着 JdbcTransactionFactory 和 ManagedTransactionFactory 两个工厂,这两个工厂实现了 TransactionFactory 这个接口,当我们在配置文件中通过 transactionManager 的 type 属性配置事务管理器类型的时候,Mybatis 就会自动从对应的工厂获取实例。我们可以把事务管理器配置成为以下两种方式:
<transactionManager type="JDBC"/>
<transactionManager type="MANAGED"/>下面说一下这两者的区别:
-
JDBC:使用 JdbcTransactionFactory 工厂生成的 JdbcTransaction 对象实现,以 JDBC 的方式进行数据库的提交、回滚等操作。
-
MANAGED:使用 ManagedTransactionFactory 工厂生成的 ManagedTransaction 对象实现,它的提交和回滚不需要任何操作,而是把事务交给容器进行处理,默认情况下会关闭连接,如果不希望默认关闭,只要将其中的 closeConnection 属性设置为 false 即可。
<transactionManager type="MANAGED"> <property name="closeConnection" value="false"/> </transactionManager>
JDBC 的事务处理方式,修改数据后,必须执行 sqlSession.commit(); 进行提交,否则虽然插入成功但是数据库中还是看不到刚才插入的数据。
MANAGED 方式就不一样了,只需调用接口即可,无需手动提交。
9.1.2 自定义事务处理器
第一步:创建一个自定义事务工厂 MyTransactionFactory,需要实现 TransactionFactory 接口。
第二步:创建一个自定义事务类 MyTransaction,用来实现 Transaction 接口。
第三步:配置自定义事务管理器。
public class MyTransactionFactory implements TransactionFactory {
@Override
public void setProperties(Properties props) { }
@Override
public Transaction newTransaction(Connection connection) {
//后面我们会创建这个类,它自定义的事务类
return new MyTransaction(connection);
}
@Override
public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean b) {
return new MyTransaction(dataSource,level,b);
}
}
public class MyTransaction extends JdbcTransaction implements Transaction {
public MyTransaction(Connection connection){ super(connection);}
public MyTransaction(DataSource ds, TransactionIsolationLevel level,boolean desiredAutoCommit) { super(ds,level,desiredAutoCommit); }
@Override public Connection getConnection() throws SQLException { return super.getConnection();}
@Override public void commit() throws SQLException { super.commit(); }
@Override public void rollback() throws SQLException { super.rollback(); }
@Override public void close() throws SQLException { super.close(); }
@Override public Integer getTimeout() throws SQLException { return super.getTimeout(); }
}<transactionManager type="com.transaction.MyTransactionFactory"/>注意:这个地方配置的是自定义的工厂类,而不是事务管理类,因为 mybatis 是根据配置的工厂获取具体实例对象的。
9.2 DataSource 标签
dataSource 连接池配置,包含 UNPOOLED, POOLED, JNDI 三类。其中 UNPOOLED , POOLED 数据源实现了 javax.sq.DataSource 接口, JNDI 和前面两个实现方式不同,了解可以。
MyBatis 在初始化时,根据 <dataSource> 的 type 属性来创建相应类型的的数据源 DataSource。
UNPOOLEDMyBatis 会创建UnpooledDataSource实例。不使用连接池,在每次执行 sql 语句,先创建连接,执行 sql,在关闭连接。
还支持属性 defaultTransactionIsolationLevel :默认的事务隔离级别,如果要传递属性给驱动,则属性的前缀为 driver。
-
POOLEDMyBatis 会创建PooledDataSource实例。使用连接池,初始化时创建多个实例,使用时直接从连接池中获取。除了
UNPOOLED中提供的,还有下面几个等属性。- PoolMaximumActiveConnections:任意时间都会存在的连接数,默认值为 10
- PoolMaxmumIdleConnections:可以空闲存在的连接数
- PoolMaxmumCheckoutTime:在被强制返回之前,检查出存在空闲连接的等待时间。即如果有 20 个连接,只有一个空闲,在这个空闲连接被找到之前的等待时间就用这个属性配置。
- PoolTimeToWait:等待一个数据库连接成功所需的时间,如果超出这个时间则尝试重新连接。
-
JNDIMyBatis 会从 JNDI 服务上查找DataSource实例,然后返回使用。Java 命名和目录服务(windows 注册表)。JNDI 数据源 JNDI 的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。这种数据源只需配置两个属性:- Initial_context:用来在 InitialContext 中寻找上下文。可选,如果忽略,data_source 属性将会直接从 InitialContext 中寻找;
- Data_source:引用数据源实例位置上下文的路径。当提供 initial_context 配置时,data_source 会在其返回的上下文进行查找,否则直接从 InitialContext 中查找。
10 DatabaseIdProvider 属性(了解)
DatabaseIdProvider 元素主要是为了支持不同厂商的数据库,这个元素不常用。比如有的公司内部开发使用的数据库都是 MySQL,但是客户要求使用 Oracle,那麻烦了,因为 Mybatis 的移植性不如 Hibernate,但是 Mybatis 也不会那么蠢,在 Mybatis 中我们可以使用 databaseIdProvider 这个元素实现数据库兼容不同厂商,即配置多中数据库。
下面以 Oracle 和 MySQL 两种数据库来介绍它们,要配置的属性如下:
<databaseIdProvider type="DB_VENDOR">
<property name="Oracle" value="oracle"/>
<property name="MySQL" value="mysql"/>
<property name="DB2" value="d2"/>
</databaseIdProvider>DatabaseIdProvider 的 type 属性是必须的,不配置时会报错。上面这个属性值使用的是 VendorDatabaseIdProvider 类的别名。
Property 子元素是配置一个数据库,其中的 name 属性是数据库名称,value 是我们自定义的别名,通过别名我们可以在 SQL 语句中标识适用于哪种数据库运行。如果不知道数据库名称,我们可以通过以下代码获取 connection.getMetaData().getDatabaseProductName() 来获取,代码如下:
public static void getDbInfo() {
SqlSession sqlSession = null;
Connection connection = null;
try {
InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
sqlSession = sqlSessionFactory.openSession();
connection = sqlSession.getConnection();
String dbName = connection.getMetaData().getDatabaseProductName();
String dbVersion = connection.getMetaData().getDatabaseProductVersion();
System.out.println("数据库名称是:" + dbName + ";版本是:" + dbVersion);
} catch (SQLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}我的输出结果是:数据库名称是:MySQL;版本是:5.7.28-log
然后下面我们就可以在自己的 sql 语句中使用属性 databaseId 来标示数据库类型了。配置如下:
<select id="selectAllUser" resultType="com.thr.User" databaseId="oracle">
select * from t_user
</select>注意:在上面的 SQL 中,我配置的 databaseId 是 oracle,但是我的实际的数据库是 mysql,结果最后肯定会报 BindingException 异常(但是我这个代码不知道为什么报的另一种异常,就很奇怪,百度了好久无果)。如果我们把 databaseId=”oracle”换成 mysql 的,就能获取正确结果了。除上述方法之外,我们还可以不在 SQL 中配置 databaseId,这样 mybatis 会使用默认的配置,也是可以成功运行的。
过上面的实践知道了:使用多数据库 SQL 时需要配置 databaseIdProvider 属性。当 databaseId 属性被配置的时候,系统会优先获取和数据库配置一致的 SQL,否则取没有配置 databaseId 的 SQL,可以把它当默认值;如果还是取不到,就会抛出异常。
除了系统自定义的标识外,我们也可以自定义一个规则,需要实现 MyBatis 提供的 DatabaseIdProvider 接口,如下:
public class MyDatabaseIdProvider implements DatabaseIdProvider {
private static final String DATABASE_TYPE_MYSQL = "MySQL";
private static final String DATABASE_TYPE_ORACLE = "Oracle";
private static final String DATABASE_TYPE_DB2 = "DB2";
//log4j 的日志,别引入错了
private Logger log = Logger.getLogger(MyDatabaseIdProvider.class);
@Override
public void setProperties(Properties p) {
log.info(p);
}
@Override
public String getDatabaseId(DataSource dataSource) throws SQLException {
Connection conn = dataSource.getConnection();
String dbName = conn.getMetaData().getDatabaseProductName();
if(MyDatabaseIdProvider.DATABASE_TYPE_DB2.equals(dbName)){
return "db2";
} else if(MyDatabaseIdProvider.DATABASE_TYPE_MYSQL.equals(dbName)){
return "mysql";
} else if(MyDatabaseIdProvider.DATABASE_TYPE_ORACLE.equals(dbName)){
return "oracle";
} else {
return null;
}
}
}然后在 databaseIdProvider 中做如下配置:
<databaseIdProvider type="com.databaseidprovider.MyDatabaseIdProvider" />Property 属性可以不做配置了,其它都一样。
11 mappers
Mapper 属性是用来加载映射文件的,也就是加载我们配置的 SQL 映射文件。它有四种方式加载:
- 用文件路径引入
- 使用 URL 方式引入
- 用类注册引入
- 用包名引入(推荐)
11.1 用文件路径引入
<mappers>
<mapper resource="com/thr/mapper/UserMapper.xml" />
<mapper resource="com/thr/mapper/StudentMapper.xml" />
<mapper resource="com/thr/mapper/TeacherMapper.xml" />
</mappers>这种方式是相对路径,相对于项目目录下,所以得用 / 分开。
11.2 使用 URL 方式引入
<mappers>
<mapper url="D:/mappers/UserMapper.xml" />
<mapper url="D:/mappers/StudentMapper.xml" />
</mappers>这种方式是绝对路径,就是从我们的磁盘读取映射文件,一般不会使用这种方式。
11.3 用类注册引入
<mappers>
<mapper class="com.thr.mapper.UserMapper" />
<mapper class="com.thr.mapper.StudentMapper" />
<mapper class="com.thr.mapper.TeacherMapper" />
</mappers>这种方式使用 Mapper 接口的全限定名,不用管路径问题,让 Mybatis 自己通过全限定名去找映射文件。但是前提是 Mapper 接口的名称必须与映射文件的名称相同,并且要在同一个包名下,否则会找不到。比如:UserMapper.java (接口)— UserMapper.xml (映射文件)。关于 Mapper 接口对应的 Mapper 映射文件后面会详细介绍。
11.4 用包名引入(推荐)
<mappers>
<package name="com.thr.mapper"/>
</mappers>推荐使用这种方式,表示引入该包下的所有 mapper 接口,这里引入了 com. Thr. Mapper 包下的所有接口文件,然后让 Mybatis 自己通过全限定名去找映射文件。
注意:这种方式的要求同样是 Mapper 接口和 Mapper 的映射文件的名称要相同,并且要放在相同的包名下,否则会导致找不到。