gpt4 book ai didi

java - 使用 EntityManager 对实体设置悲观锁

转载 作者:行者123 更新时间:2023-11-30 06:18:47 30 4
gpt4 key购买 nike

考虑以下情况:我们收到来自网络服务的请求,该请求更新我们的实体。有时我们可能(几乎)同时收到两个请求。由于并发更新,我们的实体看起来完全错误。这个想法是悲观地锁定实体,这样每当第一个请求到来时,它就会立即锁定实体,而第二个请求就无法触及它(乐观锁定对我们来说是没有选择的)。我编写了一个集成测试来检查此行为。

我得到了如下所示的集成测试:

protected static TestRemoteFacade testFacade;

@BeforeClass
public static void setup() {
testFacade = BeanLocator.lookupRemote(TestRemoteFacade.class, TestRemoteFacade.REMOTE_JNDI_NAME, TestRemoteFacade.NAMESPACE);
}

@Test
public void testPessimisticLock() throws Exception {
testFacade.readPessimisticTwice();
}

调用 bean

@Stateless
@Clustered
@SecurityDomain("myDomain")
@RolesAllowed({ Roles.ACCESS })
public class TestFacadeBean extends FacadeBean implements TestRemoteFacade {

@EJB
private FiolaProduktLocalFacade produkt;

@Override
public void readPessimisticTwice() {
produkt.readPessimisticTwice();
}
}

其中 produkt 本身就是一个 bean

@Stateless
@Clustered
@SecurityDomain("myDomain")
@RolesAllowed({ Roles.ACCESS })
public class ProduktFacadeBean implements ProduktLocalFacade {

@Override
public void readPessimisticTwice() {
EntityManager entityManager = MyService.getCrudService().getEntityManager();
System.out.println("Before first try.");
entityManager.find(MyEntity.class, 1, LockModeType.PESSIMISTIC_WRITE);
System.out.println("Before second try.");
entityManager.find(MyEntity.class, 1, LockModeType.PESSIMISTIC_WRITE);
System.out.println("After second try.");
}
}

public class MyService {

public static CrudServiceLocalFacade getCrudService() {
return CrudServiceLookup.getCrudService();
}

}

public final class CrudServiceLookup {

private static CrudServiceLocalFacade crudService;

private CrudServiceLookup(){
}

public static CrudServiceLocalFacade getCrudService() {
if (crudService == null)
crudService = BeanLocator.lookup(CrudServiceLocalFacade.class, CrudServiceLocalFacade.LOCAL_JNDI_NAME);
return crudService;
}

public static void setCrudService(CrudServiceLocalFacade crudService) {
CrudServiceLookup.crudService = crudService;
}

}

@Stateless
@Local(CrudServiceLocalFacade.class)
@TransactionAttribute(TransactionAttributeType.MANDATORY)
@Interceptors(OracleDataBaseInterceptor.class)
public class CrudServiceFacadeBean implements CrudServiceLocalFacade {

private EntityManager em;

@Override
@PersistenceContext(unitName = "persistence_unit")
public void setEntityManager(EntityManager entityManager) {
em = entityManager;
}

@Override
public EntityManager getEntityManager() {
return em;
}

}

现在出现的问题是:如果我在 System.out.println("Before secondary try."); 处断点启动一次集成测试,然后再次启动集成测试此时,后者仍然可以读取 MyEntity。值得注意的是,它们是不同实例(我在 Debug模式下对instanceId进行了观察)。这表明 entityManager 没有共享他的 hibernate 上下文。

我做了以下观察:

  • 每当我在 entity 上调用 setter 并将其保存到数据库时,就会获取锁。但这不是我需要的。我需要锁而不修改实体。
  • 我也尝试了 entityManager.lock(entity, LockModeType.PESSIMISTIC_WRITE) 方法,但行为是相同的。
  • 我在 DBVisualizer 中找到了事务设置。目前它设置为 TRANSACTION_NONE。我也尝试了所有其他方法(TRANSACTION_READ_UNCOMMITTED、TRANSACTION_READ_COMMITTED、TRANSACTION_REPEATABLE_READ、TRANSACTION_SERIALIZABLE),但没有成功。
  • 让第一个线程读取实体,然后第二个线程读取相同的实体。让第一个步骤修改实体,然后第二个步骤修改它。然后让双方都保存实体,谁最后保存实体就获胜,并且不会抛出异常。

如何读取一个悲观的对象,这意味着:每当我从数据库加载一个实体时,我希望它立即被锁定(即使没有修改)。

最佳答案

这两种方式都可以描述 ie。

  • em.find(MyEntity.class, 1, LockModeType.PESSIMISTIC_WRITE)
  • em.lock(entity, LockModeType.PESSIMISTIC_WRITE)

在数据库中的相关行上持有锁,但仅在entityManager的生命周期内,即。对于封闭事务的时间,一旦到达事务结束,锁就会自动释放

@Transactional()
public void doSomething() {
em.lock(entity, LockModeType.PESSIMISTIC_WRITE); // entity is locked
// any other thread trying to update the entity until this method finishes will raise an error
}
...
object.doSomething();
object.doSomethingElse(); // lock is already released here

关于java - 使用 EntityManager 对实体设置悲观锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48618870/

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