更新一堆行是基于事务还是基于行?
Updating a bunch of rows is it transaction based or row based?
我有一个 table 有一个需要不断重新计算的列,我希望这个 table 是可缩放的。用户也必须能够在上面书写。
没有服务器和并发用户很难测试这类东西,至少我不知道如何。
那么这两个选项中的一个可行吗?
@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,table 的引擎是 innoDB。
您应该在更新前使用悲观锁定来锁定修改的行,这样用户的手动修改就不会与后台更新发生冲突。如果您不使用锁定,您的用户的修改有时会回滚,如果它们与修改同一行的后台作业发生冲突。
此外,对于悲观锁定,如果您的用户的事务等待获取锁的时间超过发生超时的时间,您的用户可能会遇到回滚。为防止这种情况,您应该使所有使用悲观锁的事务尽可能短。因此,后台作业应该为每一行或每一小组行创建一个新事务,如果它可能 运行 比合理时间长。只有在事务完成后才会释放锁(用户将等待直到锁被释放)。
您的 MyService 的示例,运行在单独的事务中更新每个更新(实际上,您可以 运行 在单个事务中批量更新多个,传递列表或 ID 范围作为 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条件只使用id时,可以考虑em.find(row, MyEntity.class, LockModeType.PESSIMISTIC_WRITE).computeData()
而不是使用查询(在em.find()后添加空指针检查)
其他说明:
从问题中不清楚您是如何触发后台作业的。 运行 正如您在示例中所写的那样,无限的工作一方面不会创建额外的线程(当您在同一个 bean 上调用方法时,注释不会被递归地考虑)。另一方面,如果出现异常,你的后台作业至少应该处理异常,这样它才不会被停止。您可能还想在后续执行之间添加一些等待时间。
最好运行后台作业作为计划作业。一种可能的选择是 @Schedule
注释而不是 @Asynchronous
。您可以指定作业在后台执行的频率。然后检查你的工作的开始,之前的执行是否完成是很好的。 Java EE 7 的另一个选项是使用 ManagedScheduledExecutorService 以指定的时间间隔定期触发后台作业。
我有一个 table 有一个需要不断重新计算的列,我希望这个 table 是可缩放的。用户也必须能够在上面书写。
没有服务器和并发用户很难测试这类东西,至少我不知道如何。 那么这两个选项中的一个可行吗?
@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,table 的引擎是 innoDB。
您应该在更新前使用悲观锁定来锁定修改的行,这样用户的手动修改就不会与后台更新发生冲突。如果您不使用锁定,您的用户的修改有时会回滚,如果它们与修改同一行的后台作业发生冲突。
此外,对于悲观锁定,如果您的用户的事务等待获取锁的时间超过发生超时的时间,您的用户可能会遇到回滚。为防止这种情况,您应该使所有使用悲观锁的事务尽可能短。因此,后台作业应该为每一行或每一小组行创建一个新事务,如果它可能 运行 比合理时间长。只有在事务完成后才会释放锁(用户将等待直到锁被释放)。
您的 MyService 的示例,运行在单独的事务中更新每个更新(实际上,您可以 运行 在单个事务中批量更新多个,传递列表或 ID 范围作为 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条件只使用id时,可以考虑em.find(row, MyEntity.class, LockModeType.PESSIMISTIC_WRITE).computeData()
而不是使用查询(在em.find()后添加空指针检查)
其他说明:
从问题中不清楚您是如何触发后台作业的。 运行 正如您在示例中所写的那样,无限的工作一方面不会创建额外的线程(当您在同一个 bean 上调用方法时,注释不会被递归地考虑)。另一方面,如果出现异常,你的后台作业至少应该处理异常,这样它才不会被停止。您可能还想在后续执行之间添加一些等待时间。
最好运行后台作业作为计划作业。一种可能的选择是 @Schedule
注释而不是 @Asynchronous
。您可以指定作业在后台执行的频率。然后检查你的工作的开始,之前的执行是否完成是很好的。 Java EE 7 的另一个选项是使用 ManagedScheduledExecutorService 以指定的时间间隔定期触发后台作业。