0

Using Spring 2.2.5 with Spring Data / JPA / Hibernate.

When using the CrudRepository with either CrudRepository#saveAll() or a loop structure with individual CrudRepository#save(), it seems impossible to tell which of the elements violates a constraint.

Assuming this method:

@Transactional
public void storeAll(Collection<Attribute> attributes) {
    repository.saveAll(attributes);
}

The returned DataIntegrityViolationException with a nested hibernate ContraintViolationExceptionis being thrown. There seem to be no references to the entity in said exceptions.

Using a loop doesn't work either:

@Transactional
    public void storeAll(Collection<Attribute> attributes) {
        for(Attribute attribute : attributes){
            try{
                repository.save(attribute);
            }catch (Exception e){
                ///
            }
        }
    }

The exception inside the loop isn't being catched (the save method doesn't seem to throw anything). Instead, it's thrown when returning from the method. Maybe due to optimization of some sort?

Michael
  • 695
  • 2
  • 12
  • 36
  • 1
    What's the full exception and chain of causes, does it not tell the specific reason? A stack trace from the exception might help – xtratic May 28 '20 at 14:55
  • I'd recommend you to debug your code if you're using [Eclipse](https://www.baeldung.com/spring-debugging) (or STS) or [Netbeans](https://stackoverflow.com/questions/41633183/how-to-debug-spring-boot-with-netbeans-via-maven) – Guilherme Barboza May 28 '20 at 15:16
  • Maybe I should elaborate. I know exactly which item violates the unique label constraint. Point is: I can't find any method to get the entity for the thrown exception is order to return a proper error to my API. The exceptions don't seem to carry any reference to the entity and it's not possible to catch the exception in a loop. – Michael May 28 '20 at 18:19
  • 1
    Hibernate only relies to you whatever error the underlying RDBMS reported. If the RDBMS doesn't include additional info on what specific constraint failed and with which value, then I'm afraid you're out of luck. That being said, you can try flushing after each call to `repository.save()`, this should cause constraint violations to be triggered when Hibernate tries to reflect the changes in the DB. Simply use `saveAndFlush` instead of `save` – crizzis May 28 '20 at 20:49

1 Answers1

0

If your objects have an identifier (an ID, unique code, etc), you can surround the code with a try/catch block and see which objects are causing the problem:

private void saveMyObjects (List<MyObject> myObjects) {
  for (MyObject myObject: myObjects) {
    try {
      this.myObjectRepository.save(myObject);
    } catch (Exception e) {
      //To be more specific: 
      // } catch (DataIntegrityViolationException e) {
      system.out.println("I have a problem with this object: " + myObject.getMyIdentifier());
    }
  }
}
  • And as I've stated in the original post (maybe not clearly enough), this doesn't work. If the whole method is transactional (all objects or nothing), the error isn't thrown on #save, but after the loop when the transaction is actually performed, giving me a single error for the whole method. This was literally the first solution I tried. – Michael May 28 '20 at 18:17