确认
当前程序版本
3.5.7
问题描述
描述: 流式查询使用com.baomidou.mybatisplus.core.mapper.BaseMapper#selectList(com.baomidou.mybatisplus.core.conditions.Wrapper<T>, org.apache.ibatis.session.ResultHandler<T>)方法未开启流式查询,而是一次性查询出所有匹配数据
重现步骤: 1.MySQL建表
create table employee
(
id bigint auto_increment
primary key,
FIRST_NAME char(20) not null,
LAST_NAME char(20) null,
AGE int null,
SEX char null,
INCOME float null
);
2.插入10w条测试数据
DELIMITER //
CREATE PROCEDURE insert_employee_data()
BEGIN
DECLARE i INT DEFAULT 1;
WHILE i <= 100000 DO
INSERT INTO employee (FIRST_NAME, LAST_NAME, AGE, SEX, INCOME)
VALUES
(CONCAT('姓名', i), CONCAT('姓氏', i), FLOOR(RAND() * (50 - 20 + 1)) + 20, IF(MOD(i, 2) = 0, '女', '男'), FLOOR(RAND() * (10000 - 4000 + 1)) + 4000);
SET i = i + 1;
END WHILE;
END //
DELIMITER ;
CALL insert_employee_data();
3.定义mapper(省略项目搭建过程)
@Mapper
public interface EmployeeMapper extends BaseMapper<Employee>{
}
4.测试代码
@Test
void contextLoads() {
employeeMapper.selectList(null, new ResultHandler<Employee>() {
@Override
public void handleResult(ResultContext<? extends Employee> resultContext) {
System.err.println("resultCount:" + resultContext.getResultCount() + " resultObject" + resultContext.getResultObject());
}
});
}
5.设置debug断点,断点打在org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getRowValue(org.apache.ibatis.executor.resultset.ResultSetWrapper, org.apache.ibatis.mapping.ResultMap, java.lang.String)方法第一行,查看rsw最里层的resultSet的rowData字段
通过rowData字段看出直接查询了10w数据出来,如果数据量过大易产生OOM
修复建议: 提供全局的fetchSize配置设置的地方,或者提供方法级别设置fetchSize
演示正确开启流式查询
1.在重现步骤基础上,关注源码DefaultSqlInjector注入SelectList步骤,找出com.baomidou.mybatisplus.core.injector.methods.SelectList#injectMappedStatement方法中用到fetchSize的地方,打上断点,当前mp版本没有设置fetchSize也就是null,debug时需要手动指定为-2147483648(mysql只有这个值才能开启流式查询,像神通数据库之类指定大于0的值)
2.查看复现步骤5的断点,当rowData等于ResultsetRowStreaming类型时为开启流式查询成功
详细堆栈日志
No response
Comment From: nieqiurong
mybatis-plus:
configuration:
default-fetch-size: 10
Comment From: nestorbian
yaml mybatis-plus: configuration: default-fetch-size: 10
配置完重新测试是可以的,感谢回复 另外能否在mp官网流式查询上特别说明此配置,防止其他开发者遇到相同不生效的问题
Comment From: elcnu986
yaml mybatis-plus: configuration: default-fetch-size: 10配置完重新测试是可以的,感谢回复 另外能否在mp官网流式查询上特别说明此配置,防止其他开发者遇到相同不生效的问题
你好,我在yml中添加了 default-fetch-size: 10 spring boot 和 mp 的版本如下
xml <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.3.2</version> <relativePath/> </parent> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.5</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>3.0.3</version> </dependency>
使用流式查询,该处org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getRowValue(org.apache.ibatis.executor.resultset.ResultSetWrapper, org.apache.ibatis.mapping.ResultMap, java.lang.String) 还是返回了全部结果
Comment From: elcnu986
yaml mybatis-plus: configuration: default-fetch-size: 10配置完重新测试是可以的,感谢回复 另外能否在mp官网流式查询上特别说明此配置,防止其他开发者遇到相同不生效的问题
我这边只有配置上 fetchSize 为 int 最小值时,才会返回 流 @Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE) 不知道你是怎么在添加上述yml配置后,就可以返回流了?
Comment From: nestorbian
yaml mybatis-plus: configuration: default-fetch-size: 10配置完重新测试是可以的,感谢回复 另外能否在mp官网流式查询上特别说明此配置,防止其他开发者遇到相同不生效的问题
我这边只有配置上 fetchSize 为 int 最小值时,才会返回 流 @options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE) 不知道你是怎么在添加上述yml配置后,就可以返回流了?
我是配置的
mybatis-plus:
configuration:
default-fetch-size: -2147483648
```
mysql只能这个值才能生效,一开始我是没发现有这个可以指定fetchSize的配置
**Comment From: mystery4f**
这个主要问题是 mysql 与客户端的约定,写死的 Integer.MIN_VALUE 游标才生效,
在 `com.mysql.cj.jdbc.StatementImpl#createStreamingResultSet` 中:
```java
/**
* We only stream result sets when they are forward-only, read-only, and the
* fetch size has been set to Integer.MIN_VALUE
*
* @return true if this result set should be streamed row at-a-time, rather
* than read all at once.
*/
protected boolean createStreamingResultSet() {
return this.query.getResultType() == Type.FORWARD_ONLY && this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY
&& this.query.getResultFetchSize() == Integer.MIN_VALUE;
}
即对应 mapper xml select 标签的 这些属性
resultOrdered="true" fetchSize="-2147483648" resultSetType="FORWARD_ONLY"
但其实 Oracle/db2 是支持 fetchSize 自定义设置的...,感觉 非 mysql 可以加点个注解,按需 配置?