views:

98

answers:

2

Hi, I'm relatively new with hibernate so please be gentle. I'm having an issue with a long running method (~2 min long) and changing the value of a status field on an object stored in the DB. The pseudo-code below should help explain my issue.

public foo(thing) {  
    if (thing.getStatus() == "ready") {
        thing.setStatus("finished");
        doSomethingAndTakeALongTime();
    } else {
        // Thing already has a status of finished. Send the user back a message.
    }
}

The pseudo-code shouldn't take much explanation. I want doSomethingAndTakeALongTime() to run, but only if it has a status of "ready". My issue arises whenever it takes 2 minutes for doSomethingAndTakeALongTime() to finish and the change to thing's status field doesn't get persisted to the database until it leaves foo(). So another user can put in a request during those 2 minutes and the if statement will evaluate to true.

I've already tried updating the field and flushing the session manually, but it didn't seem to work. I'm not sure what to do from here and would appreciate any help.

PS: My hibernate session is managed by spring.

+2  A: 

Basically you need to let it run in a separate Thread to make the method to return immediately. Else it will indeed block until the long running task is finished. You can pass the entity itself to the thread, so that it can update the status itself. Here's a basic kickoff example using a simple Thread.

public class Task extends Thread {
    private Entity entity;
    public Task(Entity entity) {
        this.entity = entity;
    }
    public void run() {
        entity.setStatus(Status.RUNNING);
        // ...
        // Long running task here.
        // ...
        entity.setStatus(Status.FINISHED);
    }
}

and

public synchronized void foo(Entity entity) {
    if (entity.getStatus() == Status.READY) {
        new Task(entity).start();
    } else {
        // ...
    }
}

With the Status in an enum you can even use a switch statement instead of an if/else.

    switch (entity.getStatus()) {
        case READY: 
            new Task(entity).start();
            break;
        case RUNNING:
            // It is still running .. Have patience!
            break;
        case FINISHED:
            // It is finished!
            break;
    }            

For a more robust control of running threads, you may want to consider ExecutorService instead. Therewith you can control the maximum number of threads and specify a timeout.

BalusC
Hmm... I tried to make foo() synchronized earlier today and it didn't seem to work. All I did was add the "synchronized" modifier. Is there anything else you have to do to make it a synchronized method?
keeleyt83
"synchronized" will only help if you do the other Thread-related work that BalusC discussed. Did you do that?
jowierun
@keeley: the `synchronized` modifier is just there to avoid another threads to enter the method while the status is still to be set to `RUNNING` (and you thus may risk the long running task to be executed twice or more). It does not let the method immediately return. Doing the long running task in another thread will let the method immediately return. Did you try it?
BalusC
Just got into work. I'll try it now. Thanks guys.
keeleyt83
I implemented a solution like the one above, but used an inner class for the thread. It appears to be working. Thanks a lot BalusC!
keeleyt83
You're welcome.
BalusC
A: 

hey,

What the method doSomethingAndTakeALongTime() is doing? is it for DB operation or just executing some business logic?

If its not doing any DB operation, and you got your status fine then you can persist the object before calling that method.

And if its doing some DB operation, then you need to wait for it. So, even if you put in thread you need to wait for that thread to complete (using thread.join() we can do that)

the thing is, before you persist you must have completed all operation based on you ORM object right? so try to optimized the logic for the method to get it executed before you persist.

thanks.

Paarth