mybatis查询所有数据

用MyBatis查全表数据,有数最直接的有数方式就是写一个不带任何条件的select语句。但很多人第一次跑的有数时候会愣住——返回值到底该接什么?我见过不少项目里,有人用List<Map<String,有数 Object>>硬接,有人干脆返回List<Object>然后在业务层强转。有数能跑,有数但看着难受。有数

先看Mapper接口。有数假设有张user表,有数字段是有数id、name、有数age。有数接口方法就一行:

List<User> selectAll(); 

XML映射文件里对应写:

<select id="selectAll" resultType="com.example.entity.User"> SELECT id,有数 name, age FROM user </select> 

这里有个细节值得说。resultType写全限定类名当然可以,有数但如果项目里配了type-aliases-package,有数直接写User就行。我习惯配别名,少敲一截包名,看着清爽。另外SELECT *也不是不能用,但字段顺序和映射关系一旦变动,排查起来烦。显式列出字段,多花几秒钟,省心。

跑起来之后,MyBatis会拿结果集的列名去匹配User类的属性名。列名name对应属性name,这没问题。但如果数据库字段是user_name,类里是userName,就得靠resultMap或者开启mapUnderscoreToCamelCase。我一般直接开这个配置,下划线转驼峰,省得每个字段都写一遍映射。

说个实际踩过的坑。有次查一张几十万行的表,没加任何条件,selectAll直接返回List<User>。本地测试数据少,跑得挺快。上线之后接口响应时间飙到十几秒,内存也涨得厉害。问题不在SQL本身,而在于一次性把全表数据加载进JVM。MyBatis默认的fetchSize对MySQL来说,如果不设成Integer.MIN_VALUE,驱动还是会一次性拉取所有结果。后来改成流式查询,用ResultHandler逐条处理,内存才降下来。

所以“查询所有数据”这个操作,得看场景。后台管理页面展示用户列表,配合分页插件(比如PageHelper),查的其实是分页后的数据,不是真全量。真需要全量导出或者跑批处理,流式查询比List靠谱。MyBatis的<select>标签支持fetchSize属性,设成-2147483648,MySQL驱动就会逐条返回。代码大概长这样:

<select id="selectAllStream" resultType="com.example.entity.User" fetchSize="-2147483648"> SELECT id, name, age FROM user </select> 

Mapper方法签名改成void selectAllStream(ResultHandler<User> handler)。调用的时候传个handler进去,每条记录回调一次,处理完就丢,不占内存。这个写法知道的人不多,但数据量大的时候能救命。

还有一点,返回值类型。如果只是临时查一下看看数据,用List<Map<String, Object>>确实快,不用建实体类。但正式代码里我坚持用实体类。Map的key是数据库列名,一旦列名改了,编译期不报错,运行期才炸。实体类有getter/setter,IDE能提示,重构也安全。这点主观判断,我站实体类。

最后提一下日志。查全表的时候,MyBatis会把所有结果打印到日志里,几十万行刷屏,控制台直接卡死。生产环境记得把Mapper所在包的日志级别调到INFO或者WARN,别用DEBUG。本地开发无所谓,线上这么干就是给自己找麻烦。

查全表本身不复杂,复杂的是数据量上来之后怎么处理。写selectAll之前先想清楚:这表以后会不会变大?要不要分页?要不要流式?想明白了再动手,比事后改代码强。