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

PostgreSQL advisory lock封装的类层级结构合理设计方案咨询

核心疑问解答

  1. 空的DBLockService确实没有实际作用,不需要为了所谓的「逻辑协调」硬加无共同行为的父接口,属于完全冗余的设计。
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 23:27:00