按键名锁定数据库编辑

时间:2010-05-16 17:35:25

标签: java spring jpa concurrency

我需要阻止同时编辑数据库字段。用户正在对结构化数据字段执行推送操作,因此我想对操作进行排序,而不是简单地忽略一个编辑并采取第二个。

基本上我想做

synchronized(key name)
{
  push value onto the database field
}

并设置同步项目,以便一次只能对“键名称”进行一次操作。 (注意:我正在简化,并不总是一个简单的推动。)

执行此操作的粗略方法是全局同步,但这会使整个应用程序陷入困境。我需要做的就是使用相同的密钥对两个同时写入进行排序,这很少但很烦人。

这是一个基于Web的java应用程序,用Spring编写(并使用JPA / MySQL)。该操作由用户Web服务调用触发。 (根本原因是当用户使用相同的密钥发送两个同时的http请求时)。

我已经浏览了Doug Lea / Josh Bloch / et al Concurrency in Action,但没有看到明显的解决方案。尽管如此,这似乎很简单,我觉得必须有一种优雅的方式来做到这一点。

3 个答案:

答案 0 :(得分:2)

可能有一种简单的方法让您的数据库为您处理这个问题。在数据库方面,我无可否认地知识匮乏。取而代之的是,这是一种涉及为每个键名创建单独锁的方法。有一个存储库可以管理单个锁的创建/销毁,这些锁需要一个整个应用程序锁,但是只有在找到,创建或销毁单个键名锁时才持有该锁。 。为实际数据库操作保留的锁对于该操作中使用的键名是唯一的。

KeyLock类用于防止对单个密钥名称同时进行数据库操作。

package KeyLocks;

import java.util.concurrent.locks.Lock;

public class KeyLock
{
    private final KeyLockManager keyLockManager;
    private final String keyName;
    private final Lock lock;

    KeyLock(KeyLockManager keyLockManager, String keyName, Lock lock)
    {
        this.keyLockManager = keyLockManager;
        this.keyName = keyName;
        this.lock = lock;
    }

    @Override
    protected void finalize()
    {
        release();
    }

    public void release()
    {
        keyLockManager.releaseLock(keyName);
    }

    public void lock()
    {
        lock.lock();
    }

    public void unlock()
    {
        lock.unlock();
    }
}

KeyLockManager类是负责密钥锁生存期的存储库。

package KeyLocks;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class KeyLockManager
{
    private class LockEntry
    {
        int acquisitionCount = 0;
        final Lock lock = new ReentrantLock();
    }

    private final Map<String, LockEntry> locks = new HashMap<String, LockEntry>();
    private final Object mutex = new Object();

    public KeyLock getLock(String keyName)
    {
        synchronized (mutex)
        {
            LockEntry lockEntry = locks.get(keyName);
            if (lockEntry == null)
            {
                lockEntry = new LockEntry();
                locks.put(keyName, lockEntry);
            }
            lockEntry.acquisitionCount++;
            return new KeyLock(this, keyName, lockEntry.lock);
        }
    }

    void releaseLock(String keyName)
    {
        synchronized (mutex)
        {
            LockEntry lockEntry = locks.get(keyName);
            lockEntry.acquisitionCount--;
            if (lockEntry.acquisitionCount == 0)
            {
                locks.remove(keyName);
            }
        }
    }
}

以下是如何使用密钥锁的示例。

package test;

import KeyLocks.KeyLock;
import KeyLocks.KeyLockManager;

public class Main
{
    private static final String KEY_NAME = "TEST_KEY";

    public static void main(String[] args)
    {
        final KeyLockManager keyLockManager = new KeyLockManager();
        KeyLock keyLock = null;
        try
        {
            keyLock = keyLockManager.getLock(KEY_NAME);
            keyLock.lock();
            try
            {
                // Do database operation on the data with the specified key name
            }
            finally
            {
                keyLock.unlock();
            }
        }
        finally
        {
            if (keyLock != null)
            {
                keyLock.release();
            }
        }
    }
}

答案 1 :(得分:1)

即使您锁定了密钥,也无法确保下次密钥是相同的(不仅是相同的)密钥。您需要的是数据库完成的select for update。如果无法做到这一点,您需要使用一组同步锁定密钥作为存储库/ dao的成员来自行编程锁定密钥。

答案 2 :(得分:0)