Hibernate transactions does not start until the MV

2019-04-16 07:26发布

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.

1条回答
劳资没心,怎么记你
2楼-- · 2019-04-16 07:57

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.

查看更多
登录 后发表回答