2

I'm trying to persist many records in one transaction using JPA (EclipseLink). The Entry has a primary key defined on DB.

I have some duplicates in my feed so I know there will be a constraint violation. I want to catch a Constraint Violation Exception for a single item and continue persisting others.

Here is My code:

Entity:

@Entity
@Table(name="TEST")
public class TestEntity {

    @Id
    private String name;

    public TestEntity() {

    }

    public TestEntity(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

Operation:

@Transactional
public void test() {

    List<String> list = Arrays.asList("A", "B","C","D","D","E","F","G");//duplicated D

    for(String name: list) {
        try {
            TestEntity entity = new TestEntity(name);
            logger.info("" + entity);
            entityManager.persist(entity);
            logger.info("Persist done");
            entityManager.flush();
            logger.info("Flush done");
        } catch(RuntimeException e) {
            logger.warn("Entry {}  was skipped due to following exception {}.",  name, e.getLocalizedMessage());
        }
    }

    logger.info("Importing dictionary finished. ");
}

As a result I get constraint violation exception for second D (that's what i expected), but I get this exception for the next items as well and that's the thing I don't understand.

I'm getting: Internal Exception: java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (BBHDEV4.SYS_C0023890) violated

What is causing that issue?

Dominik Kunicki
  • 1,037
  • 1
  • 12
  • 35
  • You have to remove `@Transactional`. It ensures that either everything is saved or no changes are made. – coladict Oct 18 '16 at 14:50

2 Answers2

2

The problem is that the second D is still part of the the same Transaction / Unit of Work, so on the following iteration, hibernate tries to save it again.

To fix this (using vanilla JPA) you can either

  • Create a new transaction per item you want to update.
  • Do the update with a JPA update statement.

With hibernate you have a 3rd option which is to use a stateless session, but I don't know if eclipselink has the concept of a stateless entitymanager

Augusto
  • 28,839
  • 5
  • 58
  • 88
  • Is there any way to remove this entity from current transaction? – Dominik Kunicki Oct 18 '16 at 14:55
  • 1
    not from the current transaction but from the current EM state : `detach()` (called `evict`in hibernate session) – Gab Oct 18 '16 at 14:57
  • What Gab said :) - Just bear in mind this is not a standard JPA call, but rather part of the internal EclipseLink API. – Augusto Oct 18 '16 at 14:59
  • It's a standard JPA : http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#detach(java.lang.Object) – Gab Oct 18 '16 at 15:01
  • 2
    Once you get a persistence exception, you cannot continue to use your EntityManager - its state is corrupted, and the transaction should be marked for rollback. Why not just get a new EM? – Chris Oct 18 '16 at 15:12
  • @Chris you shouldn't ;) at all but you still can... :D – Gab Oct 18 '16 at 15:14
2

Your entity manager maintains a state composed of all managed entities (ie. a persistence context) and the transactional state of its objects.

So after the flush triggering the constraint violation, the D entity is still managed and in stale state and will be flushed again on the next explicit flush call (you shouldn't call flush explicitly) or at the end of the current transaction. you have to manually evict the D entity from the persistence context (detach()) before resuming the loop.

Anyway if all your operations are part of the same transaction and an error is thrown out of the transaction boundaries, the transaction manager will automatically rollback the whole transaction.

A transaction is a unit of work, if you wan't your different operations to be independent, you have to execute them in different transactions. Usually the entity manager scope is the same as the transaction one (at least when managed by the container ie. injected with @PersitenceContext) and you will solve both problem at once.

Gab
  • 7,869
  • 4
  • 37
  • 68