通过Querydsl直接更新数据后如何刷新Spring JPA的缓存?
Querydsl更新后同事务查询无法获取最新数据的解决方案
核心原因
Querydsl的update操作直接生成SQL在数据库层执行,不会自动同步JPA一级缓存(持久化上下文),如果对应实体已经被加载到缓存中,后续查询会直接命中缓存返回旧值,无法拿到更新后的数据。
可行解决方案
方案1:清除指定单实体的一级缓存(最轻量通用方案)
不需要清空整个一级缓存,仅将你更新过的单个实体从缓存中移除即可,性能损耗可忽略:
val qRecord = QCoupon.coupon // 执行原有更新逻辑 jpaQueryFactory.update(qRecord) .set(qRecord.useState, Coupon.STATE_ORDERED) .set(qRecord.useTime, useTime) .where(qRecord.id.eq(recordId)) .execute() // 新增:仅移除当前更新的Coupon实体缓存,不会影响其他缓存实体 entityManager.detach(entityManager.getReference(Coupon::class.java, recordId))
说明:getReference方法仅获取实体代理,不会触发额外的数据库查询。后续再查询该ID的Coupon时,会自动发起数据库请求拿到最新值。
方案2:更新时直接返回最新实体(适用于更新后立即需要使用实体的场景)
如果你使用的数据库支持RETURNING子句(PostgreSQL、MySQL 8.0+等主流数据库都支持),可以在更新时直接返回更新后的行,不需要额外查询,也无需处理缓存:
val qRecord = QCoupon.coupon val updatedCoupon = jpaQueryFactory.update(qRecord) .set(qRecord.useState, Coupon.STATE_ORDERED) .set(qRecord.useTime, useTime) .where(qRecord.id.eq(recordId)) .executeWithReturn(qRecord) .firstOrNull()
方案3:单次查询绕过一级缓存(适用于仅特定查询需要拿最新值的场景)
如果不想修改缓存内容,仅需要某一次查询拿到最新数据,可以给查询设置绕过缓存的Hint:
import javax.persistence.CacheRetrieveMode // 后续查询时添加Hint强制走数据库 val latestCoupon = jpaQueryFactory.selectFrom(QCoupon.coupon) .where(QCoupon.coupon.id.eq(recordId)) .setHint("javax.persistence.cache.retrieveMode", CacheRetrieveMode.BYPASS) .fetchOne()
内容的提问来源于stack exchange,提问作者Chenhe
相关产品推荐
相关产品推荐

