-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 
Author Message
 Post subject: Why does merge() cause a select as well as update?
PostPosted: Tue Dec 05, 2006 10:36 am 
Newbie

Joined: Tue Dec 05, 2006 10:23 am
Posts: 4
I have asked this question on the JBoss EJB3 forum but have not received a response. Hopefully someone will be able to answer here.

When EntityManager.merge() is called to update a detached entity, a select is executed against the database as well as an update.

It seems to me the select is an unnecessary hit against the database.

Why does ejb3/hibernate do this? Is there a way to prevent the select?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 05, 2006 10:57 am 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
http://www.hibernate.org/hib_docs/v3/re ... optimistic

Essentially, if you do not have a version or timestamp field, Hibernate must check the existing record before updating it so that concurrent modifications do not occur. You would not want a record updated that someone else modified after you read it. There are a couple of solutions, outlined in the link above. But it makes life much easier if can add a version field on each table.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 05, 2006 7:04 pm 
Newbie

Joined: Tue Dec 05, 2006 10:23 am
Posts: 4
The entity does have a version column:
Quote:
@Column( name = "time_updated" )
@Version
public Timestamp getTimeUpdated() {
return this.timeUpdated;
}

The update statement sent to the database does have a where clause checking this column.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 06, 2006 8:50 am 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
Aha. Could you post the appropriate mapping files (or annotated classes)? Maybe I can get some insight then.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 06, 2006 11:09 am 
Newbie

Joined: Tue Dec 05, 2006 10:23 am
Posts: 4
Below is the entity bean.

The app being executed is a load test. It inserts entities in one thread, then reads them (with createNativeQuery) in a second thread, then updates them (with merge) in a third thread.

Code:
@Entity
@Table( name = "msg_out" )
public class MsgOut implements Serializable {

   private static final long serialVersionUID = 5505906394742402108L;

   private long id;
   private String content;
   private int accountId;
   private int outStateId;
   private Timestamp timeScheduled;
   private Timestamp timeInserted;
   private Timestamp timeUpdated;


   @Column( name = "account_id" )
   public int getAccountId() {
      return this.accountId;
   }


   public void setAccountId( int accountId ) {
      this.accountId = accountId;
   }


   @Column
   @Id
   @GeneratedValue( strategy = GenerationType.SEQUENCE, generator = "MsgOut_Id_seq" )
   @SequenceGenerator( name = "MsgOut_Id_seq", sequenceName = "\"MsgOut_Id_seq\"" )
   public long getId() {
      return this.id;
   }


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


   @Column( name = "out_state_id" )
   public int getOutStateId() {
      return this.outStateId;
   }


   public void setOutStateId( int outStateId ) {
      this.outStateId = outStateId;
   }


   @Column( name = "content" )
   public String getContent() {
      return this.content;
   }


   public void setContent( String content ) {
      this.content = content;
   }


   @Column( name = "time_inserted" )
   public Timestamp getTimeInserted() {
      return this.timeInserted;
   }


   public void setTimeInserted( Timestamp timeInserted ) {
      this.timeInserted = timeInserted;
   }


   @Column( name = "time_scheduled" )
   public Timestamp getTimeScheduled() {
      return this.timeScheduled;
   }


   public void setTimeScheduled( Timestamp timeScheduled ) {
      this.timeScheduled = timeScheduled;
   }


   @Column( name = "time_updated" )
   @Version
   public Timestamp getTimeUpdated() {
      return this.timeUpdated;
   }


   public void setTimeUpdated( Timestamp timeUpdated ) {
      this.timeUpdated = timeUpdated;
   }
}


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 06, 2006 1:07 pm 
Regular
Regular

Joined: Mon May 22, 2006 2:30 pm
Posts: 74
Having a version field is not enough. You also have to specifiy an "unsaved value" property. Hibernate checks this to determine if an insert or update is needed. I'm not sure this applies to this particular scenario, however. The version/unsaved value approach is typically for save-update cascades. You can't update a detached entity. You can only update persistent entities.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 06, 2006 3:01 pm 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
If you are simply updating a detached instance, with no possible attached instance of the same row in the current Session, you can use update() or saveOrUpdate() to just reattach the instance (save the updated data). Note that for saveOrUpdate() you may need to specify an unsaved-value. Merge() will always do a read from the database before updating the persistent object if it has not already been loaded. From docs, merge() does:
  • if there is a persistent instance with the same identifier currently associated with the session, copy the state of the given object onto the persistent instance
  • if there is no persistent instance currently associated with the session, try to load it from the database, or create a new persistent instance
  • the persistent instance is returned
  • the given instance does not become associated with the session, it remains detached


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 06, 2006 8:31 pm 
Newbie

Joined: Tue Dec 05, 2006 10:23 am
Posts: 4
To hibernate_user: I'm using EJB3 and from what I can tell there is no option to specify an unsaved value key.

To Ananasi: I missed that bit on merge() in the docs. Thanks.

I regard this to be a fault with the EJB3/Hibernate implementation. In this case where the entity already exists in the DB and I as the developer know that, then it should be possible to update the entity without making a costly call to the DB to retrieve the original.

I will raise it on one of the design/developer forums.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 06, 2006 9:38 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
I answered here.
http://www.jboss.com/index.html?module=bb&op=viewtopic&p=3991830#3991830

_________________
Emmanuel


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.