专栏精选
文章目录
引言
大家好,我是奇迹老李,一个专注于分享开发经验和基础教程的博主。欢迎来到我的频道,这里汇聚了汇集编程技巧、代码示例和技术教程,欢迎广大朋友们点赞评论提出意见,重要的是点击关注喔 🙆,期待在这里与你共同度过美好的时光🕹️。今天要和大家分享的内容是Mybatis行为配置之Ⅲ---其他常用配置项说明。做好准备,Let's go🚎🚀
摘要
在这篇文章中,我们将了解剩下的关于Mybatis行为的配置,在mybatis项目开发过程中这些配置可能并不常用,而且大多数情况下都是使用默认配置,了解这些配置的意义可以让我们在解决很多罕见异常问题时有的放矢,不至于手忙脚乱。那么准备好开启今天的神奇之旅了吗?
正文
今天我们介绍Mybatis中最后几个控制Mybatis行为的配置项,它们是
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
类中。
批量插入数据的代码示例
java
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配置下的测试代码:
java
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可以通过以下方式设置
- 注解的方式
java
@Select(value = "select * from app_test where auth_type=#{type}")
@Options(fetchSize = 1000)
List<AppTestEntity> queryList(@Param("type") String type);
- 标签的方式
xml
<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
又是嵌套语句,如以下测试代码:
java
@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基于注解的结果映射这篇文章
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
方法操作。
通过以下代码测试设置的行为
java
@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
后才能生效
xml
<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>
新增测试代码
java
@Test
public void testMethodParam(){
SimpleQueryMapper mapper = this.sqlSession.getMapper(SimpleQueryMapper.class);
List<AppTestEntity> list = mapper.getAppByStatusAndAuthType("3","1");
System.out.println(list);
}
java
public interface SimpleQueryMapper {
//注意,这里的入参没有添加@Param注解
List<AppTestEntity> getAppByStatusAndAuthType(String status,String authType);
}
xml
<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
建议原因:可减少空指针验证
测试代码
java
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);
}
}
新增可为空的测试表
sql
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"/>
时的输出
{}
总结
今天介绍的配置项在日常工作中很多都不是很常用,但了解这些配置可以减少很多不必要的错误,甚至解决一些罕见的异常问题。如 returnInstanceForEmptyRow
这个配置能在很大程度上减少空指针异常的出现。
📩 联系方式
❗版权声明
本文为原创文章,版权归作者所有。未经许可,禁止转载。更多内容请访问奇迹老李的博客首页