You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring CRUD Repository为何count返回long,更新/删除仅支持int?

问题分析与解决

为什么@Modifying查询不能返回Long?

这是因为JPA规范中Query.executeUpdate()方法的返回类型就是int,Spring Data严格遵循这个规范,所以被@Modifying注解标记的修改类查询(delete/update)只能使用void、int或Integer作为返回类型,这就是你看到报错的直接原因。

早期JPA设计时,认为大部分场景下修改操作的受影响行数不会超过int的最大值(约21亿),所以采用int作为返回类型;而count()方法返回Long是因为统计全量数据时,很容易突破int的范围,Spring Data特意做了适配来避免溢出问题——这是两种不同场景的差异化设计,并非前后不一致。

解决方法

如果你的业务场景确实需要获取超过int范围的受影响行数,可以尝试以下方案:

方案1:分两步操作(非原子)

先执行count查询获取符合条件的记录数(返回Long),再执行删除操作。注意这两步不是原子操作,中间如果有其他线程修改数据,count结果和实际删除行数会有差异,需要结合业务场景判断是否可接受:

// 先统计符合条件的记录数
@Query(nativeQuery = true, value = "select count(*) from AUDIT_CREATE_SAVINGS_EASY_ACCESS_ACCOUNT where timestamp < :cutOff")
fun countByTimestampBefore(@Param("cutOff") cutOff: Instant): Long

// 再执行删除操作
@Modifying
@Query(nativeQuery = true, value = "delete from AUDIT_CREATE_SAVINGS_EASY_ACCESS_ACCOUNT where timestamp < :cutOff")
fun deleteByTimestampBefore(@Param("cutOff") cutOff: Instant): Int

方案2:使用原生JDBC获取精确原子行数

通过JdbcTemplate执行删除,并利用数据库的内置函数获取受影响行数(比如MySQL的ROW_COUNT()),这样可以拿到Long类型的结果,且整个操作是原子的:

@Autowired
private lateinit var jdbcTemplate: JdbcTemplate

fun deleteAndGetRowCount(cutOff: Instant): Long {
    // 执行删除语句
    jdbcTemplate.update("delete from AUDIT_CREATE_SAVINGS_EASY_ACCESS_ACCOUNT where timestamp < ?", cutOff)
    // 查询本次操作的受影响行数
    return jdbcTemplate.queryForObject("SELECT ROW_COUNT()", Long::class.java) ?: 0
}

注意不同数据库获取受影响行数的语法不同:比如PostgreSQL需要用GET DIAGNOSTICS row_count = ROW_COUNT,需要根据你的数据库类型调整。

方案3:升级Spring Data版本(适配大数值返回)

从Spring Data JPA 2.4版本开始,官方对@Modifying查询的返回类型做了扩展,支持Long/long作为返回类型(底层通过适配不同数据库驱动来处理超大数值场景)。如果你的项目版本较低,可以尝试升级到2.4+版本,这样你原来定义的方法就可以直接正常使用了。


内容的提问来源于stack exchange,提问作者Mr. Tomato

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 06:20:51