我有一个Asynchronous注释方法,它通过方法参数接收实体。在这个方法中,我尝试设置一个变量三次:
@Inject EntityDao entityDao;
@Asynchronous
public Future<String> doSomething (MyEntity p_myEntity) {
MyEntity myEntity = entityDao.merge(p_myEntity); // change from detached to attached
// em.contains(myEntity) returns true
myEntity.setName("Joe 1"); // newer set in database
// A System.out.println(myEntity.getName()) does say "Joe 1"
try {
Thread.sleep(20*1000);
} catch () ...etc
myEntity.setName("Joe 2"); // newer set in database
// A System.out.println(myEntity.getName()) does say "Joe 2"
try {
Thread.sleep(20*1000);
} catch () ...etc
myEntity.setName("Joe 3"); // only one set in database
return new AsyncResult<>("done");
}
编辑:感谢PedroKowalski,我对这个问题有了更好的理解,我将重新制定。
在执行上述方法的过程中,我有两种检查myEntity.setName()是否实际更改的方法:
上述两种方法都表明数据库中的“Joe 1”和“Joe 2”值较新。只有在doSomething()方法完成后,才会将最后一个名称集(Joe 3)放入数据库中。
所以我的问题是:为什么值“Joe 1”和“Joe 2”没有放入数据库中,只有最后一个值放在数据库中?
答案 0 :(得分:5)
如果使用JTA事务,则事务边界从方法的开始扩展到结束。
因此,在事务T2中无法看到在活动事务T1中所做的更改。如果你考虑一下,这是非常合理的。 假设T2可以对T1改变但未提交的数据进行操作。在T1回滚时,对T1中的实体所做的每个更改都必须无效。你已经结束了T2对无效数据进行操作的情况。
这就是为什么你不会从T1以外的任何交易中看到'Joe 1'(此值仅在T1中更改)。当方法结束时(T1提交),你只能看到'Joe 2'。
EntityManager#flush()将数据与基础数据库同步,但不提交。有关更多详细信息,请参阅此主题:http://www.java.net/node/665442#comment-678155
在这种情况下,我可以看到三种解决方案:
乐观锁定可以帮助您避免两个事务T1和T2更改相同数据(同一实体)的情况。如果您没有锁定,则只有最后提交的事务更改将反映在数据库中(因此前一个事务所做的更改将丢失)。使用锁定,您将在上次提交的事务中获得异常,因此不会丢失任何数据。
悲观锁定可能会在修改时锁定数据。在这种情况下,在T1完成之前,您的事务T2将不会对数据进行操作。
最后 - 最简单的情况是(如果可能的话)只是将你的方法分成较小的块。
HTH。
答案 1 :(得分:0)
所以我的问题是:为什么没有输入值“Joe 1”和“Joe 2” 数据库,只有最后一个值放在数据库中?
在em.flush()期间将数据写入数据库。提交事务时,其他用户/持久性上下文可以看到数据。
您不能通过em.flush()手动刷新数据库,因此在提交事务时会自动调用em.flush()。
事务在方法结束时提交,因此当刷新发生时,意味着“Joe3”被写入DB并且也被提交。