3urdoch
3urdoch

Reputation: 7322

Hibernate: Temporarily exclude a column from and update

Lets say I have a table like this

USER_ID     USERNAME     PASSWORD     DATE_CREATED    DATE_LAST_LOGIN
1           'user1'      'password1'  '12-Jun-2010'   '12-Nov-2010'
2           'user2'      'password2'  '14-Jun-2010'   '12-Nov-2010'

Which is mapped to a POJO class using hibernate like this

@Entity
@Table( name="user" )
public class User {

   private Integer id;
   private String username;
   private String password;
   private Date dateCreated;
   private Date dateLastLogin;

   public User() {
   }

   @Id
   @GeneratedValue( strategy = GenerationType.IDENTITY )
   public IntegergetId() {
      return id;
   }

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

   @Colum( name="USERNAME" )
   public String getUsername() {
      return username;
   }

   public void setUsername(String username) {
      this.username = username;
   }

   @Colum( name="PASSWORD" )
   public String getPassword() {
      return password;
   }

   public void setPassword(String password) {
      this.password= password;
   }

   @Colum( name="DATE_CREATED" )
   public Date getDateCreated() {
      return dateCreated;
   }

   public void setDateCreated(Date dateCreated) {
      this.dateCreated= dateCreated;
   }

   @Colum( name="DATE_LAST_LOGIN" )
   public Date getDateLastLogin() {
      return dateLastLogin;
   }

   public void setDateLastLogin(Date dateLastLogin) {
      this.dateLastLogin = dateLastLogin;
   }

}

This class will be used in two ways, 1. Authenticate a user for login and when the user logs in the DATE_LAST_LOGIN will be updated with the current date. 2. Update user details in a user edit form.

My problem is that I only ever want to update the DATE_LAST_LOGIN field when the user logs in, not when editing a user in the form. If I were to load a User record and then save it without calling setDateLastLogin then that would be fine until the time comes that a user logs in in-between the load and update operations. This will cause the DATE_LAST_LOGIN to be updated in the db but then when the user edit form saves the changes it will override the DATE_LAST_LOGIN with the old, incorrect value.

EDIT:

I don't think I explained the situation fully so here is some more info .... This is specifically in a webapp, the update of the user happens like so.

  1. User record loaded and used to populate an html form
  2. The form is edited and submitted back to the server
  3. The server commits the changes to the database

As it stands I to not query the object from the database again before updating on form submission, i just populate a new object with the fields from the form and save it to the database, this of course means that the dateLastLogin is null at the time of saving the update and so would replace the correct value with null in the DB. This obviously could not be solved by simply synchronizing threads. Before hibernate I would simply have chosen not to update the DATE_LAST_LOGIN field but hibernate is not allowing me to make that design time decision.

An alternative would be to query the object before overwriting with the fields from the form, however this forces me to have to run a query before the update which I would not have to do without hibernate, and this would force me to use the synchronization that was suggested in one answer. The problem with that is that synchronization would only apply in the current application, if I had multiple apps updating the same DB then it would be useless.

Which comes back to my original question, is there a way to exclude the field from the update, if not by simply asking hibernate to do so then perhaps by a different system design.

Upvotes: 3

Views: 5302

Answers (4)

Pascal Thivent
Pascal Thivent

Reputation: 570315

My suggestion would be to use 2 different entities for the user, e.g. User and UserLogin (where the later would extend the regular User and hold the dateLastLogin property) and to use UserLogin during authentication and the short User when editing the user details.

Upvotes: 1

3urdoch
3urdoch

Reputation: 7322

Ok so I came up with my own solution which works for me though I don't know if there is not a better solution at present.

Basically in my POJO I have made the getDateLastLogin field read only by adding updatable = false to the annotation like so

   @Colum( name="DATE_LAST_LOGIN", updatable = false )
   public Date getDateLastLogin() {
      return dateLastLogin;
   }

Then when logging in I bypass the POJO and update the database directly with an HQL update in my login controller a little bit like this

public void login(String username, String password) {
   User user = getUser( username, password );
   if( user != null ) {
      Query userUpdate = getSession().createQuery( "update User set dateLastLogin = current_timestamp where id = :userId" );
      userUpdate.setInteger( "userId", user.getId() );
      userUpdate.executeUpdate();
   }
}

Upvotes: 0

Guillaume
Guillaume

Reputation: 5555

This is a synchronization problem more than an Hibernate one. You should make your two operations (login and edition of user) atomic with respect to each other. That can be achieved by simple Java synchronization (common mutex or lock before entering these operations Java methods) or by database (i.e. Hibernate) transaction management.

Upvotes: 1

javamonkey79
javamonkey79

Reputation: 17765

Can you control this with a switch (boolean) value that you can toggle as needed and then add this check to the setter?

eg:

boolean dateLastLoginUpdatable = false;

public void setDateLastLogin(Date dateLastLogin) {
   if( dateLastLoginUpdatable ) {
      this.dateLastLogin = dateLastLogin;
   }
}

public void toggleLastLoginEditable( boolean newValue ) {
   dateLastLoginUpdatable = newValue;
}

Upvotes: 0

Related Questions