【问题标题】:db table locked row and entityManager statedb 表锁定行和 entityManager 状态
【发布时间】:2012-07-26 23:41:29
【问题描述】:

我有一个会话范围类,其中包含一个用于管理用户统计信息的对象。当用户(通过 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();

这根本不会进行数据库调用。 我试过手动回滚事务,但没有任何乐趣。

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

预计到达时间:

我尝试引发 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();
    }
}

我该如何恢复?

【问题讨论】:

    标签: oracle hibernate seam entitymanager seam2


    【解决方案1】:

    首先,在Component.getInstance()中指定作用域并没有在指定作用域内创建组件的结果。 EntityManager 实例始终存在于对话上下文中(无论是临时的还是长期运行的)。 scopegetInstance() 参数的唯一目的是提示组件应该位于的上下文,以避免在所有上下文中进行昂贵的搜索(如果您不指定上下文或指定错误的上下文)。

    由于先前的错误,事务被标记为回滚。如果 entityManager 无论如何都要提交,则它不会是事务性的(事务实际上保证如果发生错误,则不会保留任何内容)。如果要将登录事务与统计信息收集隔离开来,最简单的解决方案是在异步事件中执行saveUserStatsToDb 方法(事务绑定到线程,因此使用不同的线程可以保证事件在单独的事务中处理)。

    类似这样的:

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

    在你的createUserStats 方法中:

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

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

    【讨论】:

    • 感谢您的回复,我更新了我的原始帖子。我确实解决了锁定条件,但我仍然需要找到解决方法,我在收集涉及持久性的异常数据时也遇到了同样的问题。
    猜你喜欢
    • 1970-01-01
    • 2021-02-17
    • 2012-12-09
    • 2021-05-24
    • 1970-01-01
    • 2011-06-26
    • 1970-01-01
    • 1970-01-01
    • 2012-03-08
    相关资源
    最近更新 更多