Thursday, March 15, 2012

Running Integration Tests Across Transaction Boundaries

The normal way to run an integration test that manipulates data in a database is of course a transactional test class backed by Spring's SpringJUnit4ClassRunner. In most instances this works great. However, what happens if the the code under test spans transaction boundaries? Say, due to an @Transaction(propagation = Propagation.REQUIRES_NEW) declaration? As you'd expect, a new transaction is created that is independent of the transaction created by the test class. Fortunately, if you dig deep enough, it turns out there's a surprisingly simple solution. Get a handle on the application context using whatever means you'd like, extract the TransactionInterceptor, and swap out its TransactionAttributeSource. TransactionAttributeSource is responsible for detecting the @Transactional annotations on classes and methods. MatchAlwaysTransactionAttributeSource is a built-in implementation that doesn't even bother to look. It ignores any annotations, and just returns the default transaction configuration. Now, the entire test will run within a single transaction.

Wednesday, December 7, 2011

How to Dynamically Add Entities to a Persistence Unit

With a simple Spring extension it's possible to crack open a PersistenceUnit and dynamically add additional entity classes. It's also possible to merge multiple PersistenceUnits, but that isn't shown here. And the wiring:

How to Disable Hibernate Cascading

If domain objects are used in multiple contexts, it's possible that cascading is not always desirable. Unfortunately, it's not possible to persistNoCascade(entity). The following is a crude hack that will disable cascading entirely. It is not toggle-able, however. That would be nice, but it's a tricky thing to pull off because SessionFactoryImpl, SessionImpl, and AbstractEntityPersister can't be proxied. And the following is how to wire it:

Friday, May 6, 2011

Extracting Collection Parameterized Type at Runtime

While the parameterized type of locally declared collections is erased at compile time, it actually is possible to extract the type off of a field at runtime. Here's how it's done:

That's all there is to it. Note, this trick will not work if the collection declaration uses a wildcard or generic parameter.

Tuesday, May 3, 2011

Multi-Field Bean Validation (JSR 303)

JSR 303 is excellent, with few glaring short-comings. However, the ability to access multiple bean properties within a single field level validator is surprisingly absent. The obvious solution is to just move the validator to the class level. It's annoying and ugly, but it works. Unfortunately, the bean property path on the ConstraintViolation now points to the class and not the field where validation failed. This problem can be resolved by explicitly adding the bean property path to your validator's annotation, and doing something like the following in your validator:

Monday, February 14, 2011

Embedding XML within JAXB Objects

If a JAXB object contains a String element of un-parsed XML, it can be a little tricky to represent.

First, you need to stop JAXB from attempting to unmarshal the embedded XML. This is done by intercepting the call to unmarshal the element in order to gain access to raw DOM node that JAXB has constructed. From here, it's simply a matter of converting the node into a String, which is written to a field. As you'd expect, for marshalling you need to do the opposite, turning the XML String into a DOM node. However, you must wrap your XML in a dummy, root tag prior to returning it. For example:

The above code utilizes a utility class to wrap basic W3C XML parsing. It's not doing anything special, but, if you're curious, the source is available here.

Your code should now be able to marshal/unmarshal embedded XML to a String field. This works fine and dandy as long as your XML does not contain namespaces. Namespaces pose a couple of problems. First, if the namespaces aren't declared at the root of the document, JAXB will blowup while parsing. Second, if there is a declared, but unused, namespace, JAXB will not preserve it.

The first problem can be solved by scanning your XML prior to unmarshalling, and attaching any namespace declarations that are discovered to the root node. This can be done in a variety of ways. I have a solution that's based on DOM4J that's available in my commons project here.

The second issue is really only a problem if something else in your application is depending on having certain namespaces defined. Unfortunately, JAXB does not have a simple flag to toggle between behaviors, but there is a way to preserve unused namespaces. You have to create a SAXSource, set a couple of namespace related flags on it, and feed it to your unmarshaller. It should look something like this:

JAXB has more than its fair share of quirks, but at least you can force it to do just about anything.

Saturday, February 5, 2011

JAXB Packing Bug

It appears to be a little known fact that Java 1.6 includes a JAXB implementation -- no need for external libraries. However, the version that's currently bundled has an annoying bug related to how it packs lists. Let's say that your XML contains a list of items that you'd like to unmarshal and represent internally as a map. One might thing that all you'd have to do is the following:

And what you'll end up with is an empty map. Here's what happened:
  1. Something.getList() is called and returns null.
  2. JAXB creates a new ArrayList, and passes it to Something.setList(). At this point there is nothing in the list.
  3. JAXB unmarshals all of the items, appending them to the list.
I'm sure you see the problem. Fortunately, there is a solution. The latest version of JAXB (available here) has fixed the problem. It follows the same flow described above, but with one additional step. It calls Something.setList() a second time after populating the list.

Upgrading your version of JAXB is a two step process.
  1. Put the new jars on your classpath or install them to your Maven repository.
  2. Prior to unmarshalling, call: System.setProperty("javax.xml.bind.JAXBContext", "com.sun.xml.bind.v2.ContextFactory");