spring boot 可以在数据库事务内发布事件,并用 @transactionaleventlistener(after_commit) 在提交成功后删除对应 redis 键。事务回滚时,监听器不会执行清理。

在写入事务中发布商品变更事件
示例使用 Spring Boot 3、JDBC 和 Spring Data Redis。调用方应经由 Spring 管理的服务 Bean 调用。
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
record ProductChanged(long id) {}
@Service
class ProductService {
private final JdbcTemplate jdbc;
private final ApplicationEventPublisher events;
ProductService(JdbcTemplate jdbc, ApplicationEventPublisher events) {
this.jdbc = jdbc;
this.events = events;
}
@Transactional
public void rename(long id, String name) {
int rows = jdbc.update("UPDATE products SET name = ? WHERE id = ?", name, id);
if (rows != 1) throw new IllegalArgumentException("Update did not affect one row");
events.publishEvent(new ProductChanged(id));
}
}

应用还需确保数据库事务管理器覆盖这条 JDBC 写入。
提交后删除与读路径一致的缓存键
把监听器放在同包的另一个文件中,使用与读路径相同的键规则。
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;
@Component
class ProductCacheListener {
private final StringRedisTemplate redis;
ProductCacheListener(StringRedisTemplate redis) {
this.redis = redis;
}
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void evict(ProductChanged event) {
redis.delete("product:" + event.id());
}
}
Redis 删除失败时,数据库已经提交。生产环境可结合 TTL、监控和可重试的失效机制处理。并发读仍可能把旧值回填。
分别验证提交和回滚
测试前在隔离 Redis 写入旧缓存。通过实际服务调用成功更新后检查键是否消失,再用故意回滚的测试事务对照。
redis-cli SET product:42 old EX 60 redis-cli GET product:42
成功更新后第二条命令应返回 nil。回滚场景中缓存应继续存在。验证时避免外层测试事务始终不提交。











