我有一个会话范围类,其中包含一个用于管理用户统计信息的对象。当用户(通过 SSO)登录时,应用程序范围的方法会检查表中的活动会话 - 如果发现任何会话,则使用表中的会话 ID 使会话无效。
在会话范围类中的 userStats 表中添加了一行:
/**
* get all the info needed for collecting user stats, add userStats to the users session and save to userStats table
* this happens after session is created
* @param request
*/
private void createUserStats(HttpServletRequest request){
if (!this.sessionExists) {
this.userStats = new UserStats(this.user, request.getSession(true)
.getId(), System.getProperty("atcots_host_name"));
request.getSession().setAttribute("userstats", this.userStats);
Events.instance().raiseEvent("userBoundToSession", this.userStats);
this.sessionExists = true;
log.info("user " + this.user + " is now logged on");
// add this to the db
try {
this.saveUserStatsToDb();
} catch (Exception e) {
log.error("could not save " + this.userStats.getId().getPeoplesoftId() + " information to db");
e.printStackTrace();
}
}
}
当用户的会话被销毁时,该行会更新一个注销时间。
由于我无法解释或重复的原因,过去 2 周内有 2 个用户登录并锁定了该行。当这种情况发生时,该用户的任何数据库调用都不再可能,并且该应用程序对于该用户实际上是不可用的。
[org.hibernate.util.JDBCExceptionReporter] (http-127.0.0.1-8180-3) SQL Error: 0, SQLState: null
2012-07-26 18:45:53,427 ERROR [org.hibernate.util.JDBCExceptionReporter] (http-127.0.0.1-8180-3) Transaction is not active: tx=TransactionImple < ac, BasicAction: -75805e7d:3300:5011c807:6a status: ActionStatus.ABORT_ONLY >; - nested throwable: (javax.resource.ResourceException: Transaction is not active: tx=TransactionImple < ac, BasicAction: -75805e7d:3300:5011c807:6a status: ActionStatus.ABORT_ONLY >)
这些统计数据的收集很重要,但不是生死攸关,如果我无法获得我想放弃并继续前进的信息。但这并没有发生。发生的事情是 entityManager 将事务标记为回滚,之后的任何数据库调用都会返回上述错误。我最初将用户统计信息保存在应用程序范围内 - 因此,当行锁定时,它锁定了整个应用程序的 entityManager(这并没有很好地解决)。当我将该方法移动到会话范围时,它只会锁定有问题的用户。
I tried setting the entityManger to a lesser scope(I tried EVENT and METHOD):
((EntityManager) Component.getInstance("entityManager", ScopeType.EVENT)).persist(this.userStats);
((EntityManager) Component.getInstance("entityManager", ScopeType.EVENT)).flush();
这根本不会进行 db 调用。我试过手动回滚交易,但没有任何乐趣。
当我锁定表中包含在对话范围级别使用的数据的行时,结果几乎没有灾难性 - 没有数据被保存但它恢复了。
预计到达时间:
我尝试引发一个 AsynchronousEvent - 它在本地工作,但部署到我们的远程测试服务器 - 这很奇怪 - 我得到:
DEBUG [org.quartz.core.JobRunShell] (qtz_Worker-1) Calling execute on job DEFAULT.2d0badb3:139030aec6e:-7f34
INFO [com.mypkg.myapp.criteria.SessionCriteria] (qtz_Worker-1) observing predestroy for seam
DEBUG [com.mypkg.myapp.criteria.SessionCriteria] (qtz_Worker-1) destroy destroy destroy sessionCriteria
ERROR [org.jboss.seam.async.AsynchronousExceptionHandler] (qtz_Worker-1) Exeception thrown whilst executing asynchronous call
java.lang.IllegalArgumentException: attempt to create create event with null entity
at org.hibernate.event.PersistEvent.<init>(PersistEvent.java:45)
at org.hibernate.event.PersistEvent.<init>(PersistEvent.java:38)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:619)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:623)
...
奇怪的是,它似乎正在通过 Quartz 处理程序。
再次预计到达时间:
所以,并不奇怪,我将 Quartz 设置为异步处理程序——我认为它只是用于调度作业。此外,异步方法无法访问会话上下文,因此我必须向我的观察方法添加一个参数才能真正拥有一个要持久的对象:
@Observer("saveUserStatsEvent")
@Transactional
public void saveUserStatsToDb(UserStats userstats) throws Exception {
if(userstats != null){
log.debug("persisting userstats to db");
this.getEntityManager().persist(userstats);
this.getEntityManager().flush();
}
}
我该如何从中恢复?