0

An example from Pro JPA:

@Stateless
public class AuditServiceBean implements AuditService {
    @PersistenceContext(unitName = "EmployeeService")
    EntityManager em;

    public void logTransaction(int empId, String action) {
        // verify employee number is valid
        if (em.find(Employee.class, empId) == null) {
            throw new IllegalArgumentException("Unknown employee id");
        }
        LogRecord lr = new LogRecord(empId, action);
        em.persist(lr);
    }
}

@Stateless
public class EmployeeServiceBean implements EmployeeService {
    @PersistenceContext(unitName = "EmployeeService")
    EntityManager em;
    @EJB
    AuditService audit;

    public void createEmployee(Employee emp) {
        em.persist(emp);
        audit.logTransaction(emp.getId(), "created employee");
    }
    // ...
}

And the text:

Even though the newly created Employee is not yet in the database, the audit bean can find the entity and verify that it exists. This works because the two beans are actually sharing the same persistence context.

As far as I understand Id is generated by the database. So how can emp.getId() be passed into audit.logTransaction() if the transaction has not been committed yet and id has not been not generated yet?

Ekaterina
  • 1,642
  • 3
  • 19
  • 36
  • 1
    Because the ID can be assigned without tx commit. https://vladmihalcea.com/jpa-persist-and-merge/ – Alan Hay Dec 17 '18 at 16:44
  • When I debug the id value it's equal to 0 and then takes the real number after commit (when I check the id in the database it's not 0). – Ekaterina Dec 18 '18 at 17:03
  • Sorry, now there is another result. But how can id be get without the access to the database? – Ekaterina Dec 18 '18 at 17:19

1 Answers1

0

it depends on the strategy of GeneratedValue. if you use something like Sequence or Table strategy. usually, persistence provider assign the id to the entities( it has some reserved id based on allocation size) immediately after calling persist method.

but if you use IDENTITY strategy id different provider may act different. for example in hibernate, if you use Identity strategy, it performs the insert statement immediately and fill the id field of entity.

https://thoughts-on-java.org/jpa-generate-primary-keys/ says:

Hibernate requires a primary key value for each managed entity and therefore has to perform the insert statement immediately.

but in eclipselink, if you use IDENTITY strategy, id will be assigned after flushing. so if you set flush mode to auto(or call flush method) you will have id after persist.

https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Entities/Ids/GeneratedValue says:

There is a difference between using IDENTITY and other id generation strategies: the identifier will not be accessible until after the insert has occurred – it is the action of inserting that caused the identifier generation. Due to the fact that insertion of entities is most often deferred until the commit time, the identifier would not be available until after the transaction has been flushed or committed.

in implementation UnitOfWorkChangeSet has a collection for new entities which will have no real identity until inserted.

// This collection holds the new objects which will have no real identity until inserted.
    protected Map<Class, Map<ObjectChangeSet, ObjectChangeSet>> newObjectChangeSets;

JPA - Returning an auto generated id after persist() is a question that is related to eclipselink.

there are good points at https://forum.hibernate.org/viewtopic.php?p=2384011#p2384011

I am basically referring to some remarks in Java Persistence with Hibernate. Hibernate's API guarantees that after a call to save() the entity has an assigned database identifier. Depending on the id generator type this means that Hibernate might have to issue an INSERT statement before flush() or commit() is called. This can cause problems at rollback time. There is a discussion about this on page 490 of Java Persistence with Hibernate.

In JPA persist() does not return a database identifier. For that reason one could imagine that an implementation holds back the generation of the identifier until flush or commit time.

Your approach might work fine for now, but you could run into troubles when changing the id generator or JPA implementation (switching from Hibernate to something else).

Maybe this is no issue for you, but I just thought I bring it up.

Mehran Mastcheshmi
  • 785
  • 1
  • 4
  • 12