Reputation: 7322
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.
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
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
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
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
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