简体   繁体   中英

How to wrap Wicket page rendering in a Spring / Hibernate transaction?

My application loads entities from a Hibernate DAO, with OpenSessionInViewFilter to allow rendering.

In some cases I want to make a minor change to a field -

Long orderId ...

link = new Link("cancel") {
    @Override public void onClick() {
        Order order = orderDAO.load(orderId);
        order.setCancelledTime(timeSource.getCurrentTime());       
    };

but such a change is not persisted, as the OSIV doesn't flush.

It seems a real shame to have to call orderDOA.save(order) in these cases, but I don't want to go as far as changing the FlushMode on the OSIV.

Has anyone found any way of declaring a 'request handling' (such as onClick) as requiring a transaction?

Ideally I suppose the transaction would be started early in the request cycle, and committed by the OSIV, so that all logic and rendering would take place in same transaction.

I generally prefer to use additional 'service' layer of code that wraps basic DAO logic and provides transactions via @Transactional. That gives me better separation of presentation vs business logic and is easier to test.

But since you already use OSIV may be you can just put some AOP interceptor around your code and have it do flush()?

Disclaimer: I've never actually tried this, but I think it would work. This also may be a little bit more code than you want to write. Finally, I'm assuming that your WebApplication subclasses SpringWebApplication. Are you with me so far?

The plan is to tell Spring that we want to run the statements of you onClick method in a transaction. In order to do that, we have to do three things.

Step 1: inject the PlatformTransactionManager into your WebPage:

@SpringBean
private PlatformTransactionManager platformTransactionManager;

Step 2: create a static TransactionDefinition in your WebPage that we will later reference:

protected static final TransactionDefinition TRANSACTION_DEFINITION;
static {
    TRANSACTION_DEFINITION = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
    ((DefaultTransactionDefinition) TRANSACTION_DEFINITION).setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
}

Feel free to change the TransactionDefinition settings and/or move the definition to a shared location as appropriate. This particular definition instructs Spring to start a new transaction even if there's already one started and to use the maximum transaction isolation level.

Step 3: add transaction management to the onClick method:

link = new Link("cancel") {
    @Override 
    public void onClick() {
        new TransactionTemplate(platformTransactionManager, TRANSACTION_DEFINITION).execute(new TransactionCallback() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                Order order = orderDAO.load(orderId);
                order.setCancelledTime(timeSource.getCurrentTime());      
            }
        }
    } 
};

And that should do the trick!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM