Wiki

Clone wiki

MoleculeDatabaseFramework / Exception Handling of MoleculeDatabaseFramework

MoleculeDatabaseFrameworkException

All exceptions of MoleculeDatabaseFramework extends MoleculeDatabaseFrameworkException and are Runtime Exceptions.

Checked Exceptions

MoleculeDatabaseFramework wraps all checked exceptions in a type of MoleculeDatabaseFrameworkException and hence makes it an unchecked exception. Currently the author is of the opinion that checked exceptions clutter interfaces and method declarations.

ObjectOptimisticLockingFailureException

MoleculeDatabaseFramework uses optimistic locking with JPAs @Version annotation. In case of concurrent updates Spring throws a ObjectOptimisticLockingFailureException (caused by Hibernates StaleObjectException).

The problem with ObjectOptimisticLockingFailureException is that it does not contain a lot of information about the issue. MoleculeDatabaseFramework can be configured to catch this exception and then retrieve the newest version of the entity that caused the issue. For that it wraps ObjectOptimisticLockingFailureException in an EntityVersionConflictException and you can get the newer version by calling

RegistrationCompound currentVersion = (RegistrationCompound)entityVersionConflictException.getCurrentVersion();

You can pass the current version on to the client, as example this could be a client consuming your REST service and you can respond with HTTP 409 Conflict and the body can contain the current version according to RFC 2616:

For example, if versioning were being used and the entity being PUT included changes to a resource which conflict with those made by an earlier (third-party) request, the server might use the 409 response to indicate that it can't complete the request. In this case, the response entity would likely contain a list of the differences between the two versions in a format defined by the response Content-Type.

DataIntegrityViolationException

MoleculeDatabaseFramework can also improve the usefulness of DataIntegrityViolationException. It can be configured to catch it and convert certain cases that violate a unique constraint into an UniqueConstraintViolationException. UniqueConstraintViolationException holds a mapping of all violated unique constraints and the offending values.

The cases this works for is when updating an existing entity or when persisting a new entity and the unique constraint violation happens in the directly persisted entity and not in a cascaded one. As example if you save a new Containable of a new ChemicalCompound and the ChemicalCompound violates a unique constraint, the exception is not handled and a DataIntegrityViolationException is thrown. However if Containable violates a unique constraint a UniqueConstraintViolationException is thrown.

Examples:

DataIntegrityViolationException:

String violatingValue = "9999-99-9";
TestCompound compound2 = new TestCompound("Benzene", violatingValue,
                "Compound Description", null, null);
        ChemicalStructure structure = chemicalStructureFactory.createChemicalStructure("c1ccccc1");
        ChemicalCompoundComposition composition2 = new ChemicalCompoundComposition(compound2, structure, 100d);
        TestContainable containable2 = new TestContainable("test", compound2);
        compound2.addComposition(composition2);
        compound2.addContainable(containable2);
        containableService.save(containable2); //throws `DataIntegrityViolationException`

UniqueConstraintViolationException:

String violatingValue = "test1000";
TestCompound compound2 = new TestCompound("Benzene", "71-43-2",
                "Compound Description", null, null);
        ChemicalStructure structure = chemicalStructureFactory.createChemicalStructure("c1ccccc1");
        ChemicalCompoundComposition composition2 = new ChemicalCompoundComposition(compound2, structure, 100d);
        TestContainable containable2 = new TestContainable(violatingValue, compound2);
        compound2.addComposition(composition2);
        compound2.addContainable(containable2);
        containableService.save(containable2); //throws `UniqueConstraintViolationException`

Configure Spring AOP for Exception Handling

To enable above exception handling you need to add

<aop:aspectj-autoproxy/>

to your spring configuration. Because above aspects are annotated with @Component they will be automatically detected as aspects by component scanning and do not have to be added to the Spring configuration explicitly.

Now the important part. That this works correctly the ordering of the aspects in relation to the @Transactional annotation which is an aspect too must be correct. ObjectOptimisticLockingFailureException must be caught before the transaction is committed and DataIntegrityViolationException must be caught after the transaction is committed. This is achieved with the @Order annotation on the aspects and the order-attribute of the transaction configuration:

<tx:annotation-driven transaction-manager="transactionManager" order="2000"/>

Order attribute in transaction configuration must be between 1000 and 3000 (exclusive).

Updated