tags:

views:

160

answers:

1

What is the Hibernate best practice when having to roll back foreign key relationships, but not wanting to throw out the transaction (ie. still proceed with saving other data)?

If I just pass the exception back up the stack or don't do this rollback, then I will hit an error when I do try to save the other data (Null or Transient Object passed to persist) as the BookingLines are already persisted.

I currently handle the rollback by adding the new entities to a map and then removing them from the entity lists on failure as follows:

/* Method that will try to create an invoice but won't roll back the transaction if
 * a currency rate can't be found.
 */
public void createInvoiceFromBooking(Booking booking) {

  Invoice invoice = new Invoice();

  //Map to hold entities that I want to roll back
  Map<BookingLine, InvoiceLine> addedLines = new HashMap<BookingLine, InvoiceLine>();

  try {
    for(BookingLine bookingLine : booking.getBookingLines()) {
     InvoiceLIne invoiceLIne = new InvoiceLIne();

     //do stuff that can throw exception
     applyCurrencyRate(invoiceLine);

     bookingLine.getInvoiceLines().add(invoiceLine);
     invoice.getInvoiceLines().add(invoiceLine);

     //add to the "rollback" map
     addedLines.put(bookingLine, invoiceLine);
    }
  } catch (Exception e) {
    //remove the InvoiceLines from the related entities
    for(BookingLine bookingLine : addedLines.keySet()) {
      bookingLine.getInvoiceLines().remove(addedLines.get(bookingLine));
    }
  }

This works but feels evil. Is there a better way?

A bookingLine.clear() is no good as it may have other already saved InvoiceLines linked to it.

Thanks.

+1  A: 

The constraints on the database are created from the structure of your objects. In other words, if you violate the constraints there's something wrong with your object relationships. For example, you've got A pointing to B but no B point to A, or you've specified some relationship to not allow nulls and there is a null.

For that reason you should consider not relying on the constraints to keep your object model consistent. It would be better, and less error prone if in fact you made sure that your object model was consistent by checking the object model, rather than relying on the database to do that.

Furthermore, it is never a good idea for the exception to not be an exceptional case. Exceptions are there to handle errors, they should be a "normal" path of operation.

For this reason, in terms of your example, I would suggest that you rather check for the currency, and if it's not there, not add your data to the persistent store.

Michael Wiles
+1 I often do that as well, however the reason for the question was to determine if someone had a good approach for rolling back. You can't always predict every outcome and it's often nice to rely on Exception handling.
Damo