OpenJPA
  1. OpenJPA
  2. OPENJPA-444

Unnecessary updates during flush operation.

    Details

    • Type: Bug Bug
    • Status: Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: 1.0.1
    • Fix Version/s: None
    • Component/s: kernel, UnenhancedClasses
    • Labels:
      None
    • Environment:
      OpenJPA without field tracking. (JDK 5.0 )

      Description

      OpenJPA performs unnecessary updates during flush operation. For example:

      When we are trying to persist versioned entity with collection of other entities:

      Article 1-------------* Quantity ( Relation owner. )
                            • CODE *************

      EntityManagerFactory factory = Persistence.createEntityManagerFactory( "testjpa", System.getProperties() );
      EntityManager em = factory.createEntityManager();
      em.getTransaction().begin();

      Article a = new Article();
      a.setName( "atricle" );

      Quantity q = new Quantity();
      q.setName( "quantity" );
      q.setArticle( a );

      a.getQuantities().add( q );

      em.persist( a );

      em.flush();
      em.flush();

                              • END OF CODE ************

      Following queries are generated after first flush operation:

      SELECT SEQUENCE_VALUE FROM OPENJPA_SEQUENCE_TABLE WHERE ID = ? FOR UPDATE [params=(int) 0]
      UPDATE OPENJPA_SEQUENCE_TABLE SET SEQUENCE_VALUE = ? WHERE ID = ? AND SEQUENCE_VALUE = ? [params=(long) 2651, (int) 0, (long) 2601]
      SELECT SEQUENCE_VALUE FROM OPENJPA_SEQUENCE_TABLE WHERE ID = ? FOR UPDATE [params=(int) 0]
      UPDATE OPENJPA_SEQUENCE_TABLE SET SEQUENCE_VALUE = ? WHERE ID = ? AND SEQUENCE_VALUE = ? [params=(long) 2701, (int) 0, (long) 2651]
      INSERT INTO Article (id, name, version) VALUES (?, ?, ?) [params=(long) 2601, (String) atricle, (int) 1]
      INSERT INTO Quantity (id, name, version, article_id) VALUES (?, ?, ?, ?) [params=(long) 2651, (String) quantity, (int) 1, (long) 2601]

      Everything looks ok but after next flush we will get version update:

      UPDATE Article SET version = ? WHERE id = ? AND version = ? [params=(int) 2, (long) 2601, (int) 1]

      I am not completely sure that this analysis is correct, but I hope it helps anyway:

      During persist operation SaveFieldManager sets collection of quantities as unloaded and unsaved field (Because this field is mutable). During next flush, StateManager checks which fields are dirty by comparing its values to those stored by SaveFieldManager. Method dirtyCheck() of the StateManager is responsible for that. (It's necessary taking into consideration fact that I do not use OpenJPA enhancer with JDK 5.0). SaveFieldManager always returns false from its isFieldEqual() method because this collection (Collection of quantities) is treated as unsaved ( Comment from OpenJPA code: if the field is not available, assume that it has changed) so StateManager sets it as dirty and clears its flush bit. Then AbstractUpdateManager try to add UPDATE_ACTION for this dirty collection, but with no effect because this field is not set as updatable and insertable ( I think so. ). In next step AbstractUpdateManager adds UPDATE_ACTION for version field because StateManager is marked as dirty. It causes unnecessary update of entity version field because nothing changed in the database.

      I didn't check how it works with field tracking.

                                            • EXAMPLE ENTITIES ******************

      @Entity
      public class Article {
      private long id;

      private long version;

      private String name;

      private Set<Quantity> quantities = new HashSet<Quantity>();

      @Id
      @GeneratedValue( strategy = GenerationType.SEQUENCE )
      public long getId()

      { return id; }

      public void setId( long id ) { this.id = id; }

      @Version
      public long getVersion() { return version; }

      public void setVersion( long version ) { this.version = version; }

      @Basic
      public String getName() { return name; }

      public void setName( String name ) { this.name = name; }

      @OneToMany( mappedBy = "article", cascade = { CascadeType.MERGE, CascadeType.PERSIST }, fetch = FetchType.LAZY )
      public Set<Quantity> getQuantities() { return quantities; }

      public void setQuantities( Set<Quantity> quantities ) { this.quantities = quantities; }

      }


      @Entity
      public class Quantity {
      private long id;

      private long version;

      private String name;

      private Article article;

      @Id
      @GeneratedValue( strategy = GenerationType.SEQUENCE )
      public long getId() { return id; }

      public void setId( long id )

      { this.id = id; }

      @Version
      public long getVersion()

      { return version; }

      public void setVersion( long version )

      { this.version = version; }

      @Basic
      public String getName()

      { return name; }

      public void setName( String name )

      { this.name = name; }

      @ManyToOne( optional = false, cascade =

      { CascadeType.PERSIST, CascadeType.MERGE }

      , fetch = FetchType.LAZY )
      public Article getArticle()

      { return article; }

      public void setArticle( Article article )

      { this.article = article; }

      }

      1. updatebug.zip
        19 kB
        Adam Hardy

        Issue Links

          Activity

          Hide
          Patrick Linskey added a comment -

          Thanks for the analysis. It sounds about right.

          Show
          Patrick Linskey added a comment - Thanks for the analysis. It sounds about right.
          Hide
          Adam Hardy added a comment -

          I am attaching a stripped-down maven project that demonstrates how OpenJPA fires an update for every select it does when the transaction commits.

          It uses in-memory hypersonic db.

          Check the logging output after running the test. Each select is followed by an update, regardless of whether the entity requires it.

          Originally I thought this was a Spring issue, but when I checked, I found it occurs for the simplest config with programmatic transactions.

          I am using the latest v1.1.0-SNAPSHOT without enhancing the entities first, using JDK-1.5.0_12.

          If you're not familiar with maven, let me know and I'll try to sort out the project so that it works without.

          Show
          Adam Hardy added a comment - I am attaching a stripped-down maven project that demonstrates how OpenJPA fires an update for every select it does when the transaction commits. It uses in-memory hypersonic db. Check the logging output after running the test. Each select is followed by an update, regardless of whether the entity requires it. Originally I thought this was a Spring issue, but when I checked, I found it occurs for the simplest config with programmatic transactions. I am using the latest v1.1.0-SNAPSHOT without enhancing the entities first, using JDK-1.5.0_12. If you're not familiar with maven, let me know and I'll try to sort out the project so that it works without.
          Hide
          Patrick Linskey added a comment -

          FTR, I believe that this only happens when not using enhancement in one form or another.

          Show
          Patrick Linskey added a comment - FTR, I believe that this only happens when not using enhancement in one form or another.
          Hide
          Adam Hardy added a comment -

          When entities are populated with mixed-up data from the wrong columns (as 496 describes), these unnecessary updates write it back to the database. It's a very quick way to munge your data if you're unlucky enough to fulfill the requirements.

          Show
          Adam Hardy added a comment - When entities are populated with mixed-up data from the wrong columns (as 496 describes), these unnecessary updates write it back to the database. It's a very quick way to munge your data if you're unlucky enough to fulfill the requirements.
          Hide
          Craig L Russell added a comment -

          I found that the "before image" was being created prior to populating data from the datastore, so it always appears that the data changed.

          There's no way for a StoreManager to create a new state image because the method is protected.

          I haven't looked at it any more since I decided to use build enhancement but thought that this information might be useful to whoever looks at the issue.

          Show
          Craig L Russell added a comment - I found that the "before image" was being created prior to populating data from the datastore, so it always appears that the data changed. There's no way for a StoreManager to create a new state image because the method is protected. I haven't looked at it any more since I decided to use build enhancement but thought that this information might be useful to whoever looks at the issue.

            People

            • Assignee:
              Unassigned
              Reporter:
              Sławomir Wojtasiak
            • Votes:
              2 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated:

                Development