Apologies for the long preamble and post
I am working on a typical n-tier web application, a web tier (using an MVC framework), a business tier (a validation 'engine' and Hibernate) and Oracle. The application is a content management system which is used by a number of websites.
Users modify existing 'content items' (think a story or an link to an image) through a form and try to persist the changes. The modified content item (which is a mapped and persistent POJO) is run through the validation engine. The validation rules range from null checks right up to uniqueness checks for titles of the content item and so forth. Assuming all is well then the content items changes are made persistent, otherwise we bounce the user back to the form with some helpful validation errors.
The problem I am having is this say I have a content item which has a uniqueness constraint. When I query Hibernate to find out if an existing content item already has a value for the constrained field the 'state' of the content item is flushed to corresponding SQL session. This causes a problem when the value is not unique as you end up receiving a SQL exception where the constrained field value fires a database constraint. The act of checking for an existing value causes the SQLException.
I understand why this happens, Hibernate has a nifty trick of materializing the state of persistent objects (ie ones attached to its open session). It does this when a SELECT is generated by an HQL query or whatever so that it picks up any changes in the database session that it is connected to.
I have tried the following approaches to solve this problem:
1. Use a separate session for the validation checks. This falls down in one main place. If you have done something in the 'main' session which should cause validation to fail (you try to insert two content items, both with the same title) it won't.
2. Use Hibernate in a detached object way. This is painful. Firstly you end up retrieving a lot of data that you don't necessarily need. Secondly it possible that you don't retrieve enough data and the presentation tier has to be aware of the 'size' of the content objects (and issue subsequent retrieves).
3. Use FlushMode.COMMIT. You can tell Hibernate not to issue any nifty flushes until you commit the transaction. In the case of our validation if we find that there are any validation errors we rollback the transaction and Oracle is none the wiser to the invalid state of the content object. This has the disadvantage (like option 1) that if you issue any queries they won't pick up any changes in you current session unless you explicitly call flush() on the session.
I am currently using option 3 but I'd love to be able to pick up SQL that is currently in my current database session withough having to litter the presentation tier with flush() statements. This could be a problem if I am creating two content items in the same Hibernate session and both have the same value for a unique field.
have people solved this problem in the past?. I have looked at various postings around interceptors but I rather offer clients of the business tier the ability to validate objects as a separate method rather than only allowing it to happen on save.
Sorry once again for the length of the post!
Ian.
|