All exceptions of MoleculeDatabaseFramework extends MoleculeDatabaseFrameworkException and are Runtime 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.
MoleculeDatabaseFramework uses optimistic locking with JPAs
@Version annotation. In case of concurrent updates Spring throws a
ObjectOptimisticLockingFailureException (caused by Hibernates
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.
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 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.
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`
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
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).