0

I am developing a Java EE application using GlassFish 4.1, EclipseLink and Derby and noticed today that when I create an new Person entity the version of the newly created entity is 2 instead of 1. The following is my Person Entity.

@Entity
public class Person {

    @Id
    private Long id;
    @Version
    private Integer version;

    private String name;

    @OneToOne
    private Portrait portrait;

    @OneToMany(mappedBy = "person")
    private List<Portrait> portraits;

    ...

}

The following is my Portrait entity.

@Entity
public class Portrait {

    @Id
    private Long id;
    @Version
    private Integer version;

    private String name;

    @ManyToOne
    private Person person;

    ...

}

The following is the part of my stateless EJB createPerson method that results in a newly created Person entity with a version of 2.

Person person = new Person();
person.setName("Fred Smith");

em.persist(person);

Portrait portrait = new Portrait();
portrait.setName("Fred Smith Portrait");
portrait.setPerson(person);

portrait = portraitManager.createPortrait(portrait);  //portraitManager is another stateless EJB.

person.setPortrait(portrait);    // Commenting out this line results in newly created Person entity with a version of 1.
person.getPortraits().add(portrait);

If I comment out the person.setPortrait(portrait) line above the Person entity is created with a version of 1.

I read in other posts that JPA will increment the version of an entity if a field changes or an owned relationship changes. After the method returns from the createPortrait method, I am changing an owned relationship but I am still in the same transaction so don't see why JPA needs to increment the version.

Why does JPA increment the version on my Person entity twice during the same transaction?

As requested in the comments, I turned on EclipseLink logging and observed the following during the normal flow:

  1. Application starts createPerson method.
  2. EclipseLink inserts row into person table.
  3. Application starts createPortrait method.
  4. EclipseLink inserts row into portrait table.
  5. Application completes createPortrait method.
  6. Application associates newly created portrait with person.
  7. EclipseLink updates row in person table setting the version to 2 and the portrait_id to the newly created portrait.
  8. Application completes createPerson method.

If I comment out the "person.setPortrait(Portrait);" command, the log shows me the following:

  1. Application starts createPerson method.
  2. EclipseLink inserts row into person table.
  3. Application starts createPortrait method.
  4. EclipseLink inserts row into portrait table.
  5. Application completes createPortrait method.
  6. Application completes createPerson method.

To be more specific with my question, why does EclipseLink update the version in step 7 of the normal flow? EclipseLink created the row with version 1 in step 2 and there is no way another transaction updated the row because the row only exists in this transaction until it commits.

I suspect the answer is that this is just the way that EclipseLink choose to work under this scenario.

Reed Elliott
  • 223
  • 2
  • 15
  • How can you tell where your transaction boundaries are? You don't seem to be explicitly making transaction boundaries. Have you looked at this discussion: http://stackoverflow.com/questions/8894106/ejb-jpa-transactions – Bryan Pendleton Apr 25 '15 at 19:29
  • I am using container managed transactions with the default TransactionAttributeType of Required, therefore, my transaction starts when I call the createPerson method; is passed to the createPortrait method and ends with the completion of the createPerson method. I suspect that EclipseLink is performing a flush before it calls the createPortrait method which increments the version and then it performs another flush at the end of the transaction which increments the version again. Is this by design? – Reed Elliott Apr 26 '15 at 03:58
  • Sorry, I don't know, but it sounds like a really solid theory. The other place for the extra flush might be the person.setPortrait call itself, because it might need to flush in order to generate the primary key value in order to set up the foreign key reference from person to portrait. – Bryan Pendleton Apr 26 '15 at 15:15
  • 2
    doesn't eclipselink log tell you such information? I know the JPA impl I use tells me when a flush happens (so I can add a log line myself to see when it happens relative to my code) – Neil Stockton Apr 27 '15 at 07:45
  • Turn on EclipseLink logging to find out what is happening https://wiki.eclipse.org/EclipseLink/Examples/JPA/Logging – Chris Apr 27 '15 at 12:50
  • Thank you for the feedback and the link. I will turn on logging and see if EclipseLink reveals the truth. – Reed Elliott Apr 27 '15 at 16:16
  • @ReedElliott please update your question after you got the logging results. – MRalwasser Apr 28 '15 at 15:06

0 Answers0