You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 4 Next »

Transactions

We have various ways of configuring transactions in our apps:

1. TransactionProxyFactoryBean

For each bean to be considered for transactional behavior, an XML bean is declared with the TransactionProxyFactoryBean as its parent, and with the target bean as a property. The TransactionProxyFactoryBean itself is also configured via XML: it is made aware of the transaction manager, and method-level transaction behavior for all "children" is specified. When the Spring container initializes, the target bean is wrapped with a transactional proxy object; all the interfaces implemented by the target object are proxied.

2. AOP

Transactional behavior is defined via AOP pointcuts and a transaction advice specifier in the Spring XML configuration. By default, Spring creates a proxy class which intercepts calls to the target methods.

3. @Transactional annotation

Transactional behavior is defined using annotations in the transactional classes themselves. By default, Spring creates a proxy class which intercepts calls to the target methods.

It appears that both the AOP and @Transactional configuration can be used together; the same proxy object will be used.

What all these schemes are doing is allowing us to configure all aspects of our database transactions, for example to specify where the transactional boundaries should be in our application.

So, given three methods of configuration, what should we use?

Of the three methods for configuring transactions, the TransactionProxyFactoryBean is the oldest and should probably no longer be used. The Spring docs go into detail about AOP and @Transactional, only mentioning TransactionProxyFactoryBean in a sidebar. I wouldn't be surprised if TransactionProxyFactoryBean were deprecated and dropped in future versions of Spring.

Moving exclusively to @Transactional means modifying a lot of existing Java code and converting existing project configurations.

AOP seems to be the cleanest method - transactions can be configured without modifying Java code.

Although AOP and @Transactional can be mixed, there could be issues with unpredictable behavior where the two configurations collide. For this reason, it is probably not a good practice to mix.

This leaves us with - using AOP. So this is my recommendation for a standard. Here's an example:

    <aop:config>
        <aop:advisor pointcut="execution(* *..StudentService.*(..))" advice-ref="txAdvice" />
        <aop:advisor pointcut="execution(* *..InstructorRoleAuthorizationService.*(..))" advice-ref="txAdvice" />
        <aop:advisor pointcut="execution(* *..StudentRoleAuthorizationService.*(..))" advice-ref="txAdvice" />
        <aop:advisor pointcut="execution(* *..StvRolesAuthorizationService.*(..))" advice-ref="txAdvice" />
        <aop:advisor pointcut="execution(* *..ApplicationVariableService.*(..))" advice-ref="txAdvice" />
    </aop:config>

    <tx:advice id="txAdvice" transaction-manager="transactionManager" >
        <tx:attributes>
            <tx:method name="save*" propagation="REQUIRED" rollback-for="*" />
            <tx:method name="delete*" propagation="REQUIRED" rollback-for="*" />
            <tx:method name="*" propagation="SUPPORTS" read-only="true" />
        </tx:attributes>
    </tx:advice>


The first part - <aop-config> - defines which parts of the code (join points) should participate in the transactions, using AspectJ-style pointcut expressions. For example,  "execution(* ..StudentService.(..))" means that for every execution of a method in the StudentService class, the behavior specified by "txAdvice" should come into play.

The second part,  <tx:advice... specifies the transaction "advice" or behavior that should be applied to the join points associated with the advice, and also associates the transaction manager (specified elsewhere) with the advice. The example allows a finer-grained definition of methods and their transaction characteristics (e.g. all methods with names starting with "save" and "delete" will require a transaction and will roll back if any exception is encountered). The third <tx:method> specifier in the example is the default - methods not matching the explicit method name patterns will get this default transaction behavior.

For the most part, the propagation behavior is specified as "REQUIRED", which means that if there is a transaction in progress, the code will participate in that transaction, but it there is no transaction in progress, a new transaction will be created.

If an unchecked exception (e.g. NullPointerException) is thrown during execution of code participating in a transaction, any uncommitted database updates will be rolled back. If no exceptions are thrown, or a checked exception (e.g. Exception) is thrown, the transaction will end with a commit.

If an attempt to perform a Hibernate operation outside of a transaction, and with no Hibernate session bound to the thread, an exception will be thrown: "No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here.". The solution to this is to include the code inside a transaction.

The use of the Spring OpenSessionInView interceptor adds a wrinkle here. It makes a session available to non-transactional code, and Hibernate operations within this code will work, but... it appears that each operation takes place with autocommit set to true, so each operation will be wrapped in its own transaction. Not the behavior we want. In addition, if OpenSessionInView is used with flush mode = auto, any modifications to hibernate entities will be flushed after the completion of the controller (but before the JSP render).

So it's important to make sure we don't have any hibernate operations taking place outside of transactions - and that all update operations (ideally all operations) with in a single HTTP request are within the same transaction. We should think about putting transactions around controller methods. Just putting transactions around service methods could be a problem if a controller calls multiple service methods that do updates.

Of the three methods for configuring transactions, the TransactionProxyFactoryBean is the oldest and should probably no longer be used. The Spring docs go into detail about AOP and @Transactional, only mentioning TransactionProxyFactoryBean in a sidebar. I wouldn't be surprised if TransactionProxyFactoryBean were deprecated and dropped in future versions of Spring.

@Transactional means modifying a lot of existing code and converting existing project configurations.

AOP seems to be the cleanest method - transactions can be configured without modifying code.

Although AOP and @Transactional can be mixed, there could be issues with unpredictable behavior where the two configurations collide. For this reason, probably not a good practice to mix.

This leaves us with - using AOP.

Question - should CSF have any transaction definition or should this be the responsibility of the application?

Flush - automatic flushing is probably a good thing. Hibernate will flush before some queries to avoid inconsistent data - for example if you read some data, modify the object, then issue another query that would read the (not-updated) data again from the database, apparently Hibernate is smart enough to do a flush before the query, so that the query is getting your updated version of the data. Setting flush mode to manual would lose this benefit.

It's probably best not to modify any Hibernate entity objects until you know all the data is valid AND to always to an explicit save, update, or delete, and not rely on the automatic flush to detect updates.

JPA - currently only has two flush modes; COMMIT (required to flush only before commit - may flush at other times) and AUTO (required to flush before any query and before commit).

Bottom line here is that with an ORM, flushing outside of your control is probably a given. We need to be aware that modifications to persistent entities will be synchronized to the database on a flush - sometimes in the middle of a transaction and certainly at the end of a transaction. I think this means:

- Don't update a persistent object until you know all the data to be modified is valid.
- Make sure transactions encompass all the changes you want to make in a unit of work. This means either a transaction around the controller method or around a facade service method that in turn calls finer-grained service methods.
- Might be best not to use persistent objects directly as "form backing objects".

  • No labels