3

我需要实现一个解决方案来为不同类型但相同类型(相同类,相同表)的对象生成序列号。更重要的是,序列生成的规则是在运行时定义的(起始序列、最大数量等)。我正在使用 MySQL,休眠 MySQL5Dialect 不支持序列生成,因此我选择使用序列表来实现此功能,其中每一行都是不同类型对象的序列:

+--------------------+--------------+------+-----+---------+-------+
| Field              | Type         | Null | Key | Default | Extra |
+--------------------+--------------+------+-----+---------+-------+
| seqenceName        | varchar(255) | NO   | PRI | NULL    |       | 
| nextVal            | bigint(20)   | NO   |     | NULL    |       | 
+--------------------+--------------+------+-----+---------+-------+

我创建了一个增加值的 dao:

public synchronized long getNextValue(String seqenceName) throws MySequenceNotFoundException {

        MySequence seq = findByID(seqenceName);

        if (seq == null) {
            throw new MySequenceNotFoundException("Sequence does not exist with name: " + seqenceName);
        }//if not exists

        long nextVal = seq.getNextVal();
        getCurrentSession().saveOrUpdate(seq);

        return nextVal;
    }

这从服务层调用为:

@Transactional(readOnly=false, propagation=Propagation.REQUIRES_NEW, isolation=Isolation.SERIALIZABLE)
    public synchronized long incSequence(String seqName) throws MySequenceNotFoundException {

        getCurrentSession().getTransaction().begin();

        MySequence seq = sequenceDao.findByID(seqName);

        LockRequest lockRequest = getCurrentSession().buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE));
        lockRequest.lock(seq);

        long l = sequenceDao.getNextValue(seqName);
        getCurrentSession().getTransaction().commit();

        return l;
    }

我尝试了一切:将隔离级别设置为Isolation.SERIALIZABLE,以编程方式在方法内提交事务,向其中添加synchronized关键字,还添加了锁定请求,但我认为它已经过时了,因为已经设置了隔离级别。

尽管如此,如果我创建 100 个线程并从每个线程调用此方法 60 次,结果是该nextVal列的值约为 4000 而不是 6000。

我怀疑我在这里遗漏了一些非常基本的东西,但找不到按预期工作需要什么。

感谢您的任何提示!

4

3 回答 3

2

使用select ... for update,Hibernate 支持它 -在这里查看我的答案

于 2014-07-04T22:15:11.363 回答
1

似乎答案在于一个相关的问题:how-to-use-hibernate-in-a-multi-threaded-application

我唯一错过的是getCurrentSession().flush()每次增加计数器时都调用,因为我没有考虑的是我的服务层为测试客户端创建的每个线程使用单独的会话,因此如果不刷新会话我无法同步计数器线程之间。

于 2014-07-04T19:36:51.650 回答
1

它看起来像原子变量问题:) 请查看http://docs.oracle.com/javase/tutorial/essential/concurrency/atomicvars.html并尝试使用 AtomicInterger 而不是 int。

于 2014-07-04T11:53:19.863 回答