JPA 实体仅在方法完成后更新
我有一个异步注释方法,它通过方法参数接收实体。在这个方法中,我尝试设置一个变量三次:
@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() 是否实际更改:
- 在 sleep() 期间,我检查数据库名称值是否更改
- 在网页上显示 MyEntity 对象的列表(带有名字),此列表每 2 秒更新一次。
上述两种方法都表明值“Joe 1”和“Joe 2”在数据库中较新。只有在 doSomething() 方法完成之后,姓氏集 (Joe 3) 才会被放入数据库中。
所以我的问题是:为什么值“Joe 1”和“Joe 2”没有放入数据库中,而只有最后一个值放入数据库中?
I have an Asynchronous annotated method which receives an entity though the method parameter. In this method I try to set a variable three times:
@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");
}
Edit: So thanks to PedroKowalski I have a better understanding of the problem and I will reformulate.
During the execution of the above method, I have two ways of checking if the myEntity.setName() is actually changed:
- During the sleep() I check the database if the name value is changed
- On a webpage the list of MyEntity objects are displayed (with the names), this list is being updated every 2 seconds.
Both of the above methods indicate that the values "Joe 1" and "Joe 2" are newer in the database. Only after the doSomething() method is done, the last name set (Joe 3) is being put in the database.
So my question is: why are the values "Joe 1" and "Joe 2" not put in the database and only the last value is put in the database?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果您使用 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 更改相同数据(相同实体)的情况。如果您没有锁定,则只有最后提交的事务更改才会反映在数据库中(因此前一个事务所做的更改将丢失)。通过锁定,您将在最后提交的事务中遇到异常,因此不会丢失任何数据。
悲观锁定可以在修改时锁定数据。在这种情况下,您的事务 T2 在 T1 完成之前不会对数据进行操作。
最后 - 最简单的情况是(如果可能)只是将你的方法分成更小的块。
HTH。
If you use JTA transactions than the transaction boundary spreads from method's begin to its end.
Therefore, changes made in an active transaction T1 can't be seen in transaction T2. If you think about it, it's quite reasonable.
Assume that T2 could operate on data which was changed by T1 but not committed. Upon T1 rollback, every changes made to the entities in T1 must be invalidated. You've ended in a situation in which T2 operated on invalid data.
That's why you won't see 'Joe 1' (this value is changed only in T1) from any transaction other than T1. You can only see 'Joe 2' when methods ends (T1 commits).
The EntityManager#flush() synchronizes the data with the underlying database but does not commit it. For more detailed information you can see this thread: http://www.java.net/node/665442#comment-678155
I can see three solutions in this case:
Optimistic locking could save you from situations in which two transactions T1 and T2 change the same data (the same entity). In case you have no locking, only the last committed transaction changes will be reflected in the database (so you've changes made by the former transaction are lost). With locking, you will get an Exception in the last committed transaction, so no data will be lost.
Pessimistic locking could lock the data for the time of modification. In this case, your transaction T2 would not operate on the data until T1 finishes.
At last - the most simple situation is (if possible) just to separate your method to smaller chunks.
HTH.
数据在em.flush()期间写入数据库。提交事务时,数据对其他用户/持久性上下文可见。
您不会通过 em.flush() 手动刷新到数据库,因此事务提交时会自动调用 em.flush()。
事务在方法结束时提交,因此也就是发生刷新时,意味着“Joe3”被写入数据库并且也被提交。
Data is written to the database during em.flush(). Data is visible to other users/persistence contexts when the transaction is commited.
You do not manually flush to the database via em.flush(), so em.flush() is automatically called when the transaction is committed.
The transaction is committed at the end of your method, so that is when the flush occurs, meaning "Joe3" is written to DB and is also committed.