我有一个需要不断重新计算的列的表,我希望这个表可以扩展。用户也必须能够在其上书写。
如果没有服务器和并发用户,很难测试这类事情,至少我不知道如何做。 这两个选项中有一个可行吗?
@ApplicationScoped
public class Abean {
@EJB
private MyService myService;
@Asynchronous
public void computeTheData(){
long i = 1;
long numberOfRows = myService.getCountRows(); // gives the number of row in the table
while(i<numberOfRows){
myService.updateMyRow(i);
}
computeTheData(); // recursion so it never stops, I'm wondering if this wouldn't spawn more threads and if it would be an issue.
}
}
public class MyService implements MyServiceInterface{
...
public void updateMyRows(int row){
Query query = em.createQuery("SELECT m FROM MyEntity WHERE m.id=:id");
Query.setParameter("id", row);
List<MyEntity> myEntities = (MyEntity) query.getResultList();
myEntity.computeData();
}
}
VS
@ApplicationScoped
public class Abean {
@EJB
private MyService myService;
@Asynchronous
public void computeTheData(){
myService.updateAllRows();
}
}
public class MyService implements MyServiceInterface{
...
public void updateAllRows(int page){
Query query = em.createQuery("SELECT m FROM MyEntity");
List<MyEntity> myEntities = (MyEntity) query.getResultList();
myEntity.computeData();
}
}
这是否可行?我使用的是mysql,表的引擎是innoDB。
答案 0 :(得分:1)
您应该在更新之前使用悲观锁定来锁定已修改的行,以便用户手动修改不会与后台更新冲突。如果您没有使用锁定,则有时会回滚用户的修改,如果它们与修改了同一行的后台作业发生冲突。
此外,如果使用悲观锁定,如果用户的事务等待获取锁定的时间超过超时,则用户可能会遇到回滚。为防止这种情况,您应该尽可能缩短使用悲观锁的所有事务。因此,后台作业应为每行或一小组行创建一个新事务,如果它可能运行的时间超过合理时间。只有在事务完成后才会释放锁(用户将等到锁被释放)。
MyService的样子示例,在单独的事务中运行每个更新(实际上,您可以在单个事务中批量运行多个更新,将list的列表或范围作为参数传递给updateMyRows):
public class MyService implements MyServiceInterface{
...
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) // this will create a new transaction when running this method from another bean, e.g. from Abean
public void updateMyRows(int row){
TypedQuery<MyEntity> query = em.createQuery(SELECT m FROM MyEntity WHERE m.id=:id", MyEntity.class);
query.setParameter("id", row);
query.setLockMode(LockModeType.PESSIMISTIC_WRITE); // this will lock all entities retrieved by the query
List<MyEntity> myEntities = query.getResultList();
if (!myEntities.isEmpty()) {
myEntities.get(0).computeData();
}
}
}
当你在where where条件中只使用id时,你可以考虑em.find(row, MyEntity.class, LockModeType.PESSIMISTIC_WRITE).computeData()
而不是使用查询(在em.find()之后添加空指针检查)
其他说明:
从问题中如何触发后台作业尚不清楚。正如您在示例中所写的那样,无限地运行作业一方面不会创建额外的线程(因为您在同一个bean上调用了methond,注释不会被递归地考虑)。另一方面,如果存在异常,则后台作业应该至少处理异常,以便不会停止。您可能还希望在后续执行之间添加一些等待时间。
最好将后台作业作为计划作业运行。一种可能的选项是@Schedule
注释,而不是@Asynchronous
。您可以指定作业将在后台执行的频率。然后,在作业开始时检查以前的执行是否结束是很好的。 Java EE 7的另一个选择是使用ManagedScheduledExecutorService以指定的时间间隔定期触发后台作业。