PostgreSQL advisory lock封装的类层级结构合理设计方案咨询
核心疑问解答
- 空的
DBLockService确实没有实际作用,不需要为了所谓的「逻辑协调」硬加无共同行为的父接口,属于完全冗余的设计。 SessionLockService和TransactionalLockService不是实现细节,是面向业务的行为契约,单独定义非常合理:两种锁的使用规则、释放时机完全不同,业务方必须明确知道自己用的是哪种锁,否则很容易出现资源泄漏、同步失效等生产问题。
优化思路
- 遵循接口隔离原则,不为没有共同行为的接口强设父类
- 拆分「业务契约接口」和「公共实现抽象层」,两者不需要强绑定继承关系
- 可选增加锁门面服务,提供统一的锁获取入口,业务方指定锁类型即可获取对应实例,满足通用锁抽象的需求
改造后的代码结构
1. 业务契约层(给业务方直接依赖)
/** * 会话级 advisory 锁:需要手动加锁解锁,会话断开自动释放,适合跨事务场景 */ interface SessionLockService { fun acquire(id: Long) fun unlock(id: Long): Boolean // 可扩展tryAcquire等方法 } /** * 事务级 advisory 锁:加锁后事务提交/回滚自动释放,不需要手动解锁,适合事务内同步场景 */ interface TransactionalLockService { fun txAcquire(id: Long) } /** * 可选:超时自动释放锁契约,后续扩展用 */ interface TimeoutLockService { fun tryAcquire(id: Long, timeoutMs: Long): Boolean }
2. 公共实现抽象层(封装公共查询逻辑,和上层契约解耦)
abstract class BaseDBLockService(protected val entityManager: EntityManager) { protected fun executeAcquire(preparedStatement: String, id: Long) { executeAcquire<Any>(preparedStatement, id) } protected inline fun <reified T> executeAcquire(preparedStatement: String, id: Long) = entityManager .createNativeQuery(preparedStatement, T::class.java) .setParameter("id", id) .singleResult as T }
3. 具体实现层
@Component class SessionLockServiceImpl( entityManager: EntityManager ) : BaseDBLockService(entityManager), SessionLockService { companion object { const val acquireStatement = "SELECT pg_advisory_lock(:id)" const val unlockStatement = "SELECT pg_advisory_unlock(:id)" } override fun acquire(id: Long) { executeAcquire(acquireStatement, id) } override fun unlock(id: Long) = executeAcquire<Boolean>(unlockStatement, id) } @Component class TransactionalLockServiceImpl( entityManager: EntityManager ) : BaseDBLockService(entityManager), TransactionalLockService { companion object { const val acquireStatement = "SELECT pg_advisory_xact_lock(:id)" } override fun txAcquire(id: Long) { executeAcquire(acquireStatement, id) } }
4. 可选:通用锁门面(满足通用LockService注入需求)
enum class LockType { SESSION, TRANSACTIONAL, TIMEOUT } @Component class LockFacade( private val sessionLockService: SessionLockService, private val transactionalLockService: TransactionalLockService ) { fun <T> getLock(type: LockType): T { return when(type) { LockType.SESSION -> sessionLockService as T LockType.TRANSACTIONAL -> transactionalLockService as T else -> throw IllegalArgumentException("不支持的锁类型") } } }
使用示例
直接依赖对应契约(推荐,更安全,避免锁规则误用)
@Service class SomethingService(private val transactionalLockService: TransactionalLockService){ @Transactional fun aMethod(entityId: Long){ transactionalLockService.txAcquire(entityId) // 事务内同步逻辑 } }
用门面通用获取(适合需要动态切换锁类型的场景)
@Service class DynamicLockService(private val lockFacade: LockFacade){ fun dynamicLockMethod(entityId: Long, lockType: LockType){ when(lockType) { LockType.SESSION -> { val lock = lockFacade.getLock<SessionLockService>(LockType.SESSION) lock.acquire(entityId) try { // 业务逻辑 } finally { lock.unlock(entityId) } } LockType.TRANSACTIONAL -> { // 走事务锁逻辑 } } } }
优化收益
- 无冗余空接口,结构更清晰
- 业务方依赖明确,不会出现锁规则误用的问题
- 扩展性强:后续加新的锁类型只需要新增契约接口+实现类,不需要修改现有代码,符合开闭原则
- 公共逻辑统一封装在
BaseDBLockService中,避免重复代码
内容的提问来源于stack exchange,提问作者Taz
相关产品推荐
相关产品推荐

