1
votes

When I intentionally throw an exception within an Objectify transaction, my transaction is not being rolled back. What am I doing wrong?

        @Inject 
        Dao dao
        ...
        public void testTransaction(){
            dao.transact(new VoidWork() {
            @Override
            public void vrun() {
                Key aclKey= dao.save().entity(acl).now(); //expecting this to be rolled back
                //throw exception
                if(true) throw new IllegalArgumentException();
                //expecting rollback of acl save
            }

            });
        }

I am using a setup like this:

@Singleton
public class DaoFactory extends ObjectifyFactory {
    private Injector injector;

    @Inject
    public DaoFactory(Injector injector) {
        this.injector = injector;
        registerEntities();
    }

    private void registerEntities() {
    }

    @Override
    public <T> T construct(Class<T> type) {
        return injector.getInstance(type);
    }

    @Override
    public Objectify begin() {
        Dao dao = new Dao(this);
        return dao;
    }
}

Where:

public class Dao extends ObjectifyImpl<Dao> {
    @Inject
    public Dao(ObjectifyFactory fact) {
        super(fact);
    }
}

and

public class DaoService {
    @Inject
    public static void setObjectifyFactory(DaoFactory fact) {
        ObjectifyService.setFactory(fact);
    }
}

are all injected using Guice for DI.

I stepped through the code, and objectify does call txnOfy.getTransaction().rollback(); in TransactorNo.class

but, when I check app-engine local db, I see a an entity created for the acl (sometimes taking a few seconds longer to show up)

1

1 Answers

4
votes

Transaction state is attached to a specific ObjectifyImpl instance. You're starting a transaction (which makes a new ObjectifyImpl available via the static ofy() method) and then re-using the old, nontransactional ObjectifyImpl instance.

So even though you are rolling back the transaction, your save operation utilized the nontransactional Objectify impl from outside the transaction.

This is why the documentation suggests that you never hold on to an Objectify instance in a variable; always use the static ofy() method. You can make your own static ofy() (or whatever) method that returns your Dao type instead. Look at the example code for http://www.motomapia.com/

Because transaction and sessions states are thread-local concerns, injecting persistence contexts is just a bad idea, even under JPA.