Mybatis配置说明
约 8532 字大约 28 分钟
xml配置文件标签和Configuration类属性的对应关系
| 名称 | 标签 | Configuration类属性名 | Configuration类属性类型 | 备注 |
|---|---|---|---|---|
| 属性配置 | <properties> | variables | Properties | 自定义的k-v属性 |
| 环境配置 | <environment> | environment | Environment | 环境配置,如数据源 |
| 行为配置 | <settings> | 各配置项不同 | 一般是boolean,有例外 | 用于配置mybatis的行为 |
| 类型别名 | <typeAliases> | typeAliasRegistry | TypeAliasRegistry | 配置类型别名 |
| 类型处理器 | <typeHandlers> | typeHandlerRegistry | TypeHandlerRegistry | 配置类型转换器 |
| 映射配置 | <mappers> | mapperRegistry | MapperRegistry | 配置mapper映射类/映射文件的位置 |
| 插件配置 | <plugins> | interceptorChain | InterceptorChain | 配置mybatis插件 |
属性配置
属性配置可以设置程序或后续配置中将要用到的k-v型参数。属性配置的方式有四种:
- property元素标签配置(可以与后两种的其中一种混合使用)
- 外部资源配置
- 远程资源配置(注意,外部配置和远程配置只能存在一个)
- ConfigurationAPI配置
- 从Mybatisv3.4.2开始,可以通过
${uName:zhangsan}的方式为没有指定的属性名设置默认值
properties元素配置:
<configuration>
<properties>
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://db.sunyog.top:13306/sunyog_db"/>
<property name="username" value="root"/>
<property name="password" value="123456admin"/>
<property name="test-key" value="test-value-c"/>
</properties>
</configuration>外部资源配置:
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://db.sunyog.top:13306/sunyog_db
username=root
password=123456admin
test-key=test-value-b<configuration>
<!--引用当前目录下的mysql-env.properties文件-->
<properties resource="mysql-env.properties"/>
</configuration>远程资源的配置方式为:
test-key=test-value-a<configuration>
<properties url="/config/mybatis-blog.properties"/>
</configuration>混合写法:
<properties resource="mysql-env.properties">
<property name="test-key" value="test-value-c"/>
</properties>如果混合使用两种方式,遇到同名配置时,Mybatis的处理优先级为:
Configuration API>resource或url>property元素
以上的属性配置可以在mybatis的配置文件中通过 ${}符号引用,亦可以在mybatis程序中通过Configuration类使用,配置文件引用示例,见 环境配置 一章,MybatisAPI引用示例:
public class MybatisConfigService extends MybatisService{
@Override
public void doService() {
SqlSessionFactory sqlSessionFactory = MybatisAppContext.getSqlSessionFactory();
Object v = sqlSessionFactory.getConfiguration().getVariables().get("test-key");
System.out.println(v);
}
}
//mybatis容器
public class MybatisAppContext {
private static SqlSessionFactory sqlSessionFactory = null;
static {
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
try (InputStream in = MybatisApp.class.getResourceAsStream("/mybatis-config.xml");
InputStreamReader reader=new InputStreamReader(in)) {
sqlSessionFactory = builder.build(reader);
} catch (IOException e) {
System.out.println("文件路径读取错误");
}
}
public static SqlSessionFactory getSqlSessionFactory(){
return sqlSessionFactory;
}
}
//打印结果如下
test-value-b环境配置
环境配置主要用于配置数据源和事务处理器,Mybatis同时可以配置多个环境,但同一个SqlSessionFactory实例只能有一个环境是可用的,通过<environments default="">属性控制。
<configuration>
<environments default="mysql">
<environment id="mysql">
<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>
<environment id="mysql8">
<transactionManager type="MANAGED"></transactionManager>
<dataSource type="UNPOOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
</configuration>测试多数据源应用
public class EnvConfigTest {
@Test
public void test2Env() throws IOException {
InputStream input = null;
InputStream input2=null;
try {
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
input = EnvConfigTest.class.getResourceAsStream("/mybatis-config.xml");
SqlSessionFactory factory_1 = builder.build(input);
input2=EnvConfigTest.class.getResourceAsStream("/mybatis-config.xml");
SqlSessionFactory factory_2 = builder.build(input2, "mysql8");
TransactionFactory transaction_1 = factory_1.getConfiguration().getEnvironment().getTransactionFactory();
TransactionFactory transaction_2 = factory_2.getConfiguration().getEnvironment().getTransactionFactory();
System.out.println(transaction_1.getClass());
System.out.println(transaction_2.getClass());
} finally {
if (input !=null){
input.close();
}
if (input2!=null){
input2.close();
}
}
}
}打印结果
class org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory
class org.apache.ibatis.transaction.managed.ManagedTransactionFactory事务管理器
内置事务管理器
在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
- JDBC – 这个配置直接使用了 JDBC 的提交和回滚功能,它依赖从数据源获得的连接来管理事务作用域。默认情况下,为了与某些驱动程序兼容,它在关闭连接时启用自动提交。然而,对于某些驱动程序来说,启用自动提交不仅是不必要的,而且是一个代价高昂的操作。因此,从 3.5.10 版本开始,你可以通过将 "skipSetAutoCommitOnClose" 属性设置为 "true" 来跳过这个步骤。
- MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
<transactionManager type="JDBC">
<property name="skipSetAutoCommitOnClose" value="true"/>
</transactionManager>
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
</transactionManager>自定义事务管理器
除了以上两种内置的事务管理器之外,Mybatis还支持自定义事务管理器,需要实现 TransactionFactory和 Transaction两个接口。使用方式如下:
- 实现
Transaction和TransactionFactory接口
package top.sunyog.mybatis.trans;
import org.apache.ibatis.transaction.Transaction;
import java.sql.Connection;
import java.sql.SQLException;
/**
* @author Myste
* @since 2023/11/20 13:36
*/public class MyTransaction implements Transaction {
private Connection conn;
public MyTransaction(Connection conn) {
this.conn=conn;
}
@Override
public Connection getConnection() throws SQLException {
//设置不自动提交
conn.setAutoCommit(false);
return this.conn;
}
@Override
public void commit() throws SQLException {
//提交之前打印信息
System.out.println("自定义commit");
this.conn.commit();
}
@Override
public void rollback() throws SQLException {
this.conn.rollback();
}
@Override
public void close() throws SQLException {
this.conn.close();
}
@Override
public Integer getTimeout() throws SQLException {
return null;
}
}package top.sunyog.mybatis.trans;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.TransactionFactory;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class MyTransactionFactory implements TransactionFactory {
@Override
public Transaction newTransaction(Connection conn) {
return new MyTransaction(conn);
}
@Override
public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
Transaction trans = null;
try {
Connection conn = dataSource.getConnection();
trans = new MyTransaction(conn);
return trans;
} catch (SQLException e) {
e.printStackTrace();
System.out.println("数据异常");
return null; }
}
}- 配置自定义的事务处理器
<environments default="mysql">
<environment id="mysql">
<transactionManager type="top.sunyog.mybatis.trans.MyTransactionFactory"></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>- 测试update方法
private void testUpdateStatusScript(ApplicationRepository mapper){
AppTestEntity param = new AppTestEntity();
param.setId(1L);
param.setAppStatus("3");
int row = mapper.updateByScript(param);
System.out.println(row);
}打印结果为:
1
自定义commit查看数据库,确认状态已修改
数据源
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。 有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"):
- UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。
- POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。
- JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。
以上三种数据源的可配置项如下
| 配置项 | 数据源 | 备注 | |
|---|---|---|---|
| driver | POOL和UNPOOL | 驱动类全名 | |
| url | POOL和UNPOOL | JDBC的url地址 | |
| username | POOL和UNPOOL | 数据库用户名 | |
| password | POOL和UNPOOL | 数据库密码 | |
| defaultTransactionIsolationLevel | POOL和UNPOOL | 数据库事务隔离级别 | |
| defaultNetworkTimeout | POOL和UNPOOL | 数据库操作超时时间 | |
| poolMaximumActiveConnections | POOL | 连接池中活动链接数量,默认10 | |
| poolMaximumIdleConnections | POOL | 空闲连接数量 | |
| poolMaximumCheckoutTime | POOL | 在被强制返回之前,池中连接被检出的时间,默认20秒 | |
| poolTimeToWait | POOL | 如果获取连接花费时间超过这个数(默认20秒),连接池会打印状态日志并重新尝试获取一个连接 | |
| poolMaximumLocalBadConnectionTolerance | POOL | 如果一个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnections 与 poolMaximumLocalBadConnectionTolerance 之和 | |
| poolPingQuery | POOL | 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET” | |
| poolPingEnabled | POOL | 是否启用侦测查询。若开启,需要设置 poolPingQuery 属性为一个可执行的 SQL 语句(最好是一个速度非常快的 SQL 语句),默认值:false | |
| poolPingConnectionsNotUsedFor | POOL | 配置 poolPingQuery 的频率。(默认值为0,仅当 poolPingEnabled 为 true 时适用) |
关于数据源的配置,前四项是必填项,其他配置都有默认值。实际应用中,可根据服务器硬件配置修改个别配置。
除去以上三种数据源之外,还可以使用第三方数据源,如:c3p0和druid两种数据源比较常见,其中druid为alibaba提供,国内常用。
类型别名
通过配置类型别名,可以在xml映射文件中使用别名来简化resultType和parameterType属性,如
<configuration>
<typeAliases>
<package name="top.sunyog.common.entity"/>
</typeAliases>
</configuration><!--xml映射文件-->
<select id="queryById" resultType="AppTestEntity">
<include refid="query_column"/> where id=#{id} limit 0,1
</select>注意:可以为整个包内的所有类型设置别名,也可以为一个类设置单独的别名。两种方式的写法如下
<!--app代表这个类-->
<typeAlias type="top.sunyog.common.entity.AppTestEntity" alias="app"/>
<!--使用类名称(非全名)代表这个类,如
appTestEntity或AppTestEntity代表top.sunyog.common.entity.AppTestEntity
-->
<package name="top.sunyog.common.entity"/>另外,mybatis内置了一部分常见的类型别名,可以在 org.apache.ibatis.type.TypeAliasRegistry这个类的构造方法中查看
public class TypeAliasRegistry {
...
public TypeAliasRegistry() {
registerAlias("string", String.class);
registerAlias("byte", Byte.class);
registerAlias("char", Character.class);
registerAlias("character", Character.class);
registerAlias("long", Long.class);
registerAlias("short", Short.class);
registerAlias("int", Integer.class);
registerAlias("integer", Integer.class);
registerAlias("double", Double.class);
registerAlias("float", Float.class);
registerAlias("boolean", Boolean.class);
registerAlias("byte[]", Byte[].class);
registerAlias("char[]", Character[].class);
registerAlias("character[]", Character[].class);
registerAlias("long[]", Long[].class);
registerAlias("short[]", Short[].class);
registerAlias("int[]", Integer[].class);
registerAlias("integer[]", Integer[].class);
registerAlias("double[]", Double[].class);
registerAlias("float[]", Float[].class);
registerAlias("boolean[]", Boolean[].class);
registerAlias("_byte", byte.class);
registerAlias("_char", char.class);
registerAlias("_character", char.class);
registerAlias("_long", long.class);
registerAlias("_short", short.class);
registerAlias("_int", int.class);
registerAlias("_integer", int.class);
registerAlias("_double", double.class);
registerAlias("_float", float.class);
registerAlias("_boolean", boolean.class);
registerAlias("_byte[]", byte[].class);
registerAlias("_char[]", char[].class);
registerAlias("_character[]", char[].class);
registerAlias("_long[]", long[].class);
registerAlias("_short[]", short[].class);
registerAlias("_int[]", int[].class);
registerAlias("_integer[]", int[].class);
registerAlias("_double[]", double[].class);
registerAlias("_float[]", float[].class);
registerAlias("_boolean[]", boolean[].class);
registerAlias("date", Date.class);
registerAlias("decimal", BigDecimal.class);
registerAlias("bigdecimal", BigDecimal.class);
registerAlias("biginteger", BigInteger.class);
registerAlias("object", Object.class);
registerAlias("date[]", Date[].class);
registerAlias("decimal[]", BigDecimal[].class);
registerAlias("bigdecimal[]", BigDecimal[].class);
registerAlias("biginteger[]", BigInteger[].class);
registerAlias("object[]", Object[].class);
registerAlias("map", Map.class);
registerAlias("hashmap", HashMap.class);
registerAlias("list", List.class);
registerAlias("arraylist", ArrayList.class);
registerAlias("collection", Collection.class);
registerAlias("iterator", Iterator.class);
registerAlias("ResultSet", ResultSet.class);
}
}类型处理器
在获取到数据库查询结果时,mybatis会通过类型处理器将查询结果转换为对应的java类型,mybatis中内置了很多类型处理器,除此之外,如果内置的类型处理器不满足业务需要,还可以通过实现org.apache.ibatis.type.TypeHandler<T>接口或继承org.apache.ibatis.type.BaseTypeHandler类的方式自定义类型处理器。
mybatis内置的类型处理器可以通过这个链接查看 https://mybatis.org/mybatis-3/zh/configuration.html#typeHandlers
自定义类型转换器详见 Mybatis基础#类型处理器 一章
映射器配置
mybatis中的映射器包括mapper接口和mapper文件两种,mybatis程序在启动时需要读取映射器到内存中备用,所以需要指定mapper接口或mapper文件的位置。有以下几种形式
- 指定映射器文件的位置
<mappers>
<mapper resource="mapper/ApplicationMapper.xml"/>
</mappers>- 指定mapper接口类全名
<mapper class="top.sunyog.mybatis.mapper.AppAnnoMapper"/>- 将包内的接口全部注册为映射器
<package name="top.sunyog.mybatis.mapper"/>- 指定外部mapper资源
<mapper url="file:///app/app_demo/mappers/ApplicationMapper.xml"/>这里需要注意:如果通过指定mapper接口来配置映射器,需要保证接口的包路径名称和xml文件的文件夹路径名称一致,否则会因为两者对应不上而报错,除非没有xml映射文件(通过注解或代码的形式指定sql语句)。
行为配置 settings(常用)
cacheEnabled
备注:是否开启二级缓存 默认值:true 建议值:false 建议原因: mybatis缓存包括一级缓存和二级缓存,一级缓存是不能关闭的(可以通过配置修改作用域),二级缓存可以通过此配置关闭。关于一二级缓存的详细内容,见Mybatis缓存一章 在集群/微服务环境下,其他服务修改了同一个文件对当前微服务是不可见的,在这种情况下,数据库里的真实数据已经更新了,但当前服务的缓存仍然在生效中,会读取到脏数据。
localCacheScope
备注:设置一级缓存作用域 默认值:SESSION 建议值:STATEMENT 建议原因:微服务环境下可以防止读取到脏数据。关于一二级缓存的详细内容,见Mybatis缓存
autoMappingBehavior
备注:设置如何将字段映射行为属性,可以在NONE、PARTIAL、FULL三个可选项中选择 默认值:PARTIAL 建议值: 建议原因: 选项说明:
| 配置 | 说明 |
|---|---|
| NONE | 不自动映射,必须通过 <resultMap>标签手动指定映射 |
| PAERIAL | 只会自动映射没有定义嵌套结果映射的字段 |
| FULL | 自动映射任何复杂的结果集(无论是否嵌套) |
可通过一下代码测试NONE配置的行为
@Test
public void testAutoMapBehavior() {
SqlSession session = this.sqlSessionFactory.openSession();
ApplicationRepository mapper = session.getMapper(ApplicationRepository.class);
AppTestEntity e1 = mapper.queryById(1L);
System.out.println(e1);
}默认配置下的打印结果
AppTestEntity{id=1, appName='测试应用1', appCode='ceshi', authType='1', createDate=2023-10-31, creator='admin', appStatus='null', authTypeDict=null, appStatusDict=null, services=null}配置 <setting name="autoMappingBehavior" value="NONE"/>下的打印结果
null如果通过 resultMap的方式指定映射关系,在NONE配置下也能成功映射
添加resultMap映射
<resultMap id="appTestMap" type="top.sunyog.common.entity.AppTestEntity">
<result column="app_name" property="appName"/>
<result column="app_code" property="appCode"/>
<result column="auth_type" property="authType"/>
<result column="create_date" property="createDate"/>
</resultMap>
<!--省略sql-->
<select id="queryById" resultMap="appTestMap">...</select>配置 <setting name="autoMappingBehavior" value="NONE"/>下的打印结果
AppTestEntity{id=null, appName='测试应用1', appCode='ceshi', authType='1', createDate=2023-10-31, creator='null', appStatus='null', authTypeDict=null, appStatusDict=null, services=null}注意:只有result标签指明的字段才能正确映射,没指明的不会映射出结果。
#细节 这里可以通过 autoMapping属性实现局部的自动映射,来改变 autoMappingBehavior的配置行为
<resultMap id="appTestMap" type="top.sunyog.common.entity.AppTestEntity" autoMapping="true">
...
</resultMap>配置 <setting name="autoMappingBehavior" value="NONE"/>下的打印结果
AppTestEntity{id=1, appName='测试应用1', appCode='ceshi', authType='1', createDate=2023-10-31, creator='admin', appStatus='3', authTypeDict=null, appStatusDict=null, services=null}可以通过以下代码测试FULL配置的行为
public interface ApplicationRepository {
AppVo queryAppVoById(Long id);
}
//测试代码
public class EnvConfigTest {
private SqlSessionFactory sqlSessionFactory;
@Test
public void testAutoMapFull(){
SqlSession session = this.sqlSessionFactory.openSession();
ApplicationRepository mapper = session.getMapper(ApplicationRepository.class);
AppVo e1 = mapper.queryAppVoById(1L);
System.out.println(e1);
}
}<mapper namespace="top.sunyog.mybatis.mapper.ApplicationRepository">
<resultMap id="appVoMap" type="appVo" autoMapping="true">
<!--注意,这里的association内部没有设置自动映射-->
<association property="service" column="service_id" foreignColumn="id">
<id column="service_id" property="id"/>
<result column="service_name" property="serviceName"/>
</association>
</resultMap>
<select id="queryAppVoById" resultMap="appVoMap">
select t1.id,t1.app_name,t2.id as service_id,t2.service_name,t2.service_code,t2.service_path from app_test t1
left join service_test t2 on t1.id=t2.app_id
where t1.id=#{id}
</select>
</mapper>默认配置状态下的打印结果
AppVo{id=1, appName='测试应用1', service=ServiceTestEntity{id=3, serviceName='注册中心', serviceCode='null', servicePath='null', appId=null}}修改配置为 <setting name="autoMappingBehavior" value="FULL"/>后的结果
AppVo{id=1, appName='测试应用1', service=ServiceTestEntity{id=3, serviceName='注册中心', serviceCode='nacos-service', servicePath='/nacos', appId=null}}因此,FULL可以实现复杂对象的自动映射
autoMappingUnknownColumnBehavior
备注:自动映射时,发现未知列/属性时的行为。可选值包括NONE、WARNING、FAILING 默认值:NONE 建议值: 建议原因: 选项说明:
| 配置 | 说明 |
|---|---|
| NONE | 无操作 |
| WARNING | 打印警告日志,需要保证org.apache.ibatis.session.AutoMappingUnknownColumnBehavior的日志级别为warn |
| FAILING | 抛出 SqlSessionException 异常 |
测试代码使用上例 autoMappingBehavior中的 testAutoMapFull()方法,修改xml映射文件
<select id="queryAppVoById" resultType="appVo">默认配置下的输出结果(仅输出打印)
AppVo{id=1, appName='测试应用1', service=null}配置 <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>后的结果(输出错误日志)
WARN [main] - Unknown column is detected on 'top.sunyog.mybatis.mapper.ApplicationRepository.queryAppVoById' auto-mapping. Mapping parameters are [columnName=service_id,propertyName=service_id,propertyType=null]
WARN [main] - Unknown column is detected on 'top.sunyog.mybatis.mapper.ApplicationRepository.queryAppVoById' auto-mapping. Mapping parameters are [columnName=service_name,propertyName=service_name,propertyType=null]
WARN [main] - Unknown column is detected on 'top.sunyog.mybatis.mapper.ApplicationRepository.queryAppVoById' auto-mapping. Mapping parameters are [columnName=service_code,propertyName=service_code,propertyType=null]
WARN [main] - Unknown column is detected on 'top.sunyog.mybatis.mapper.ApplicationRepository.queryAppVoById' auto-mapping. Mapping parameters are [columnName=service_path,propertyName=service_path,propertyType=null]
AppVo{id=1, appName='测试应用1', service=null}配置 <setting name="autoMappingUnknownColumnBehavior" value="FAILING"/>0后的结果(报错退出)
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.apache.ibatis.session.SqlSessionException: Unknown column is detected on 'top.sunyog.mybatis.mapper.ApplicationRepository.queryAppVoById' auto-mapping. Mapping parameters are [columnName=service_id,propertyName=service_id,propertyType=null]
### The error may exist in mapper/ApplicationMapper.xml
### The error may involve top.sunyog.mybatis.mapper.ApplicationRepository.queryAppVoById
### The error occurred while handling results
### SQL: select t1.id,t1.app_name,t2.id as service_id,t2.service_name,t2.service_code,t2.service_path from app_test t1 left join service_test t2 on t1.id=t2.app_id where t1.id=?
### Cause: org.apache.ibatis.session.SqlSessionException: Unknown column is detected on 'top.sunyog.mybatis.mapper.ApplicationRepository.queryAppVoById' auto-mapping. Mapping parameters are [columnName=service_id,propertyName=service_id,propertyType=null]lazyLoadingEnabled
备注:延迟加载开关,设置关联查询的延迟加载。其中的关联查询即是通过<association>或 <collection> 标签配置的关联查询 默认值:false(V3.4.1及之前版本为true) 建议值:true 建议原因:很多情况下我们只需要获取主查询中的结果,对关联查询的结果不关心,通过设置延迟加载可以减少不必要的查询。
为了测试这个配置需要修改mapper包的日志级别为 DEBUG
<Loggers>
<Logger name="top.sunyog.mybatis.mapper" level="debug"/>
<Loggers>测试类
@Test
public void testLazyLoad(){
SqlSession session = this.sqlSessionFactory.openSession();
SimpleQueryMapper mapper = session.getMapper(SimpleQueryMapper.class);
AppTestEntity entity = mapper.queryAppDetail(2);
System.out.println("主查询结束");
System.out.println("查询结果app_name="+entity.getAppName());
System.out.println("获取子查询结果");
DictTest appStatusDict = entity.getAppStatusDict();
System.out.println("子查询结果app_status_dict="+appStatusDict);
}默认情况下(或 <setting name="lazyLoadingEnabled" value="false"/>)的输出结果(先完成所有查询,再输出app_name)
DEBUG [main] - ==> Preparing: select t1.* ,t2.dict_code as auth_type_dc,t2.dict_name as auth_type_dn,t2.dict_type as auth_type_dt,t2.dict_sort as auth_type_ds from ( select id,app_name,app_code,auth_type,create_date,creator,app_status from app_test where id=? ) t1 left join ( select dict_code,dict_name,dict_type,dict_sort from dict_test where dict_type='app_auth_type' ) t2 on t1.auth_type=t2.dict_code
DEBUG [main] - ==> Parameters: 2(Integer)
DEBUG [main] - ====> Preparing: select dict_code,dict_name,dict_type,dict_sort from dict_test where dict_type='app_status' and dict_code=?
DEBUG [main] - ====> Parameters: 0(String)
DEBUG [main] - <==== Total: 1
DEBUG [main] - ====> Preparing: select * from service_test where app_id=?
DEBUG [main] - ====> Parameters: 2(Integer)
DEBUG [main] - <==== Total: 2
DEBUG [main] - <== Total: 1
主查询结束
查询结果app_name=公共应用1
获取子查询结果
子查询结果app_status_dict=DictTest{dictName='临时应用', dictCode='0', dictType='app_status', dictSort=0}配置 <setting name="lazyLoadingEnabled" value="true"/>下的输出(先执行主查询,再打印app_name,再执行后续必要的查询)
DEBUG [main] - ==> Preparing: select t1.* ,t2.dict_code as auth_type_dc,t2.dict_name as auth_type_dn,t2.dict_type as auth_type_dt,t2.dict_sort as auth_type_ds from ( select id,app_name,app_code,auth_type,create_date,creator,app_status from app_test where id=? ) t1 left join ( select dict_code,dict_name,dict_type,dict_sort from dict_test where dict_type='app_auth_type' ) t2 on t1.auth_type=t2.dict_code
DEBUG [main] - ==> Parameters: 2(Integer)
DEBUG [main] - <== Total: 1
主查询结束
查询结果app_name=公共应用1
获取子查询结果
DEBUG [main] - ==> Preparing: select dict_code,dict_name,dict_type,dict_sort from dict_test where dict_type='app_status' and dict_code=?
DEBUG [main] - ==> Parameters: 0(String)
DEBUG [main] - <== Total: 1
子查询结果app_status_dict=DictTest{dictName='临时应用', dictCode='0', dictType='app_status', dictSort=0}#细节 这里需要注意resultMap中设定的 fetchType属性,一个resultMap终端关联关系是否延迟加载取决于fetchType的设置,和整体配置无关。fetchType属性的可选值包括
- lazy:延迟加载
- eager:非延迟加载
如:可以修改这个resultMap,自定义appStatusDict属性延迟加载 services属性不延迟
<resultMap id="appDetail" type="appTestEntity" autoMapping="true">
...
<association fetchType="lazy" property="appStatusDict" javaType="dictTest" select="queryAppStatus" column="app_status"></association>
<collection fetchType="eager" property="services" column="id" select="queryServices" javaType="ArrayList" ofType="serviceTestEntity"/>
</resultMap>#细节 另一个需要注意的是 aggressiveLazyLoading这个配置 👇
aggressiveLazyLoading
备注:开启该配置后,查询结果对象的任意方法调用,都会触发延迟加载属性的加载 默认值:false 建议值:false 建议原因: 任意方法包括getter、setter方法,而getter、setter方法的调用频率极高。而Mybatis自动映射通过setter方法实现,因此开启这个配置后延迟加载也就相当于关闭了(包括fetchType)
开启此配置后,上例的测试代码输出内容直接锁定为
DEBUG [main] - ==> Preparing: select t1.* ,t2.dict_code as auth_type_dc,t2.dict_name as auth_type_dn,t2.dict_type as auth_type_dt,t2.dict_sort as auth_type_ds from ( select id,app_name,app_code,auth_type,create_date,creator,app_status from app_test where id=? ) t1 left join ( select dict_code,dict_name,dict_type,dict_sort from dict_test where dict_type='app_auth_type' ) t2 on t1.auth_type=t2.dict_code
DEBUG [main] - ==> Parameters: 2(Integer)
DEBUG [main] - ====> Preparing: select dict_code,dict_name,dict_type,dict_sort from dict_test where dict_type='app_status' and dict_code=?
DEBUG [main] - ====> Parameters: 0(String)
DEBUG [main] - <==== Total: 1
DEBUG [main] - ====> Preparing: select * from service_test where app_id=?
DEBUG [main] - ====> Parameters: 2(Integer)
DEBUG [main] - <==== Total: 2
DEBUG [main] - <== Total: 1
主查询结束
查询结果app_name=公共应用1
获取子查询结果
子查询结果app_status_dict=DictTest{dictName='临时应用', dictCode='0', dictType='app_status', dictSort=0}即便设置了fetchType也失效
<resultMap id="appDetail" type="appTestEntity" autoMapping="true">
<association fetchType="lazy" property="appStatusDict" javaType="dictTest" select="queryAppStatus" column="app_status"></association>
<collection fetchType="lazy" property="services" column="id" select="queryServices" javaType="ArrayList" ofType="serviceTestEntity"/>
</resultMap>lazyLoadTriggerMethods
备注:指定对象的哪些方法触发延迟加载,方法名用 ,分隔 默认值:equals,clone,hashCode,toString 建议值: 建议原因:
继续修改方法的内容如下:
public class EnvConfigTest {
private SqlSessionFactory sqlSessionFactory;
@Test
public void testLazyLoad(){
SqlSession session = this.sqlSessionFactory.openSession();
SimpleQueryMapper mapper = session.getMapper(SimpleQueryMapper.class);
AppTestEntity entity = mapper.queryAppDetail(2);
System.out.println("主查询结束");
System.out.println("查询结果app_name="+entity.getAppName());
int row = entity.hashCode();
// System.out.println("获取子查询结果");
// DictTest appStatusDict = entity.getAppStatusDict();
// System.out.println("子查询结果app_status_dict="+appStatusDict);
}
}默认配置下的输出:(hashCode方法被调用后,执行了所有的延迟加载)
DEBUG [main] - ==> Preparing: select t1.* ,t2.dict_code as auth_type_dc,t2.dict_name as auth_type_dn,t2.dict_type as auth_type_dt,t2.dict_sort as auth_type_ds from ( select id,app_name,app_code,auth_type,create_date,creator,app_status from app_test where id=? ) t1 left join ( select dict_code,dict_name,dict_type,dict_sort from dict_test where dict_type='app_auth_type' ) t2 on t1.auth_type=t2.dict_code
DEBUG [main] - ==> Parameters: 2(Integer)
DEBUG [main] - <== Total: 1
主查询结束
查询结果app_name=公共应用1
DEBUG [main] - ==> Preparing: select dict_code,dict_name,dict_type,dict_sort from dict_test where dict_type='app_status' and dict_code=?
DEBUG [main] - ==> Parameters: 0(String)
DEBUG [main] - <== Total: 1
DEBUG [main] - ==> Preparing: select * from service_test where app_id=?
DEBUG [main] - ==> Parameters: 2(Integer)
DEBUG [main] - <== Total: 2修改配置 <setting name="lazyLoadTriggerMethods" value="toString"/>后的输出:( hashCode方法调用不再执行延迟加载)
DEBUG [main] - ==> Preparing: select t1.* ,t2.dict_code as auth_type_dc,t2.dict_name as auth_type_dn,t2.dict_type as auth_type_dt,t2.dict_sort as auth_type_ds from ( select id,app_name,app_code,auth_type,create_date,creator,app_status from app_test where id=? ) t1 left join ( select dict_code,dict_name,dict_type,dict_sort from dict_test where dict_type='app_auth_type' ) t2 on t1.auth_type=t2.dict_code
DEBUG [main] - ==> Parameters: 2(Integer)
DEBUG [main] - <== Total: 1
主查询结束
查询结果app_name=公共应用1multipleResultSetsEnabled
备注:是否支持单次查询返回多个结果集,此配置需要数据库驱动的支持。Mysql数据库多结果集查询方法见Mybatis结果映射#collection集合类型的多结果集查询 默认值:true 建议值:不设置 建议原因:mysql-connector-javaV8数据库驱动中,此项配置无效
useColumnLabel
备注:是否使用列标签代替列名。依赖于数据库驱动 默认值:true 建议值:不设置 建议原因:mysql-connector-java数据库驱动中,此项配置无效 失效原因: 此项设置的区别在于,在对resultSet的处理过程中是调用 ResultSetMetaData#getColumnLabel方法还是 ResultSetMetaData#getColumnName方法,(源码见UnknownTypeHandler#resolveTypeHandler和 ResultSetWrapper构造方法) 而对于mysql-connector-java来说,这两个方法最后都是调用的 Field#getName方法,部分源码如下
package com.mysql.cj.jdbc.result;
public class ResultSetMetaData implements java.sql.ResultSetMetaData {
@Override
public String getColumnLabel(int column) throws SQLException {
if (this.useOldAliasBehavior) {
return getColumnName(column);
}
return getField(column).getColumnLabel();
}
@Override
public String getColumnName(int column) throws SQLException {
if (this.useOldAliasBehavior) {
return getField(column).getName();
}
String name = getField(column).getOriginalName();
if (name == null) {
return getField(column).getName();
}
return name;
}
protected Field getField(int columnIndex) throws SQLException {
if ((columnIndex < 1) || (columnIndex > this.fields.length)) {
throw SQLError.createSQLException(Messages.getString("ResultSetMetaData.46"), MysqlErrorNumbers.SQL_STATE_INVALID_COLUMN_NUMBER,
this.exceptionInterceptor);
}
return this.fields[columnIndex - 1];
}
}
package com.mysql.cj.result;
public class Field implements ProtocolEntity {
public String getColumnLabel() {
return getName();
}
public String getName() {
return this.columnName.toString();
}
}useGeneratedKeys
备注:允许 JDBC 自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。 默认值:false 建议值:不设置 建议原因:主键可通过代码自行生成。另外,mysql-connector-javaV8数据库驱动中,此项配置没有任何影响。
#细节 简单的主键生成行为可以参考Mybatis基础#selectKey标签生成主键,分布式主键生成方案可参考Twitter雪花算法。
defaultExecutorType
备注:配置默认的执行器。 默认值:SIMPLE 可选值:
| 值 | 说明 | 对应的Executor实现类 |
|---|---|---|
| SIMPLE | 普通的执行器,使用JDBC默认Statement | SimpleExecutor |
| REUSE | 预处理语句执行器,使用JDBC的PreparedStatement | ReuseExecutor |
| BATCH | 重用语句+批量更新执行器 | BatchExecutor |
建议值:SIMPLE 建议原因:SIMPLE比较通用。如果有特殊需求,可以通过 SqlSessionFactoryBuilder#openSession(ExecutorType execType)方法获取到包含对应的Executor的SqlSession。
注:Executor保存在 org.apache.ibatis.session.defaults.DefaultSqlSession类中。
批量插入数据的代码示例
public class EnvConfigTest {
private SqlSessionFactory sqlSessionFactory;
private SqlSession sqlSession;
@Test
public void testBatchInsert(){
List<AppTestEntity> list=new ArrayList<>();
AppTestEntityBuilder builder = new AppTestEntityBuilder();
builder.setAppName("test-name").setAppCode("test-name-code").setAuthType("1").setCreator("junit");
list.add(builder.build());
//省略n个list.add
//关键在ExecutorType.BATCH
SqlSession session = this.sqlSessionFactory.openSession(ExecutorType.BATCH);
ApplicationRepository mapper = session.getMapper(ApplicationRepository.class);
list.stream().forEach(o->{
mapper.addApp(o);
});
session.commit();
session.close();
}
}defaultStatementTimeout
备注:等待数据库响应的秒数。这项配置需要数据库驱动的支持,在Mysql中此项配置基本无效,在postgres数据库中此配置有效 默认值:null 可选值:任意正整数 建议值:根据实际情况设置 建议原因:取决于数据库硬件和配置
Postgres配置下的测试代码:
public class PostgresTest {
private SqlSessionFactory sqlSessionFactory;
private SqlSession sqlSession;
@Before
public void before(){
try (InputStream inputStream = PostgresTest.class.getResourceAsStream("/mybatis-config.xml")){
this.sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream,"postgres9");
this.sqlSession=this.sqlSessionFactory.openSession();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@After
public void after(){
this.sqlSession.clearCache();
this.sqlSession.close();
}
//新增十万条数据
@Test
public void testBatchInsert(){
List<AppTestEntity> list=new ArrayList<>();
AppTestEntityBuilder builder = new AppTestEntityBuilder();
builder.setAppName("test-name").setAppCode("test-name-code").setAuthType("1").setCreator("junit");
for (int i = 0; i < 100000; i++) {
AppTestEntity e = builder.build();
e.setId(((long) i+1));
list.add(e);
}
//省略n个list.add
SqlSession session = this.sqlSessionFactory.openSession(ExecutorType.BATCH);
ApplicationRepository mapper = session.getMapper(ApplicationRepository.class);
list.stream().forEach(o->{
mapper.addApp(o);
});
session.commit();
session.close();
}
//查询测试
@Test
public void testFetchSize(){
SimpleQueryMapper mapper = this.sqlSession.getMapper(SimpleQueryMapper.class);
long start = System.currentTimeMillis();
List<AppTestEntity> list = mapper.queryList("1");
long gap = System.currentTimeMillis() - start;
System.out.println("selected result size: "+list.size());
System.out.println("selected time: "+gap);
}
}配置 <setting name="defaultFetchSize" value="1"/>后的输出
selected result size: 100000
selected time: 8994配置 <setting name="defaultFetchSize" value="1000"/>后的输出
selected result size: 100000
selected time: 413不配置情况下的输出
selected result size: 100000
selected time: 418#细节 这里需要注意,此配置会被 fetchSize属性覆盖。fetchSize可以通过以下方式设置
- 注解的方式
@Select(value = "select * from app_test where auth_type=#{type}")
@Options(fetchSize = 1000)
List<AppTestEntity> queryList(@Param("type") String type);- 标签的方式
<select id="queryList" resultType="AppTestEntity" fetchSize="1000">
select * from app_test where auth_type=#{type}
</select>defaultResultSetType
备注:指定语句默认的滚动策略 可选值:
| 配置值 | 说明 |
|---|---|
| FORWARD_ONLY | 索引只能向后方滚动,不能往前 |
| SCROLL_SENSITIVE | 索引可向前后滚动,且更新敏感 |
| SCROLL_INSENSITIVE | 索引可向前后滚动,且更新不敏感 |
| DEFAULT | 默认,效果和不设置相同 |
默认值:null(DEFAULT) 建议值:不设置 建议原因:不常用,此配置是对jdbc的行为控制,而在mybatis项目中不会直接操作jdbc
safeRowBoundsEnabled
备注:是否允许在嵌套语句中使用分页API(RowBounds)。如果允许使用则设置为 false 默认值:false 建议值: 建议原因:
此配置常见的生效情况是,调用了 sqlSession#selectList(String statement,Object param,RowBounds rowBounds)这个方法,而参数statement又是嵌套语句,如以下测试代码:
@Test
public void testSafeRow(){
List objects = this.sqlSession.selectList("top.sunyog.mybatis.mapper.SimpleQueryMapper.queryAppDetail", 1, new RowBounds(0, 1));
for (Object obj : objects) {
System.out.println(obj);
}
}此代码在默认配置情况下可以正常输出
AppTestEntity{id=null, appName='测试应用1', appCode='ceshi', authType='1', createDate=2023-10-31, creator='admin', appStatus='3', authTypeDict=DictTest{dictName='NONE', dictCode='1', dictType='app_auth_type', dictSort=0}, appStatusDict=DictTest{dictName='正常应用', dictCode='3', dictType='app_status', dictSort=0}, services=[ServiceTestEntity{id=3, serviceName='注册中心', serviceCode='nacos-service', servicePath='/nacos', appId=1}]}而配置 <setting name="safeRowBoundsEnabled" value="true"/>后,会报错
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.apache.ibatis.executor.ExecutorException: Mapped Statements with nested result mappings cannot be safely constrained by RowBounds. Use safeRowBoundsEnabled=false setting to bypass this check.
### The error may exist in mapper/SimpleQueryMapper.xml
### The error may involve top.sunyog.mybatis.mapper.SimpleQueryMapper.queryAppDetail
### The error occurred while handling results
### SQL: select t1.* ,t2.dict_code as auth_type_dc,t2.dict_name as auth_type_dn,t2.dict_type as auth_type_dt,t2.dict_sort as auth_type_ds from ( select id,app_name,app_code,auth_type,create_date,creator,app_status from app_test where id=? ) t1 left join ( select dict_code,dict_name,dict_type,dict_sort from dict_test where dict_type='app_auth_type' ) t2 on t1.auth_type=t2.dict_code
### Cause: org.apache.ibatis.executor.ExecutorException: Mapped Statements with nested result mappings cannot be safely constrained by RowBounds. Use safeRowBoundsEnabled=false setting to bypass this check.safeResultHandlerEnabled
备注:是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false 默认值:true 建议值:按实际情况设置 建议原因: 结果处理器ResultHandler的使用方法见Mybatis结果映射#关于 @ResultMap 和 @ResultType的应用
mapUnderscoreToCamelCase
备注:是否开启驼峰命名自动映射 默认值:false 建议值:true 建议原因:可以减少 <resultMap>标签的使用
jdbcTypeForNull
备注:当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER 可选值:org.apache.ibatis.type.JdbcType 常量 默认值:OTHER 建议值:不设置 建议原因:
defaultScriptingLanguage
备注:指定动态 SQL 生成使用的默认脚本语言。 默认值:org.apache.ibatis.scripting.xmltags.XMLLanguageDriver 建议值:不设置 建议原因:
defaultEnumTypeHandler
备注:指定枚举类型的默认TypeHandler,关于枚举类型的TypeHandler使用示例,见Mybatis基础#枚举类型映射 默认值:EnumTypeHandler 建议值:按实际情况设置,建议设置为 EnumOrdinalTypeHandler 建议原因:实际应用中,枚举类型大多数都是顺序编码的字典值
callSettersOnNulls
备注:指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法 默认值:false 建议值:true 建议原因:返回值中某个字段为null时,再map对象中也会插入这个对应的key,这样可以减少对map的 containsKey方法操作。
通过以下代码测试设置的行为
@Test
public void testNullSet(){
SimpleQueryMapper mapper = this.sqlSession.getMapper(SimpleQueryMapper.class);
//在这之前先执行这个sql
//update app_test set creator=null where id=13;
List<Map<String, Object>> list = mapper.queryMapRes(13);
System.out.println(list);
}默认设置时的输出:
[{app_name=test-name, auth_type=1, id=13, create_date=2023-11-30, app_code=test-name-code}]配置 <setting name="callSettersOnNulls" value="true"/>时的输出
[{app_name=test-name, auth_type=1, creator=null, id=13, create_date=2023-11-30, app_code=test-name-code}]useActualParamName
备注:允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,项目必须采用 Java 8 编译,并且加上 -parameters 选项 默认值:true 建议值:true 建议原因:
parameters选项的添加方式为修改maven的pom.xml文件 #细节 注意:这个插件安装或修改后,需要执行maven:clean和maven:compile后才能生效
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>新增测试代码
@Test
public void testMethodParam(){
SimpleQueryMapper mapper = this.sqlSession.getMapper(SimpleQueryMapper.class);
List<AppTestEntity> list = mapper.getAppByStatusAndAuthType("3","1");
System.out.println(list);
}public interface SimpleQueryMapper {
//注意,这里的入参没有添加@Param注解
List<AppTestEntity> getAppByStatusAndAuthType(String status,String authType);
}<select id="getAppByStatusAndAuthType" resultType="appTestEntity">
<include refid="top.sunyog.mybatis.mapper.ApplicationRepository.query_column"></include>
where app_status=#{status} and auth_type=#{authType}
</select>配置 <setting name="useActualParamName" value="false"/>后执行报错
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.apache.ibatis.binding.BindingException: Parameter 'status' not found. Available parameters are [0, 1, param1, param2]
### Cause: org.apache.ibatis.binding.BindingException: Parameter 'status' not found. Available parameters are [0, 1, param1, param2]默认配置或 <setting name="useActualParamName" value="true"/>配置下,打印结果
[AppTestEntity{id=1, appName='测试应用1', appCode='ceshi', authType='1', createDate=2023-10-31, creator='admin', appStatus='3', authTypeDict=null, appStatusDict=null, services=null}]returnInstanceForEmptyRow
备注:当返回行的所有列都是空时,默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集 默认值:false 建议值:true 建议原因:可减少空指针验证
测试代码
public interface SimpleQueryMapper {
@Select("select name,code from empty_table_test where 1=1 limit 0,1")
Map<String, Object> queryEmptyMap(int i);
}
public class EnvConfigTest {
@Test
public void testReturnEmptyObj(){
SimpleQueryMapper mapper = this.sqlSession.getMapper(SimpleQueryMapper.class);
Map<String,Object> entity = mapper.queryEmptyMap(112);
System.out.println(entity);
}
}新增可为空的测试表
create table empty_table_test
(
name int null,
code varchar(32) null
);
insert into empty_table_test(name,code) values(null,null),(null,null)默认配置下的输出
null配置 <setting name="returnInstanceForEmptyRow" value="true"/>时的输出
{}nullableOnForEach
备注:为 <foreach> 标签的 nullable 属性指定默认值 默认值:false 建议值:false 建议原因:如果允许foreach标签传入null,foreach标签返回的sql语句为空,有可能查询出所有数据,这与foreach的初衷不符,可能造成业务出错。
新增测试代码
<mapper>
<select id="queryAppsByIds" resultMap="appTestMap">
<include refid="query_column"></include>
<where>
auth_type='1'
<foreach collection="ids" open="id in (" close=")" index="index" item="item" separator=",">#{item}</foreach>
</where>
</select>
</mapper>public interface ApplicationRepository {
List<AppTestEntity> queryAppsByIds(@Param("ids") Long[] ids);
}
public class EnvConfigTest {
@Test
public void testForeachNull(){
ApplicationRepository mapper = this.sqlSession.getMapper(ApplicationRepository.class);
List<AppTestEntity> list = mapper.queryAppsByIds(null);
System.out.println(list.size());
}
}默认配置下报错
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.apache.ibatis.builder.BuilderException: The expression 'ids' evaluated to a null value.
### Cause: org.apache.ibatis.builder.BuilderException: The expression 'ids' evaluated to a null value.配置 <setting name="nullableOnForEach" value="true"/>后输出(查询条件中没有 in)
[org.apache.ibatis.logging.jdbc.BaseJdbcLogger#debug(137)] DEBUG [main] ==> Preparing: select id, app_name, app_code, auth_type, create_date, creator, app_status from app_test WHERE auth_type='1'
[org.apache.ibatis.logging.jdbc.BaseJdbcLogger#debug(137)] DEBUG [main] ==> Parameters:
[org.apache.ibatis.logging.jdbc.BaseJdbcLogger#debug(137)] DEBUG [main] <== Total: 7
7defaultSqlProviderType
备注:指定一个默认的SqlProvider类 默认值:未设置 建议值:根据实际情况设置,一般不设置 建议原因:实际使用中一般不止一个SqlProvider,故不建议设置
SqlProvider的具体用法见 Mybatis动态SQL详解#动态sql API一章
logImpl
备注:设置Mybatis日志的具体实现 可选值:
- SLF4J
- LOG4J(3.5.9 起废弃)
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING
- NO_LOGGING
默认值:自动 建议值:根据实际情况设置,尽量设置一个 建议原因:官方文档建议
MyBatis 内置日志工厂基于运行时自省机制选择合适的日志工具。它会使用第一个查找得到的工具(按上文列举的顺序查找)。如果一个都未找到,日志功能就会被禁用。
不少应用服务器(如 Tomcat 和 WebShpere)的类路径中已经包含 Commons Logging,所以在这种配置环境下的 MyBatis 会把它作为日志工具,记住这点非常重要。这将意味着,在诸如 WebSphere 的环境中,它提供了 Commons Logging 的私有实现,你的 Log4J 配置将被忽略。MyBatis 将你的 Log4J 配置忽略掉是相当令人郁闷的(事实上,正是因为在这种配置环境下,MyBatis 才会选择使用 Commons Logging 而不是 Log4J)。如果你的应用部署在一个类路径已经包含 Commons Logging 的环境中,而你又想使用其它日志工具,你可以通过在 MyBatis 配置文件 mybatis-config.xml 里面添加一项 setting 来选择别的日志工具。
——《mybatis官方文档》
具体用法见Mybatis日志
logPrefix
备注:指定 MyBatis 增加到日志名称的前缀 默认值:未设置 建议值:不设置 建议原因:此设置后会影响日志系统的默认行为,因此不建议设置,如有必要设置名称前缀,可以在日志配置文件中设置日志规则
具体用法见Mybatis日志