考虑以下情况:我们收到来自web服务的请求,该服务更新我们的实体。有时我们可能(几乎)同时收到两个请求。我们遇到的情况是,由于并发更新,我们的实体看起来完全错误。这样做的想法是将实体锁定为悲观的,这样每当第一个请求出现时,它就会立即锁定实体,而第二个请求就无法触摸它(乐观锁定对我们来说是没有选择的)。我编写了一个集成测试来检查这种行为。
我得到了一个集成测试,如下所示:
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();
}它叫豆子
@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 second try.");上使用断点启动了一次集成测试,然后再次启动集成测试,那么后者仍然可以读取MyEntity。值得注意的是,它们是不同的实例(我在调试模式下对instanceId做了这样的观察)。这表明entityManager没有共享他的hibernate上下文。
我提出以下意见:
entity上的setter并将其保存到db时,锁就会被查询。但这不是我需要的。我需要锁而不修改实体。entityManager.lock(entity, LockModeType.PESSIMISTIC_WRITE)方法,但是行为是一样的。我如何读取悲观的对象,这意味着:每当我从db加载一个实体时,我都希望它立即被锁定(即使没有修改)。
发布于 2018-02-15 13:01:36
您是否尝试过在应用服务器中设置隔离级别?
要获得行上的锁,无论以后要做什么(读/写),都需要将隔离级别设置为TRANSACTION_SERIALIZABLE。
发布于 2018-02-14 08:53:17
你描述的两种方式。
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发布于 2018-02-05 10:40:54
只有当另一个线程已经持有锁时,锁才会失败。您可以在DB中的单行上使用两个FOR UPDATE锁,所以这不是JPA特有的东西。
https://stackoverflow.com/questions/48618870
复制相似问题