[ 
https://issues.apache.org/jira/browse/OPENJPA-444?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12710871#action_12710871
 ] 

Craig Russell commented on OPENJPA-444:
---------------------------------------

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.

> Unnecessary updates during flush operation.
> -------------------------------------------
>
>                 Key: OPENJPA-444
>                 URL: https://issues.apache.org/jira/browse/OPENJPA-444
>             Project: OpenJPA
>          Issue Type: Bug
>          Components: kernel, UnenhancedClasses
>    Affects Versions: 1.0.1
>         Environment: OpenJPA without field tracking. (JDK 5.0 )
>            Reporter: Sławomir Wojtasiak
>         Attachments: updatebug.zip
>
>
> 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;
>       }
> }

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.

Reply via email to