当前位置: 首页 > 知识库问答 >
问题:

jpa-spring EntityManager.Find超时不工作

云宏儒
2023-03-14

我试图在我的spring boot应用程序中用jpa和db作为MySQL实现悲观锁。我的目标是让一个存储库首先从db获取一行,然后对其设置锁。当此事务运行时,任何人都不能读取同一行。以下是我实现的代码

@Repository
@Transactional
public class UserRepo {

@PersistenceContext
private EntityManager entityManager;

/**
 *
 * @param token
 * @param data
 * @return
 */
public boolean lockUser(String token, int data) {
        Map<String, Object> props = new HashMap<String, Object>();
    props.put("javax.persistence.query.timeout", 0);
        User usr = entityManager.find(User.class, token, LockModeType.PESSIMISTIC_WRITE, props);
        System.out.println("BEFOREE LOCK = " + 
        Thread.currentThread().getId() + " user="+usr.getPlayerBalance());

        entityManager.lock(usr, LockModeType.PESSIMISTIC_WRITE, props);
        System.out.println("AFTER LOCK = " + Thread.currentThread().getId());
        if (data>2) {
            System.out.println("IN IF BEFORE SLEEP Thread = " + Thread.currentThread().getId());
            Thread.sleep(90000);
            System.out.println("IN IF AFTER SLEEP Thread = " + Thread.currentThread().getId());
        } else {
            System.out.println("IN ELSE Thread = " + Thread.currentThread().getId());
        }  return false;
      }
 }

现在,当我运行它时,当第一个请求come with data>3时,这个提取行,然后锁定行,线程Hibernate90秒。现在,当第二个请求带有data=1时,线程等待锁(em.find-具有超时为0毫秒的悲观锁)。现在,理想情况下,它应该抛出异常,因为我已经将timeout设置为0。但是第二个线程并没有立即抛出exprection,而且线程从db中读取行然后等待。

共有1个答案

农存
2023-03-14

lockmodetype.pessimistic_write用于锁定行,这很容易测试。

我对UserRepo进行了一点调整,以:

@Repository
public class UserRepo {

    @PersistenceContext
    private EntityManager entityManager;

    @Transactional
    public void lockUser(final Long id, final boolean wait) throws InterruptedException {
        entityManager.clear(); // be sure there is nothing in the cache, actually the threads don't share first level cache

        final Map<String, Object> props = new HashMap<String, Object>();
        props.put("javax.persistence.query.timeout", 0);

        System.out.println("Thread " + Thread.currentThread().getId() + " EXECUTES SELECT FOR UPDATE");
        entityManager.find(User.class, id, LockModeType.PESSIMISTIC_WRITE, props);

        if (wait) {
            System.out.println("Thread " + Thread.currentThread().getId() + " started blocking!");
            Thread.sleep(10000);
            System.out.println("Thread " + Thread.currentThread().getId() + " finished blocking!");
        }

        System.out.println("Thread " + Thread.currentThread().getId() + " FINISHED QUERY");
    }
}

我为那次回购创建了一个(不漂亮但功能强大的)测试:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringRunner.class)
@Transactional
@SpringBootTest
public class UserRepoTests {

    @Autowired
    private UserRepo userRepo;

    @Test
    public void testSelectForUpdate() throws InterruptedException {

        final Runnable requestOne = () -> {
            try {
                userRepo.lockUser(1L, true); // this one should wait and block the others
            } catch (InterruptedException e) {
            }
        };

        final Runnable requestTwo = () -> {
            try {
                userRepo.lockUser(1L, false);
            } catch (InterruptedException e) {
            }
        };

        final Runnable requestThree = () -> {
            try {
                userRepo.lockUser(1L, false);
            } catch (InterruptedException e) {
            }
        };

        final Thread threadOne = new Thread(requestOne);
        threadOne.start();

        Thread.sleep(1000); // give the first one some time to start

        final Thread threadTwo = new Thread(requestTwo);
        threadTwo.start();
        final Thread threadThree = new Thread(requestThree);
        threadThree.start();

        Thread.sleep(20000); // wait before destroying context
    }

}
Thread 16 EXECUTES SELECT FOR UPDATE
Hibernate: select user0_.id as id1_31_0_, user0_.player_balance as player_b2_31_0_ from "user" user0_ where user0_.id=? for update
Thread 16 started blocking!
Thread 17 EXECUTES SELECT FOR UPDATE
Hibernate: select user0_.id as id1_31_0_, user0_.player_balance as player_b2_31_0_ from "user" user0_ where user0_.id=? for update
Thread 18 EXECUTES SELECT FOR UPDATE
Hibernate: select user0_.id as id1_31_0_, user0_.player_balance as player_b2_31_0_ from "user" user0_ where user0_.id=? for update
Thread 16 finished blocking!
Thread 16 FINISHED QUERY
Thread 17 FINISHED QUERY
Thread 18 FINISHED QUERY

QueryTimeoutException:查询花费的时间比指定的超时时间长(请参见javax.persistence.query.timeout-该属性是一个提示,可能不会被跟随)

或也在同一页上:

timeout查询以毫秒为单位的超时(整数或字符串),这是Hibernate使用的一个提示,但需要底层数据库的支持(TODO是100%为真,还是我们使用了一些其他技巧)。

 类似资料:
  • 我想我已经阅读了关于堆栈溢出的所有Selenium超时问题,但是在我的Selenium webdriver 2.25(Python 2.7绑定)中,隐式超时和显式超时都不起作用,而且“no_timeout_here=”行都将永远挂起-- 所有指针将非常感谢! 10月16日更新 我可以知道您的OS/Python版本吗?

  • 我试图在JPA中通过Hibernate3对Postgres数据库使用悲观锁定。我无法使锁超时--它似乎永远挂着。 这里有一个例子: 按照我的理解,em2应该尝试最多5秒(5000ms)来获得锁,然后应该抛出一个异常。相反,代码变成死锁。 谢谢,阿拉斯泰尔

  • 我是新的完全未来。我试图为元素列表(即参数)调用并行方法,然后将结果组合起来创建最终响应。我还试图设置50毫秒的超时,以便如果调用不返回50毫秒,我将返回默认值。 到目前为止,我已经尝试过: 但我一直得到错误说: 有人能告诉我我在这里做错了什么吗?如果我走错了方向,请纠正我。 谢谢

  • 我创建的VPC具有如下的公共和私有子网: < li >公共子网:堡垒服务器、弹性负载平衡器、igw < li >专用子网:ec2实例(应用程序使用http 8080端口运行),nat网关 以下是监听器配置。 < li >负载平衡器协议:http < li >负载平衡器端口:80 < li >实例协议:http < li >实例端口:8080 当我尝试使用命令wget-O-http://elb-xx

  • 问题内容: 我正在尝试在JPA中使用悲观锁定,而不是针对Postgres数据库使用Hibernate 3。我无法超时锁定-它似乎永远挂着。 这是一个例子: 据我了解,em2应该尝试长达五秒钟(5000毫秒)来获取锁,然后应该抛出异常。而是代码陷入僵局。 如果我在两个不同的线程中运行它,那么我会看到线程2(带有em2)在线程1(em1)释放它后立即获得了锁。因此锁定正在发生,只是永不超时。 我用PE

  • 如何配置可见性超时,以便可以再次读取SQS中的消息? 我将Amazon SQS作为消息队列。消息由多个应用程序发送。我现在使用Spring listener读取队列中的消息,如下所示: 类实现了进一步使用了方法。 我还配置了一个调度器,在一段时间后再次读取队列。它使用