-->
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.  [ 5 posts ] 
Author Message
 Post subject: Updating object while changing its class (complex problem)
PostPosted: Sat May 03, 2008 12:05 pm 
Beginner
Beginner

Joined: Wed Dec 20, 2006 11:21 am
Posts: 20
Hello. This post is somehow related to a previous one (http://forum.hibernate.org/viewtopic.php?p=2384248)

The context: (running latest Hibernate stable version on Linux Sun's JDK 1.6, MySQL)

I have an UserAccount class that is extended by an AdminAccount class. Both are mapped to Hibernate, I use one table per class hierarchy.
The UserAccount has an unique constraint on the userName property.

For a certain operation I wish to transform an existing UserAccount object into an AdminAccount one.

What I tried so far:

* My first approach was to delete the old object prior to recreating the new one. The problem with that is that when I delete the old object, the session needs to be flushed at this point. If I don't flush it, when the transaction is commited Hibernate tries to insert the new object first and the DB complains that the unique constraint is violated - it is indeed as the new admin account object has the same userName as the older user account object which is to be deleted (unfortunately Hibernate schedules the object insertion before the object deletion).

Flushing the session when deleting the old object has proven to be a pain, as I want to be able to rollback my whole transaction if certain validations I perform go wrong. Thus this whole approach seems impossible to me, unless someone can advise me a better way.

* So my second approach was to try to update the original user account object. My code looks like (in Groovy):

def hibernateSession = sessionFactory.getCurrentSession()

UserAccount userAccount = UserAccount.get(id)

// In the following constructor most properties are copied

AdminAccount adminAccount = new AdminAccount(userAccount)

// Now copying id and version to the new object so that Hibernate thinks it is a detached object, not a transient one, and issue an UPDATE instead of an INSERT

adminAccount.id = userAccount.id
adminAccount.version = userAccount.version

// Evicting the previous object from the session

hibernateSession.evict(userAccount)

// Updating the object

hibernateSession.saveOrUpdate(adminAccount)

Unfortunately this also does not work. The object seems to be correctly updated in the DB, but its class stays an UserAccount (eg, in the SQL table, the column for the class discriminator did not have its value updated).

Why is this the case? I create an AdminAccount object, so Hibernate should know it is trying to update with an AdminAccount object... but maybe it somehow remembers that the row corresponded to an UserAccount before and thus issues the UPDATE without taking into account the new class...

I really need some insight into this problem. I have spent lots of hours googling and trying a lot of different things, so if someone can lend a hand it'll be really appreciated :)

Jean-Noel


Top
 Profile  
 
 Post subject: suggestions
PostPosted: Sun May 04, 2008 3:29 am 
Senior
Senior

Joined: Sun Jun 11, 2006 10:41 am
Posts: 164
Hi,
First, I've been working for many years in IT development, and let me tell you this: it is not a good practice to inherit classes based on permissions. You should separate the USER ACCOUNT from the USER ROLES. I won't count the reasons at the moment, but as your project grows more and more complex, I'm guessing you'll find that out. A better approach would be to design a permission model for users with appropriate columns/tables.

The simplest solution: create some "IS_ADMIN" boolean flag for any user account.

A more complex option: create an ACCESS_CONTROL record with many boolean flags - each specifying what app-level permissions the user has (i.e. IS_ADMIN_ROLE, IS_MANAGER_ROLE, IS_SCHEDULER_ROLE, etc).

An even more complex approach would be to implement resource-specific permissions, i.e. a user may have ROLE1 on entities 1,2,3 of class EntityA, and ROLE2 on entities 40,50,60 of class EntityB, and so on.


Having said that, why is flush() a problem from a transactional point of view? AFAIK, flush does not imply commit.


Top
 Profile  
 
 Post subject:
PostPosted: Sun May 04, 2008 6:28 am 
Beginner
Beginner

Joined: Wed Dec 20, 2006 11:21 am
Posts: 20
Hi, Your advice is interesting, and I would be glad if you could list some of the main reasons why my design is a dead end (or link to a good url explaining that).

I actually realised that as I was using MyISAM MySQL tables, transactions were not working as they should. But with InnoDB I get real transactions. So in fact, the first approach can work. I am still bothered about the result of the second approach however, which I do not fully understand.


Top
 Profile  
 
 Post subject: comments
PostPosted: Sun May 04, 2008 7:21 am 
Senior
Senior

Joined: Sun Jun 11, 2006 10:41 am
Posts: 164
Hi,
Your design is not dead-end. However, since you chose to use ORM tools, you are somewhat "limited" to the attributes of BOTH persistence layer and runtime layer (JVM in this case). i.e. since a Java object cannot change its runtime type on the fly, there is no "standard" mapping that could handle this at db-level. Therefore, your first approach sounds like the best option.

In your case, if there's a genuine reason for changing inheritence models on the fly, you should also consider not using a "standard" ORM. However, based on the scenario you describe (one that I'm most familiar with), it sounds like you can easily use standard ORM if you think what roles and permissions are, how you administer them during the lifetime of the application, how you use them to allow/restrict access, etc.

You asked for references. That's easy - just look at any OS admin guide, any App Server security admin guide, and so on. They all pretty much base their design on similar and proven concepts.


Top
 Profile  
 
 Post subject:
PostPosted: Sun May 04, 2008 11:57 am 
Beginner
Beginner

Joined: Wed Dec 20, 2006 11:21 am
Posts: 20
Hi, Thanks for this insight. You make a very good point: I should be fully aware of the limitations of an ORM tool such as Hibernate. Hibernate has such power that it is easy to overlook its limitations. And as you say, one of these limitations is indeed the runtime layer (JVM), since Java does not allow an object to easily change type at runtime.

So, you're right, there's no reason Hibernate should allow this too (at least not easily). And the approach should be the same as with Java: to change an object type, you are forced to copy the object to a new object (of the new type) first, then maybe delete the old one. Exactly like in Hibernate (the only difference being that in Hibernate, you have to delete the old one prior to saving the new one, usually because of unique business key constraints).

With this knowledge, I can maybe review my design and adapt it more to the tools I chose to use. Thanks!


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 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.