Spring Boot中JdbcTemplate执行多查询报错问题咨询
这个问题我之前踩过坑,核心原因是JdbcTemplate默认不支持在单个query调用里执行多条SQL语句——哪怕这些语句在MySQL客户端里能正常跑通。这是因为JDBC连接默认关闭了多语句执行的开关,主要是为了防范SQL注入风险。下面给你几个可行的解决办法:
办法一:修改JDBC连接URL,开启多语句支持
在你的数据库连接URL末尾加上allowMultiQueries=true参数,示例如下:
jdbc:mysql://localhost:3306/your_db_name?allowMultiQueries=true
开启这个参数后,你原来的多语句查询就能直接通过JdbcTemplate执行了。不过要注意:这个开关会降低SQL注入的防护能力,如果你的SQL语句里包含用户输入的动态内容,一定要做好参数绑定(用?占位符),绝对不能直接拼接字符串。
办法二:拆分语句,分两次执行
既然你的需求是分页查询+获取总行数,完全可以把两条SQL拆成两次JdbcTemplate调用,这样不用修改连接参数,安全性更高:
// 第一步:执行分页查询,同时标记要计算总行数 List<Foo> fooList = jdbcTemplate.query( "select SQL_CALC_FOUND_ROWS * from foo Limit 10", new FooRowMapper() // 这里替换成你自己的RowMapper实现 ); // 第二步:获取刚才标记的总行数 Long totalCount = jdbcTemplate.queryForObject( "SELECT FOUND_ROWS()", Long.class );
注意:
SQL_CALC_FOUND_ROWS和FOUND_ROWS()是依赖同一个数据库连接会话的,只要这两次调用在同一个线程(或同一个事务)里,JdbcTemplate会从连接池复用同一个连接,结果就是准确的。
办法三:用CallableStatement手动处理多结果集
如果你不想修改连接URL,也可以通过CallableStatement来手动执行多条语句并处理多个结果集,代码示例如下:
jdbcTemplate.execute(connection -> { try (CallableStatement cs = connection.prepareCall( "select SQL_CALC_FOUND_ROWS * from foo Limit 10; SELECT FOUND_ROWS()" )) { boolean hasMoreResults = cs.execute(); // 处理第一个结果集(分页数据) if (hasMoreResults) { try (ResultSet rs = cs.getResultSet()) { // 这里解析ResultSet成你的实体类列表 List<Foo> fooList = new ArrayList<>(); while (rs.next()) { Foo foo = new Foo(); // 给foo赋值,比如foo.setId(rs.getLong("id")); fooList.add(foo); } } } // 切换到第二个结果集(总行数) hasMoreResults = cs.getMoreResults(); if (hasMoreResults) { try (ResultSet rs = cs.getResultSet()) { if (rs.next()) { Long totalCount = rs.getLong(1); // 处理总行数 } } } return null; } });
不过这个方法同样需要开启allowMultiQueries=true参数才能生效,而且代码量比拆分执行要大,一般只在特殊场景下使用。
额外提醒
如果你的表数据量不大,其实也可以直接用select count(*) from foo来获取总行数,虽然性能比SQL_CALC_FOUND_ROWS略差,但胜在简单且不需要依赖会话状态。如果是大数据量表,那SQL_CALC_FOUND_ROWS还是更优的选择。
内容的提问来源于stack exchange,提问作者Shahid Ghafoor

