我想知道在以下情况下组织我的类层次结构的正确方法是什么。
我想围绕postgresql的建议锁构建一个抽象。
仅供参考:建议锁是可以在会话中获得的锁。事务或的水平。Postgres为你处理所有的复杂性。
到目前为止我写的代码类似于interface DBLockService
interface SessionLockService : DBLockService {
fun acquire(id: Long)
fun unlock(id: Long): Boolean
}
interface TransactionalLockService : DBLockService {
fun txAcquire(id: Long)
}
abstract class BaseDBLockService(protected val entityManager: EntityManager): DBLockService {
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
}
@Component
class LockServiceImpl(
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 {
// very similar implementation
}
看看这段代码,有一些东西告诉我有问题:
DBLockService
是一个有点没用的接口,没有方法SessionLockService
和TransactionalLockService
只是一个实现细节吗?每种"类型"都有不同的界面,对吗?锁吗?
但同时,如果我删除DBLockService
对我来说似乎很奇怪,有两个接口(SessionLockService
和TransactionalLockService
)具有非常相似的上下文,它们以任何方式都不相关。
此外,删除DBLockService
,我将有两个实现(LockServiceImpl
和TransactionalLockServiceImpl
),从抽象类BaseDBLockService
扩展到实现这两个接口,但同时抽象类与它们无关。
你觉得呢?
谢谢
更新
根据要求,我将添加一个真实案例场景的示例
@Service
class SomethingService(private val lockService: TransactionalLockService){
@Transactional
fun aMethod(entityId: Long){
lockService.txAcquire(entityId)
//code to be synchronized or there will be problems
}
}
我想注入一个类的通用LockService
,但我找不到一种方法来抽象,因为imho一个锁,消失事务结束后的锁不同于与数据库连接关闭后消失的锁(会话锁),也不同于需要自动解锁的锁。
可能有很多锁的其他实现,例如TimeoutLock
在一段时间后删除锁。
但是我想不出如何将这些实现细节与Lock
的一般概念分开。
好的,谢谢你的例子。我仍然觉得把你想要实现的东西称为Service
有点奇怪。我可能会称之为Strategy
,但这并不是真正的重要。这只是一个语义偏好。
interface LockService {
Boolean acquire(Long id);
Boolean unlock(Long id);
}
abstract class BaseLockService
implements LockService {
protected EntityManager entityManager;
BaseLockService(EntityManager entityManager) {
this.entityManager = entityManager;
}
protected Boolean executeAcquire(String preparedStatement, Long id) {
// your generic implementation
}
}
class SessionLockService
extends BaseLockService {
private static class Statements {
static final String acquireStatement = "...";
static final String unlockStatement = "...";
}
SessionLockService(EntityManager entityManager) {
super(entityManager);
}
@Override
Boolean acquire(Long id) {
return executeAcquire(Statements.acquireStatement, id);
}
@Override
Boolean unlock(Long id) {
return executeAcquire(Statements.unlockStatement, id);
}
}
class TransactionalLockService
extends BaseLockService {
private static class Statements {
static final String acquireStatement = "...";
}
TransactionalLockService(EntityManager entityManager) {
super(entityManager);
}
@Override
Boolean acquire(Long id) {
return executeAcquire(Statements.acquireStatement, id);
}
@Override
Boolean unlock(Long id) {
// simply return true
return true;
// or if there's some Postgres or EntityManager mechanism to find out if the transaction is still active:
return !entityManager.isInTransaction(id);
}
}
class SomeService {
private final LockService lockService;
SomeService(LockService lockService) {
this.lockService = lockService;
}
void aMethod(Long entityId) {
if(!lockService.acquire(entityId)) {
throw new SomeException();
}
// do code that needs lock
if(!lockService.unlock(entityId)) {
throw new SomeException();
}
}
}
所以基本上,I将使用一个通用接口,只是使TransactionalLockService.unlock()
成为一个总是返回true
的无操作函数,或者,如果可以实现,更可取的是:返回一些探测机制的结果,以找出与id
的事务是否正确结束。
另一个想法是有一个Lock
接口,LockService
返回(非常简短的例子):
interface Lock {
Boolean unlock();
}
interface LockService {
Lock acquire(Long id);
}
class TransactionalLock
implements Lock {
private Long id;
TransactionalLock(Long id) {
this.id = id;
}
@Override
Boolean unlock() {
// again, either simply return true
return true;
// ...or some result that verifies the transaction has ended
return verifyTransactionHasEnded(id);
}
}
class SomeService {
private final LockService lockService;
SomeService(LockService lockService) {
this.lockService = lockService;
}
void aMethod(Long entityId) {
Lock lock = lockService.acquire(entityId);
if(lock == null) {
throw new SomeException();
}
// do code that needs lock
if(!lock.unlock()) {
throw new SomeException();
}
}
}
…等。,但这可能会变得非常复杂,因为Lock
的实现需要自己的机制来解锁自己。
这可能还不是你想要的,但希望它能给你一些想法。