【问题标题】:Hibernate transactions does not start until the MVC request is done在 MVC 请求完成之前,休眠事务不会启动
【发布时间】:2013-05-31 23:18:32
【问题描述】:

我正在使用 Spring MVC 3 和 Hibernate 3.6,使用 Spring tx 来管理休眠事务,

现在我正在向控制器发出 ajax 请求,当控制器返回值时,我会导航到另一个页面,并且我会一直检查控制器,直到返回值为止。

这个控制器方法调用也有一些数据库事务要做,我想做的是在每件事都完成并且数据库tx完成并且一切都很好时进行导航,但是发生的是hibernate的persist()或 save() 方法退出但事务未启动。

我调试了代码,发现 spring 对事务进行了某种排队,直到对控制器的请求完成,这意味着它实际上并没有执行事务,而是说方法保存已完成,并将事务排队以后再做。

编辑

这是我的相关代码

           $("#kse_search").click(function (e){
            $.get('updateProgress',function(data){
                if(data == 'NaN' || data < 0){
                    $(".modal-backdrop").removeClass("hidden");
                    $("#bar_carrier").removeClass("hidden");
                    $.get('kse.htm');
                    interval = setInterval("ajaxP()",1000);
                }else{
                    alert("There is an ongoing query for the same session, please wait until its finished");
                }
            });
        })
        // updata progress
        function ajaxP(){
            $.get('updateProgress',function(data){
                datain = data;
                if(data != 404){
                    var bar = "<div id='please_wait' class='row-fluid'> </div> <div class='row-fluid'> <div class='span5 progress progress-striped active'> <div id='bar' class='bar'></div> </div> </div>";

                    if($("#bar").length == 0){
                        $("#bar_carrier").html(bar);
                    }
                    if(data < 100){
                        $("#bar").css("width",data+"%");
                        $("#please_wait").html("<font id='please-wait-font'>" +Math.round(data)+"% complete</font>");
                    }
                    else if(data >= 100 && (data == 203 || data == 204)){
                        var please_wait = "<font id='please-wait-font'>Finalizing and saving to database please wait<i class='icon-spinner icon-spin'</font>";
                        if($("#please-wait-font").length == 0)
                        {
                            $("#bar").css("width",data+"%");
                        }
                        else{
                            $("#please_wait").html(please_wait);
                            $("#bar").css("width",data+"%");
                        }
                        clearInterval(interval);
                        setTimeout('ajaxProgress()',2*60*1000);
                    }
                }
                else{
                    clearInterval(interval);
                    $("#bar_carrier").html("<h4 class='label label-success'>market is still open please try again later </h4>")
                }
            })
        }

这是我关于更新进度和初始请求的控制器

@RequestMapping("/kse.htm")
public @ResponseBody String kseData(Model model){
    parser.setExchange("kse");
    boolean choice = parser.start();
    return String.valueOf(choice);
}

@RequestMapping("/updateProgress")
public @ResponseBody String progress(Model model){
    float progress = parser.getProgress();
    if(progress != 404 && progress != 204 && progress != 203){
        return String.valueOf((progress/parser.getTotalProgress())*100);
    }
    else{
        return String.valueOf(progress);
    }
}

这是我在我的dao中的保存函数,函数

public boolean add(T entity) {
    getSession().save(entity);
    return true;
}

这是我的 getSession() 工厂是自动装配的

public Session getSession(){
    return (this.factory.getCurrentSession()==null)?
            this.factory.openSession(): this.factory.getCurrentSession();
}

这就是我通过 xml config 管理事务的方式

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<tx:advice id="tx" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="find*" propagation="REQUIRED"/>
        <tx:method name="add*" propagation="REQUIRED"/>
        <tx:method name="remove*" propagation="REQUIRED" />
        <tx:method name="update*" propagation="REQUIRED" />
        <tx:method name="findById*" propagation="REQUIRED" />
        <tx:method name="findBetween*" propagation="REQUIRED" />
        <tx:method name="findFromTo*" propagation="REQUIRED" />
        <tx:method name="updateOwnerId*" propagation="REQUIRED" />
        <tx:method name="updateOwnerType*" propagation="REQUIRED" />
        <tx:method name="findAllSearch*" propagation="REQUIRED" />
        <tx:method name="*" propagation="SUPPORTS" read-only="true"/>
    </tx:attributes>
</tx:advice>

<aop:config>
    <aop:advisor advice-ref="tx" pointcut="execution(* *..AbstractDao.*(..))" />
    <aop:advisor advice-ref="tx" pointcut="execution(* *..TempDataDao.*(..))" />
    <aop:advisor advice-ref="tx" pointcut="execution(* *..OwnershipDao.*(..))" />
    <aop:advisor advice-ref="tx" pointcut="execution(* *..OwnersDao.*(..))" />
    <aop:advisor advice-ref="tx" pointcut="execution(* *..ChangesDao.*(..))" />
    <aop:advisor advice-ref="tx" pointcut="execution(* *..TargetCompaniesDao.*(..))" />
</aop:config>

控制器调用解析器(一个会话范围的bean)的方法来执行开始执行,解析器的功能是一个异步的,它又调用一个dao的另一个方法来保存,每件事工作正常并且 dao 的方法退出,然后在发送更新进度的所有请求完成后,事务开始,它们是否懒惰地完成了?我要做的就是在我到达 save 方法或任何方法时进行转换。

编辑 2

好的,这是我更新进度或重要部分的方法

if(found != null){
        this.tempData = found;
        notFound = dataChecker.checkData(found);
        if(notFound.size() == 0){
            saveAllData(addDuplicates(dataChecker.getModifiedHolders()));
            this.progress = 204;
            return true;
        }
        else
        {
            this.progress = 203;
            return false;
        }
    }
    return false;

现在我注意到了一个更奇怪的行为,dataChecker 负责填充 notfound 列表,并且在这样做的同时将大量数据保存到数据库中,现在在所有这些完成之前不应返回进度,

但是发生的情况是进度返回为 204,就好像数据检查器已完成并且数据为空一样。

然后事务开始发生,Like datachecker 只将它们添加到队列中。

但这发生在我导航到另一个带有空 notFound 的页面之后,并且在事务完成后刷新页面并且所有事情都完成时,页面现在有了数据。

【问题讨论】:

  • 请用Controller的相关代码和Ajax请求更新您的问题。最重要的部分是事务分界是可见的。

标签: java spring hibernate spring-mvc spring-transactions


【解决方案1】:

请注意,默认的 Spring 事务管理是线程特定的。每个事务状态(及其数据库连接)都存储在 ThreadLocal 变量 (TransactionSynchronizationManager) 中。

这意味着,如果您在一个请求中启动事务(比如说线程 http-0),然后您尝试通过从另一个线程(比如说 http-1)进行的数据库查询来检查进度,那么您将不会成功。 第二个线程有自己的事务,它不会看到绑定到第一个线程的事务中的数据,直到第一个线程提交更改。

您真正需要的是定期在某个变量中存储和更新第一个线程 (http-0) 的进度,然后可以被其他线程读取。


更新 1:更具体一点 - 您的 parser.getProgress() 不得调用数据库来获取正确的进度信息。


更新 2: 刚刚注意到您的 getSession() 方法。 自行打开新会话是一个严重错误。这应该由 Spring 的事务管理器完成。如果sessionFactory.currentSession() 返回null,你应该抛出一个异常。

【讨论】:

  • 这就是问题所在,它不是,进度是基于 dao 方法调用的,当 dao 方法退出时它应该调用事务,但它不只是退出和然后进度递增,依此类推,完成后,它开始事务。
  • 我的意思是你不能使用 DAO 方法来获取进度。您的查询将在不同的事务中。也许您可以在问题中添加parser 的示例...
  • 刚刚注意到您的getSession() 代码。我已经更新了我的问题。请注意,打开和关闭会话必须由 Spring 的事务管理器完成。否则,您将绕过您的 AOP 配置。
  • 你的意思是我不应该自动连接我的工厂吗?因为这就是我正在做的事情,所以我正在自动装配我的工厂,然后从中获取会话,如何从 tx 管理器中进行操作?
  • 不...这与自动装配会话工厂无关。那没问题。你不应该打电话给sessionFactory.openSession()。检查我的答案UPDATE 2
猜你喜欢
  • 1970-01-01
  • 2020-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-22
  • 1970-01-01
  • 2019-09-22
  • 1970-01-01
相关资源
最近更新 更多