Selective/dynamic update with hibernate

Hi we have a User entity with a password field, and two use cases: 1) update the user entity but not the password field, the entity is sent from the UI and has null in the password field. 2) update the user entity including the password,again the entity is sent from the UI.   in both use cases it is assumed that the entity is not attached to the hibernate session.   so using just saveOrUpdate in the first use case will set null to the password field in the DB(I'm ignoring the DB constraints for now), and my understanding is that hibernate merge is not the solution. my question is how to implement it without having to do two round trips to the DB and without having to write HQL statements.   my thoughts where to use the Seppuku pattern: have two entity classes mapped to the same table User UserWP extends User where in User the password fields will be update=false and use this entity in the first use case. in UserWP the password field is update=true and use that for the second use case,actually the rest of the application only knows about User and the the UserDao method updateWithPassword converts User to UserWP and saves it.   is there a more elegant,maybe dynamic, solution?                      

Comments

In my opinion writing 2 entities mapped to the same table is less appropriate. The entities represent the domain model, and in this case you just have 2 different use cases and NOT 2 different business entities.
In my opinion it is better to have two different methods. The former, updates the whole entity (including the password). The latter loads only the password for the User entity using an HQL (select u.password from User u where u.id = :userId) , and set the loaded password into the the User entity.
Another option is to create an entity called UserPassword (which has a separate table containing only the passwords and foreign key to users table) with one-to-one relationship to User entity. Then, considering our cases again, the former use case (which updates the password and the user properties) will send User entity with its updated UserPassword entity. The latter use case (updates only the User entity) will send a User entity but in this the UserPassword entity will NOT be updated

your first solution was the first i thought off but it means two round trips to the DB.
an update statement would cost one trip to the DB but then every change to the User entity requires changing the update statement.

it's strange that hibernate doesn't have a support for that, something that would be dynamic.

anyway the first solution is probably easier to implement.

User userFromDB = getObjectById(user.getUserId(),User.class);
user.setPassword(userFromDB.getPassword());
getHibernateTemplate().update(user);

Thanks.

Two comments:
1. Please note that your code above might be problematic, and in my opinion will throw an exception since you associate 2 objects with the same id to the same session (both userFromDB and user).
As suggested, you can select only the password with the HQL above, and then update the "user" with the new password.

2. Regarding the performance issue - theoretically you are right, but...
Lets face it, how many updates do you expect to have on your system per minute ? If you have less than a few dozens, the extra select is probably not an issue. If its more frequent, you can use query cache, and reduce these extra hits on the DB.

there shouldn't be too many updates on a user entity and thus we can pay the two round trips.
regarding the code snippet: it works, but i agree its problematic, will hibernate merge be better?

The fact that it works might indicate (yet!) another problem... Missing transaction layer on top of the service layer. I'll try explain...

The fact you didn't see the exception indicates the loadedUser and the updatedUser are loaded on different session and transaction ==> The service layer has no wrapping transaction layer.

This absence of transaction definition, caused your DAO calls (through HibernateTemplate) to run on a separate session and transaction, even-though these calls are aggregated, and logically grouped, on the same service method, as happened in your case. This is wrong from both architecture and performance perspectives.

HibernateTemplete (malicious creature in my opinion) creates a session and transaction in case they do NOT exist on the current thread.

My recommendations are:
1.Create a transaction aspect on top of all the service layer (you can do it with regular expression which take all “com.mycomp.service.package.layer.*Service” classes.
2.Stop using , and throw away the evil HibernateTemple from the DAO layer. The Hibernate implementation of this DAO layer can (better say should) use the Hibrenate api only – no Spring classes in this layer. Simply inject the SessionFactory into your DAO layer and use clean vanilla Hibernate code.
3.After you get the the exception above (good point – you are running on the same session!), correct the code by loading the password with HQL and set it into the (attached) User entity.

Small revolution, but it worth the effort...

your right about the absence of a transaction layer, this is a customer case and they don't use a transaction manager with spring/hibernate, i didn't ask yet why.
so i'll follow your our suggestion ,but with my code above,will merge do the work with a transaction layer?

this leads us to another subject,what is the prefered dao architecture for a spring+hibernate application?
JPA with spring looks tempting, how do you explain why it is better to use direct hibernate api and not spring classes ? if you do it by the spring book it should be ok,isn't it? actually if you use HibenateTemplate with HibernateCallback you use hibernate api.

Using the merge() rather than update() api will do the job on both non-transactional and transactional service layer.
Regarding Spring HibernateTemplate usage - In my opinion the DAO layer should be clean from Spring code. Since the introduction of Hibernate's contextual session (http://www.redhat.com/docs/en-US/JBoss_Hibernate/3.2.4.sp01.cp03/html/Re...), it has became easy to implement DAO layer clean from Spring polluted classes. Simply inject the SessionFactory into your DAO beans and use sessionFactory.getCurrentSession() method.
From my perspective, using HibernateTemplate is a kind of invasive step pf Spring into the DAO layer without any real need. The reason for this class HibernateTemplate comes more from historical reasons, when Hibernate 2 exception hierarchy was derived from compiled Exception class, and you wanted to write DAO calls inside the service layer, without "polluting" your code with Hibernate code and compiled Hibernate exception... probably NOT your "cup of tea" in a modern Hibernate 3.x application.
You can also (some will say “should”) use JPA api calls rather than Hiberanet api. In this case you can inject the EntityManager or EntityManagerFactory instead of the SessionFactory. Choosing this way will keep your code more “standard”, but from my experience you will get to Hibernate api in many cases since the JPA standards lacks some important features like Criteria api.

there's yet another (maybe a bit naive) solution:


public class User {

    // ...
    private String pswdHash.

    public User (String psswd, ...) {
        setPassword(hashPassword(psswd));
        // ...
    }

    private get/set PasswordHash() {
        // ..
    }

    public void changePAssword(String psswd) {
       this.pswdHash = hashPassword(psswd);
    }

    public boolean login(String psswd) {
      return this.pswdHash.equals(hashPassowrd(psswd);
    }
}

this way you don't have to worry about having the user carry the password all time.

after playing with that a little this is my solution:
the password field is not updatable,this way there is no way to mistakenly update the password with hibernate, and i don't have to wary about overriding the password with the encrypted one from the DB every time some other user details are updated.
the password can be changed only by calling a service method changeUserPassword, the plain password is encrypted in the service layer(using jasypt) and the encrypted text is then updated to the user table with a simple update hql.

there is still the issue that every time a user is fetched it is fetched with the encrypted password, which i think is unsafe.
i didn't understand Adi's solution for that, the only way i can think of is to make the field a lazy one-to-one relationship. lazy fetch with a primitive String doesn't work,if there was a way to tell hibernate that a field is never fetched that would do the trick.
or a much stronger solution where the passwords are stored somewhere else then the users table with no hibernate relation to the User.

Thanks.

Encrypt passwords should be done using one-way techniques, this is, digests. This is because, there is no reason for a password being decrypted. Both MD5 or SHA-1 will be adequate choices for password digesting, although applying these algorithms will not be enough. I strongly recommend reading this http://www.jasypt.org/howtoencryptuserpasswords.html