Nicolas Frankel is an IT consultant with 10 years experience in Java / JEE environments. He likes his job so much he writes technical articles on his blog and reviews technical books in his spare time. He also tries to find other geeks like him in universities, as a part-time lecturer. Nicolas is a DZone MVB and is not an employee of DZone and has posted 221 posts at DZone. You can read more from them at their website. View Full User Profile

Transaction management: EJB3 vs Spring

07.23.2012
| 21621 views |
  • submit to reddit

Transaction management is a subject that is generally left to the tender care of a senior developer (or architect). Given the messages coming from some actors of the JavaEE community that with newer versions of JavaEE you don't need Spring anymore, I was interested in some fact-checking on how transaction management was handled in both technologies. Note: these messages were already sent one year and a half ago and prompted me to write this article.

Transaction demarcation

Note that although both technologies provide programmatic transaction demarcation (start transaction then commit/rollback), we'll focus on declarative demarcation since they are easier to use in real life. In EJB3, transactions are delimited by the @TransactionAttribute annotation. The annotation can be set on the class, in which case every method will have the transactional attribute, or per method. Annotation at the method level can override the annotation at the class level. Annotations are found automatically by the JavaEE-compliant application server. In Spring, the former annotation is replaced by the proprietary @Transactional annotation. The behaviour is exactly the same (annotation at method/class level and possibility of overriding). Annotations can only be found when the Spring bean definition file contains the http://www.springframework.org/schema/tx namespace as well as the following snippet:

<tx:annotation-driven />

Alternatively, both technologies provide an orthogonal way to set transaction demarcation: in EJB3, one can use the EJB JAR deployment descriptor (ejb-jar.xml) while in Spring, any Spring configuration file will do.

Transactions propagation

In EJB3, there are exactly 6 possible propagation values: MANDATORY, REQUIRED (default), REQUIRES_NEW, SUPPORTS, NOT_SUPPORTED and NEVER. Spring adds support for NESTED (see below).

Nested transactions

Nested transactions are transactions that are started then commited/rollbacked during the execution of the root transaction. Nested transaction results are limited to the scope of this transaction only (it has no effect on the umbrella transaction). Nested transactions are not allowed in EJB3; they are in Spring.

Read-only transaction

Read-only transactions are best used with certain databases or ORM frameworks like Hibernate. In the latter case, Hibernate optimizes sessions so that they never flush (i.e. never push changes from the cache to the underlying database). I haven't found a way (yet) to qualify a transaction as read-only in EJB3 (help welcome). In Spring, @Transactional has a readOnly parameter to implement read-only transactions:

@Transactional(readOnly = true)

Local method calls

In local method calls, a bean's method A() calls another method B() on the same instance. The expected behavior should be that transaction attributes of method B() is taken into account. It's not the case in neither technologies, but both offer some workaround. In EJB3, you have to inject an instance of the same class and call the method on this instance in order to use transaction attributes. For example:

@Stateless
public MyServiceBean implements MyServiceLocal {

    @EJB
    private MyServiceLocal service;

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    public void A() {

        service.B();
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void B() {

        ...
    }
}

In Spring, by default, transaction management is handled through pure Java proxies. If you want to have transaction management in local method calls, you'll have to turn on AspectJ. This is easily done in the Spring beans definition file (but beware the side-effects, see the Spring documentation for more details):

<tx:annotation-driven mode="aspectj" />

Exception handling and rollback

Exception handling is the area with the greater differences between Spring and EJB3. In EJB3, only runtime exceptions thrown from a method rollback a transaction delimited around this method by default. In order to mimic this behavior for checked exceptions, you have to annotate the exception class with @ApplicationException(rollback = true). Likewise, if you wish to discard this behavior for runtime exceptions, annotate your exception class with @ApplicationException(rollback = false). This has the disadvantage of not being able to use the same exception class to rollback in a method and to still to commit despite the exception in another method. In order to achieve this, you have to manage your transaction programmatically:

@Stateless
public MyServiceBean implements MyServiceLocal {

    @Resource
    private SessionContext context;

    public void A() {

        try {

            ...

        } catch (MyException e) {

            context.setRollbackOnly();
        }
    }
}

In Spring, runtime exceptions also cause transaction rollback. In order to change this behavior, use the rollbackFor or noRollbackFor attributes of @Transactional:

public MyServiceImpl {

    @Resource
    private SessionContext context;

	@Transactional(rollbackFor = MyException.class)
    public void A() {

		...
    }
}

Conclusion

There's no denying that JavaEE has made giant steps in the right direction with its 6th version. And yet, small details keep pointing me toward Spring. If you know only about one - Spring or JavaEE 6, I encourage you to try "the other" and see for yourself which one you're more comfortable with. To go further:

Published at DZone with permission of Nicolas Frankel, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Reza Rahman replied on Mon, 2012/07/23 - 10:09am

A few facts missing from this blog:

* EJB 3 implementations come with JTA configured out-of-the-box and there is no configuration needed to get the transaction manager up-and-running, just add annotations to your beans and that's it. In Spring, you need to configure the transaction manager itself explicitly (often beyond the two XML fragments mentioned here). To get JTA to work, you may need even more configuration if you are running outside of an application server.

* "Read-only" and "nested" transactions are not supported in JTA (in fact many database drivers don't support these features ouright let alone JTA -- Spring docs do a poor job of mentioning this). You can get the equivalent of "read-only" transactions as referred to in the Spring docs as an "optimization technique" in a database neutral fashion simply by setting transaction propagation to NOT_SUPPORTED in EJB 3: http://www.slideshare.net/caroljmcdonald/td09jpabestpractices2 or setting the database isolation level properly. BTW I haven't needed any of these features in my applications -- part of the reason these edge cases are not in the standard.

* If you are really that averse to EJB 3 transactions, you can use Seam Persistence or CODI transactions via CDI extensions in Java EE (although these don't include the above features either -- the same story with Guice, Seam 2, etc -- for pretty much the same reasons that EJB 3 does not). 

* Finally, Java EE declarative transactions are being re-written from the ground-up in Java EE 7. So, if there is something you believe that really needs to be supported, feel free to join the discussion here: http://java.net/projects/javaee-spec/pages/Home.

Joonas Javanainen replied on Mon, 2012/07/23 - 10:19am in response to: Reza Rahman

You can get the equivalent of "read-only "transactions by setting transaction propagation to NOT_SUPPORTED in EJB 3 if you need them

I don't see how they could be equivalent. Read-only transactions might be affected by other simultaneous write transactions and isolation levels can be used to control the effects. If you use NOT_SUPPORTED, you have no isolation control at all. A read-only SERIALIZABLE transaction has different semantics than a NOT_SUPPORTED transaction.

I think the biggest value of read-only transactions is the clear communication of developer intent. If you intend to only read stuff, the fact is reflected in the annotation. In practice there is also a performance impact as mentioned by the blog post, but I don't know how widely the readonly flag is applied in a Spring application. For example, PostgreSQL supports read-only transactions, but I don't know if that is set by Spring right now. At least in theory using read-only could have major positive performance implications if the whole stack is aware of it.

Reza Rahman replied on Mon, 2012/07/23 - 11:31am in response to: Joonas Javanainen

Yep, that's why I said equivalent and not exactly the same. In practice, "no isolation" is about the same as "read-only" from an optimization standpoint (as well as locking standpoint in the vast majority of cases). As I said, if you really deem this distinction enough to add yet another setting vs simply using NOT_SUPPORTED you (or someone else) certainly can suggest it to the Java EE 7 EG. The biggest practical reason it probably won't be added is because a majority of DB vendors likely will not support it on top of the isolation levels they already support.

Liam Knox replied on Mon, 2012/07/23 - 3:35pm in response to: Reza Rahman

This most 'database vendors do not support' argument is a bit flawed and wrong IMHO.  What modern drivers do not support savepoints? So why doesn't J2EE support nested transactions.  

You need to look at the benefits, business use cases and majority actual technicla usage , not some stat around number of vendors or number of driver versions that support a feature when making a call to include a feature. The call must be pragrammatic and not just to fit to the lowest delimeter. Java is now really pain on that idiom on many fronts.  

J2EE was always hailed as easy to migrate from one platform (container or db) to another. This was an invalid claim from the outset and the last thing you would be worried about in this day and age if you were doing this, not that many people ever get that far, would be moving to a less rich platform with a less rich transactional model. 

 

Reza Rahman replied on Mon, 2012/07/23 - 4:30pm in response to: Liam Knox

As to nested transactions (assuming this is what your tirade is about), the issue is not JTA, it's X/Open. X/Open, the only available cross-database transaction management standard, does not support nested transactions (and somewhat consequently it just isn't used that often in real life). In fact, most databases like MySQL do not even allow nested transactions.

Also, no standard, and certainly not Java EE, guaratees 100% portability across compatible implementations. What it hopes to achieve is to maximize vendor/implementation neutrality, hence adding obscure features few implementations will/can support is definitely not the way to go.

BTW, savepoints are different (but a closely related concept) from full support for nested transactions in the sense that they are supported in databases like Oracle and DB2 and defined here: http://en.wikipedia.org/wiki/Nested_transaction. Savepoints is what many JTA suspended transaction implementions use...

Liam Knox replied on Tue, 2012/07/24 - 5:40pm in response to: Reza Rahman

Very much you are not seeing the wood from the trees IMHO.  How many people are using the configurations you mention?  If you are you Spring and MySQL there is no worry of not using the nested transaction support as you don't have it supported by the vendor.  However the question you must ask is would you take a big DB2 application and ever look to move to MySQL and then become very upset with the lack of Nested transaction support? vs. Would developers/companies be really put out by not having Nested support even though there vendor supports transactional savepoints?  The world doesn't work on some base religous level of compatability it works on what gives value.

Clearly you are coming from the 'standard' perspective that has already shot Java once in the J2EE space,  now shooting it big style in the module space and will likely lead to it tail off and movement to Scala.

Any one can create a 'standard', you need to have the objectivity to measure the standard in terms of worth. Spring is a great technology and defacto standard and I doubt many would question its worth. Other API's, Guava, Mockito, etc you can say the same.  How many Java 'standard' API's can you hold up against this light? Doesn't matter that they are 'standard'. Standard is a too often miss interpretted as quality. 

 

 

Henk De Boer replied on Thu, 2012/07/26 - 5:14pm in response to: Liam Knox

So why doesn't J2EE support nested transactions.
So why do you keep using a term that was deprecated 6 years ago?

Liam Knox replied on Sun, 2012/07/29 - 2:15am in response to: Henk De Boer

What J2EE ? 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.