本文介绍了直到MVC请求完成,Hibernate事务才会启动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用Spring MVC 3和Hibernate 3.6,用Spring tx来管理hibernate事务,现在我正在向一个控制器发出一个ajax请求,控制器返回一个值我导航到另一个页面,我不断检查控制器,直到返回值。

这个控制器方法调用也有一些数据库事务要做,并且我试图做的是当每一件事情完成并且数据库tx完成并且一切都很好时导航,但是会发生什么是hibernate的persist()或save()方法退出,但事务没有开始。



我对代码进行了调试,发现spring在交易完成之前会对交易进行某种排队,这意味着它不会实际执行交易,而是说交易方法保存完成后,排队交易,然后再做。



编辑





$ .get('updateProgress',函数(数据){
if(data)); pre> $(#kse_search =='NaN'||数据< 0){
$(。modal-backdrop)。removeClass(hidden);
$(#bar_carrier)。removeClass(hidden);
$ .get('kse.htm');
interval = setInterval(ajaxP(),1000);
} else {
alert(对于同一个会话有一个正在进行的查询,请等待直到完成);
}
});
})
//更新进度
函数ajaxP(){
$ .get('updateProgress',函数(数据){
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&& (数据== 203 ||数据== 204)){
var please_wait =< font id ='please-wait-font'>完成并保存到数据库,请等待< 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>)
}






$ b

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

  @RequestMapping(/ kse.htm)
public @ResponseBody字符串kseData(模型模型){
parser.setExchange(kse);
布尔选择= parser.start();
return String.valueOf(choice);
}

@RequestMapping(/ updateProgress)
public @ResponseBody字符串进度(模型模型){
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);
返回true;

$ / code>

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

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

这就是我的事务是如何通过xml config进行管理的。

 < bean id =transactionManagerclass =org.springframework.orm.hibernate3.HibernateTransactionManager> 
< property name =sessionFactoryref =sessionFactory/>
< / bean>

< tx:advice id =txtransaction-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:方法名称=findFromTo *propagation =REQUIRED/>
< tx:method name =updateOwnerId *propagation =REQUIRED/>
< tx:method name =updateOwnerType *propagation =REQUIRED/>
< tx:method name =findAllSearch *propagation =REQUIRED/>
< tx:方法名称=*propagation =SUPPORTS只读=true/>
< / tx:属性>
< / tx:advice>

< aop:config>
< / aop:config>

控制器调用解析器(会话作用域bean)的方法来执行开始执行,解析器的函数是一个Async函数,它依次调用另一个执行保存的dao方法,每个函数都可以正常工作,并且dao的方法退出,然后在所有发送更新进度的请求完成后,事务开始,他们懒洋洋地完成了吗?所有我想要做的就是让转换发生,就像我达到保存方法或任何方法一样。

编辑2



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

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

现在我已经注意到了一些更奇怪的行为,dataChecker负责填充notfound List,while这样做可以将大量数据保存到数据库中,现在只有在完成所有工作之后才能返回进度,

但是,发生的情况是,进度返回为204就好像数据缓存已经结束并且数据是空的。

然后在事务开始之后,就像datachecker将它们添加到队列中一样。



但是,当我使用空的notFound导航到另一个页面时,发生这种情况,并且在事务完成后每次刷新页面时,页面现在都有数据。

解决方案

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

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



实际需要的是定期存储和更新某个变量中第一个线程( http-0 )的进度,然后可以由其他线程读取。 / p>




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




UPDATE 2:刚注意到你的 getSession()方法。自己开设新会话严重错误。这应该由Spring的事务管理器完成。你应该在 sessionFactory.currentSession()返回 null

I am using spring MVC 3 and Hibernate 3.6, with Spring tx to manage the hibernate transactions,

Now I am doing an ajax request to a controller that when the controller returns a value I navigate to another page, and i keep checking the controller till the value is returned.

This controllers method invocation also have some database transactions to do, and what I am trying to do is to navigate when every thing is finished and the database tx is done and all is good, but what happens is that hibernate's persist() or save() method exits but the transaction is not started.

I debugged the code and found that spring does some kind of a queue to transactions until the request to the controllers is done, meaning it does not actuly do the transaction rather it says that the method save is done, and queue the transaction then do it later.

EDIT

This is my relevant code

           $("#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>")
                }
            })
        }

and this is my controller regarding the update progress and the initial request

@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);
    }
}

this is my saving function in my dao, the function

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

this is my getSession() the factory is autowired

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

and this is how my transaction is managed via 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>

The controller calls the method of the parser (a session scoped bean) to execute start the execution, the function of the parser is an Async one, which by its turn calls another method of a dao which does the saving, Every thing works fine and it the dao's method exits and then after all the request sent to update progress is done, the transactions start, are they Lazily done ? all I want to do is make the transation occur just as I reach the save method or any method infact.

EDIT 2

Ok this is my method that update the progress or the part of it that matters

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;

Now I have noticed a little wierder behavior, the dataChecker is responsible for filling the notfound List,and while doing that it saves alot data to the database, now the progress should not be returned until all of that is done,

But what happens is that the progress is returned as 204 as if the datachecker has finished and the data was empty.

Then after that the transaction start to happen, Like datachecker only added them to a queue.

But this happens after I am navigate to the other page with an empty notFound, and when refresh the page after the transaction and every thing is done, the page now has the data.

解决方案

Be aware that default Spring transaction management is thread specific. Each transaction status (and its database connection) are stored in ThreadLocal variable (TransactionSynchronizationManager).

This means, that if you start transaction in one request (lets say thread http-0) and then you try to check the progress via database query made from a different thread (lets say http-1), then you will not succeed. The second thread will have its own transaction and it will not see data from the transaction bound to the first thread, until the first thread commits the changes.

What you actually need is to periodically store and update progress of the first thread (http-0) in some variable, which can then be read by other threads.


UPDATE 1: To be little bit more specific - your parser.getProgress() must not call database to obtain correct progress information.


UPDATE 2: Just noticed your getSession() method. It is a severe mistake to open new session on your own. This should be done by Spring's transaction manager. You should throw an exception in case sessionFactory.currentSession() returns null.

这篇关于直到MVC请求完成,Hibernate事务才会启动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-04 06:48
查看更多