gpt4 book ai didi

multithreading - 使用 Hibernate SessionFactory 的多线程问题

转载 作者:行者123 更新时间:2023-12-01 06:46:18 26 4
gpt4 key购买 nike

有一个表“临时”..
代码:

CREATE TABLE `temp` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`student_id` bigint(20) unsigned NOT NULL,
`current` tinyint(1) NOT NULL DEFAULT '1',
`closed_at` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_index` (`student_id`,`current`,`closed_at`),
KEY `studentIndex` (`student_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

对应的 Java pojo 是 http://pastebin.com/JHZwubWd .该表具有唯一约束,使得每个学生只能有一个记录处于事件状态。

2)我有一个测试代码,它确实尝试为学生不断添加记录(每次将旧的事件记录设为非事件记录并添加新的事件记录),并且还在访问一些随机(非相关)表的不同线程中。
代码:
public static void main(String[] args) throws Exception {
final SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
ExecutorService executorService = Executors.newFixedThreadPool(1);
int runs = 0;
while(true) {
Temp testPojo = new Temp();
testPojo.setStudentId(1L);
testPojo.setCurrent(true);
testPojo.setClosedAt(new Date(0));
add(testPojo, sessionFactory);
Thread.sleep(1500);

executorService.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
Session session = sessionFactory.openSession();
// Some dummy code to print number of users in the system.
// Idea is to "touch" the DB/session in this background
// thread.
System.out.println("No of users: " + session.createCriteria(User.class).list().size());
session.close();
return null;
}
});
if(runs++ > 100) {
break;
}
}

executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.MINUTES);
}

private static void add(final Temp testPojo, final SessionFactory sessionFactory) throws Exception {
Session dbSession = null;
Transaction transaction = null;
try {
dbSession = sessionFactory.openSession();
transaction = dbSession.beginTransaction();

// Set all previous state of the student as not current.
List<Temp> oldActivePojos = (List<Temp>) dbSession.createCriteria(Temp.class)
.add(Restrictions.eq("studentId", testPojo.getStudentId())).add(Restrictions.eq("current", true))
.list();
for(final Temp oldActivePojo : oldActivePojos) {
oldActivePojo.setCurrent(false);
oldActivePojo.setClosedAt(new Date());

dbSession.update(oldActivePojo);
LOG.debug(String.format(" Updated old state as inactive:%s", oldActivePojo));
}
if(!oldActivePojos.isEmpty()) {
dbSession.flush();
}

LOG.debug(String.format(" saving state:%s", testPojo));
dbSession.save(testPojo);
LOG.debug(String.format(" new state saved:%s", testPojo));

transaction.commit();

}catch(Exception exception) {
LOG.fatal(String.format("Exception in adding state: %s", testPojo), exception);
transaction.rollback();
}finally {
dbSession.close();
}
}

运行代码后,运行几次后,我收到索引约束异常。发生这种情况是因为出于某种奇怪的原因,它没有找到最新的事件记录,而是找到一些较旧的陈旧事件记录,并尝试在保存之前将其标记为非事件状态(尽管数据库实际上已经存在一个新的事件记录)。

请注意,这两个代码共享相同的 sessionfactory,并且这两个代码在完全不同的表上工作。我的猜测是某些内部缓存状态变脏了。如果我为前台和后台线程使用 2 个不同的 sessionfactory,它工作正常。

另一个奇怪的事情是,在后台线程(我打印用户数)中,如果我将它包装在一个事务中(即使它只是一个读取操作),代码工作正常! Sp 看起来我需要将所有数据库操作(不管读/写)包装在一个事务中,以便它在多线程环境中工作。

有人可以指出这个问题吗?

最佳答案

是的,基本上,事务划分总是需要的:

Hibernate documentation说:

Database, or system, transaction boundaries are always necessary. No communication with the database can occur outside of a database transaction (this seems to confuse many developers who are used to the auto-commit mode). Always use clear transaction boundaries, even for read-only operations. Depending on your isolation level and database capabilities this might not be required, but there is no downside if you always demarcate transactions explicitly.



在尝试重现您的设置时,我遇到了由于缺乏事务划分(尽管与您的不同)而导致的一些问题。进一步调查显示,有时,根据连接池配置, add()在与之前的 call() 相同的数据库事务中执行.添加 beginTransaction()/ commit()call()解决了这个问题。这种行为可能会导致您的问题,因为取决于事务隔离级别, add()可以使用在事务开始时拍摄的数据库的陈旧快照,即在上一个 call() 期间.

关于multithreading - 使用 Hibernate SessionFactory 的多线程问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4976542/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com