1
votes

I have entities with validation annotations such as @NotNull. I don't know how to prevent a container managed transaction from rolling back in the case of a ConstraintViolationException in a bulk persist operation such as:

public void persistAll(List<TheEntity> entities) throws Exception{

for(TheEntity ent : entities){

em.persist(ent);

}

}

Wrapping the persist operation in a try-catch would not solve the issue because even by catching the Constraint exception, the transaction would be flagged for rollback (none of the other "validated" entities would be persisted). I could isolate the transaction per entity to persist, but I think this will impact a lot on performance (not sure about this, I'm using eclipselink with batch JDBC optimization).

I know that the behavior of the ContraintValidationException is working as the JPA spec mandates (flagging rollback), but I'm not sure if I'm understanding how the eclipselink batch optimization works (does the bulk opeations need to be in a single transaction?).

Thanks for the interest.

Regards.

EDIT: Welp, eclipelink docs state that "Batch writing can improve database performance by sending groups of INSERT, UPDATE, and DELETE statements to the database in a single transaction, rather than individually.", so yes, it needs to be done in a single transaction.

EDIT: I could also inject a constraint validator from the context and disable the JPA validator on persistence.xml, so I can validate the entity list prior the JPA PrePersist operation. However, this will affect other entities that won't need bulk operations on them but still need validation. Ahh! almost there.

1
just to be sure, so for example you need to handle 4 entries and the third one throws exception, you want the first 2 changes to commit and discard the 3rd and 4th?Jacky Cheng
I just want to discard the entity that fired the constraint exception. The rest must be persisted. It also ocurred to me that i can use the flush method, but yet again, not sure about the performance impact.ra2085
to be totally honest, I don't know much about JPA, I am just here to learn plus maybe contribute a bit. You said try catch is not working....are you sure? If you surround the whole operation then it might not work, what about only surrounding the line that throw exception(inside the loop)? That way you might choose to IGNORE the exception and keep moving on with you transaction. You could also add some sort of variable/buffer in catch to record all exception that happens during the loop, and carry out you action for those exception later.Jacky Cheng
Well, the spec dictates that if a ContraintValidationException occurs, the transaction will get flagged for rollback. So once it gets fired up, can't do nothing about it. Yes, I can catch it, but useless at that point.ra2085
this might be a useless comment, but clearly at this specific case you DO NOT want the persistency, than why use JPA :D . Ok, useless comment aside, although this might be a less-efficient way to do things, maybe you could try something like what i suggested, but in the catch part record the which entity is having trouble(by list index?) and then after everything is rollback, if you detect the error buffer is not null, remove the entity from the list and put the list back into the method and run it again? Not sure about the preformance thou.Jacky Cheng

1 Answers

2
votes

You can do manual validation and just skip the entities that are not valid, like this:

// in the class inject validator
@Resource
Validator validator;
...

for(TheEntity ent : entities){
     if( validator.validate(ent).size() == 0) {
         // valid - persist
         em.persist(ent);       
     } else {
        // invalid - skip
    }
}