db表锁定行和entityManager状态

时间:2012-07-26 23:41:30

标签: oracle hibernate seam entitymanager seam2

我有一个会话作用域类,其中包含一个用于管理用户统计信息的对象。当用户登录(通过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正在标记事务以进行回滚,之后的任何db调用都会返回上述错误。我最初在应用程序范围内保存了用户统计信息 - 因此当行锁定时,它锁定了整个应用程序的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调用。 我试过手动回滚交易,但没有快乐。

当我在具有在会话范围级别使用的数据的表中锁定行时,结果几乎不会是灾难性的 - 没有数据被保存但是它会恢复。

ETA:

我尝试了一个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处理程序。

再次签署ETA:

所以,不是很奇怪,我已经将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();
    }
}

如何从中恢复?

1 个答案:

答案 0 :(得分:0)

首先,在Component.getInstance()中指定范围不具有在指定范围内创建组件的结果。 EntityManager个实例始终存在于对话上下文中(无论是临时的还是长时间运行的)。 scope getInstance()参数的唯一目的是暗示组件应该在其中的上下文,以避免在所有上下文中进行昂贵的搜索(如果您未指定上下文或指定错误的上下文。)

由于上一个错误,事务正在标记为回滚。如果entityManager无论如何都要提交,那么它就不是事务性的(事实上事务保证如果发生错误,则不会保留任何内容)。如果要将登录事务与stats收集隔离,最简单的解决方案是在异步事件中执行saveUserStatsToDb方法(事务绑定到线程,因此使用不同的线程保证事件在单独交易)。

这样的事情:

@Observer("saveUserStatsEvent")
@Transactional
public void saveUserStatsToDb(UserStats stats) {
    ((EntityManager)Component.getInstance("entityManager")).persist(stats);
}

并使用createUserStats方法:

Events.instance().raiseAsynchronousEvent("saveUserStatsEvent", this.userStats);

但是,这只是通过将事务分成两部分来解决问题。您真正想要解决的问题是问题基础上的锁定条件。