5

Hibernate (4.1.9.Final) 和 MySQL (14.14 Distrib 5.5.29, InnoDB table)我得到了一个非常奇怪的结果:

当我使用一个线程将某些内容保存到数据库并尝试使用另一个线程获取它时,Hibernate 并不总是找到该实体。(尽管我在打开加载会话之前正确地提交了事务并关闭了持久会话。)

一些观察:

  • 无法使用单线程程序重现这一点。

  • 可以在数据库中看到“丢失”的实体,并且

  • 如果我重新启动应用程序 Hibernate可以成功加载实体。

这是一个说明问题的 SSCCE。(为简洁起见,省略了进口):

public class StressTest {

    static SessionFactory sessionFactory;

    public static void main(String[] args) throws InterruptedException,
                                                     ExecutionException {

        // Configure Hibernate
        Configuration conf = new Configuration();
        conf.setProperty("hibernate.dialect",
                         "org.hibernate.dialect.MySQLInnoDBDialect");
        conf.configure();

        ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
                .applySettings(conf.getProperties())
                .buildServiceRegistry();        

        sessionFactory = conf.buildSessionFactory(serviceRegistry);

        // Set up producer / consumer
        BlockingQueue<Long> queue = new LinkedBlockingQueue<Long>();

        new Consumer(queue).start();
        new Producer(queue).start();
    }

}

class DummyEntity {
    long id;
    public long getId() { return id; }
    public void setId(long id) { this.id = id; }
}

生产者类(创建DummyEntities并持久化它们)。

class Producer extends Thread {

    BlockingQueue<Long> sink;

    public Producer(BlockingQueue<Long> sink) {
        this.sink = sink;
    }

    @Override
    public void run() {
        try {
            while (true) {

                Session session = StressTest.sessionFactory.openSession();

                DummyEntity entity = new DummyEntity();
                entity.setId(new Random().nextLong());

                session.beginTransaction();
                session.save(entity);
                session.getTransaction().commit();
                session.close();

                sink.put(entity.getId());
            }
        } catch (InterruptedException ignore) {
            System.exit(-1);
        }
    }
}

DummyEntities消费者类(从数据库加载):

class Consumer extends Thread {

    BlockingQueue<Long> source;

    public Consumer(BlockingQueue<Long> source) {
        this.source = source;
    }

    @Override
    public void run() {

        try {
            while (true) {

                long entityId = source.take();

                Session session = StressTest.sessionFactory.openSession();
                Object entity = session.get(DummyEntity.class, entityId);
                session.close();

                if (entity == null) {
                    System.err.printf("Entity with id %d NOT FOUND", entityId);
                    System.exit(-1);
                }
            }
        } catch (InterruptedException ignore) {
            System.exit(-1);
        }
    }
}

最后,这是DummyEntity.

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping
    default-cascade="all"
    default-lazy="false">

    <class name="se.stresstest.DummyEntity">

        <id name="id" type="long">
            <generator class="assigned"/>
        </id>

    </class>

</hibernate-mapping>

结果输出总是以类似以下内容结束:

Entity with id -225971146115345 NOT FOUND

任何想法为什么?

(这是上一个问题的精炼版本。)

4

2 回答 2

1

这种行为可以与 REPEATABLE READ 的事务隔离模式一致,这恰好是 InnoDB 的默认事务隔离模式:

http://dev.mysql.com/doc/refman/5.5/en/set-transaction.html#isolevel_repeatable-read

如果您的应用程序逻辑依赖于能够在当前事务启动查看其他事务中提交的数据- 以可重复读取为代价(数据可以/将由于其他事务中的操作而更改) - 您应该设置事务隔离阅读提交:

http://dev.mysql.com/doc/refman/5.5/en/set-transaction.html#isolevel_read-committed

于 2013-11-07T18:02:14.263 回答
0

改为使用MySQL5InnoDBDialect

于 2013-04-20T04:31:56.353 回答