-->
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.  [ 6 posts ] 
Author Message
 Post subject: ! CGLIB conflict - revisited
PostPosted: Mon Sep 15, 2003 3:50 pm 
Beginner
Beginner

Joined: Fri Sep 12, 2003 12:54 pm
Posts: 20
Ok, so I want to be able to "enchance" my persistent objects using CGLIB, in order to provide some of the functionality that I need in my application during runtime.

There are a few obsticals to this when using Hibernate, which are listed below, along with some possible solutions to them, which I'd like feedback on - some of them would entail changes to Hibernate.


Issue 1 - save()
===========
If I pass an instances of an enhanced version of a class to Hibernate in a call to save(), I get a mapping exception, because the enhanced version of the class (which extends the original class) is not mapped for persistence. (Note that the enhanced class can't be mapped, because its class name isn't known until runtime).


1a- the Mappings class, instead of simply returning null when a class isn't found to be mapped, could look at the class's super-class, to see if it is mapped, and use that. This behavior could somehow be configurable...

1b- an alternative could be: have the application get the Mappings from the Configuration (by createMappings() ? -- does this give a handle to the same mappings that will be used by the session ?) and then manually place in a mapping for the enhanced class, mapping it to the same PersistenClass instance as is there for the original class.

1c- another alternative could be: alter the Configuration() api to have another "addXXX" method, with which you can inform it that one class should be handled equivalently to another class. (i.e. that my enhanced class should be handled as the original class).

1d- provide a new mechanism on the Configuration api for mapping a class at runtime (not getting the mapping from a file / stream / document) - via a simple bean-like object that holds all of the mapping info. I could then set on it the enhanced class (which is known at runtime) as the type for the mapping.

1e- some other way that I haven't thought of ???


Issue 2 - load()
===========
If I pass an instance of an enhanced version of a class to Hibernate in a call to load() I get the same problem as above with save(). Or if I only tell load() the type of class I want to load, then I also get the same Mapping exception as with save() - or if I pass the parent class as the type, then I obviously don't get my enhanced version...

2a- Hibernate could be altered to look at super-classes for mappings such as in "1a" above.

2b- Hibernate could be altered to let the user provide a factory to be used for producing instances of a class. For example, I could map the persistence for class "Foo", and tell hibernate what to use as a factory for producing instances of foo (rather than Hibernate calling newInstance() as it currently does). Then my factory can produce the enchanced version of the class. Perhaps the "Configuration.addClass()" method could be overloaded to also take a "factory" instance for classes that should be produced by a factory, rather than by newInstance(). It seems this "factory" could be useful to people wanting to do a variety of things - not just CGLIB enhancement.

2c- I could make a ClassPersister that knows how to use a factory to produce the enhanced version (for example, extend EntityPersister and over-ride the instantiate() method). Then map this new persister to be used for the (non-enhanced) class. This has the problem that only the type (class) of the Persister is specified in the mappings file, not an instance of a Persister -- so static members would somehow have to be used on the persister class to get it a handle to the factory to use to produce the enhanced classes (since they only exist at runtime).

2d- some other method I haven't thought of ???



Issue 3 - load()
===========
If I have a class that contains a collection of another class, and the class of the objects in that collection need to be enhanced, then when I load() the 'containing' class, I have no control over what class is instantiated as the instances in the collection (I would want enhanced versions as the instances).

3a- same as 2b

3b- same as 2c


=================================


I'd really appreciate any feedback anyone can provide. Or suggestions for alternative approaches / or pointing out Hibernate features that I haven't noticed.

The solutions I personally like best are 1c and 2b - which require small changes (from what I can tell, looking through the Hibernate code) which I would be willing to contribute as a patch for some commiter to integrate. - So comments from the Hibernate developers would be especially appreciated.

thanks,
James


Top
 Profile  
 
 Post subject:
PostPosted: Wed Sep 17, 2003 2:04 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
Can you give us some real life sample of your enhancement needs.
Here is my point of view. Mixing your application business rules with the persistence engine rules mecanism seems to be a quite bad design.
I cannot figure out business needs at runtime.

I would naturally think of a CGLIB wrapping of the hibernate session object allowing you to let thibernate deals with classical pojo objets and let you application deals with wrapped or enhanced objects. The hardest stuff is what you pointed in your third issue. Would be a lot of work/ovenhead to encapsulate every reached object of the primaty entity.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Sep 17, 2003 3:07 pm 
Beginner
Beginner

Joined: Fri Sep 12, 2003 12:54 pm
Posts: 20
epbernard wrote:
Can you give us some real life sample of your enhancement needs.


Sure, here's an example:

One of my model objects is a shopping cart. The Cart object obviously has various methods for manipulating the contents of the cart, and for assigning memo's, naming the cart, etc. etc. Naturally it has an addLineItem() method on it - which adds a LineItem object to a collection (list) on the Cart.

I need the Cart (and LineItem) to be persistent, and I've made the great choice of using Hibernate ;-). And I want to build my "shopping cart system" so that it is essentially a re-usable library that I can plug into various applications.

The various applications have many similarities, but have differences in buisiness rules. For example, the DoD has business rules to make sure that the items in the cart are "compatible" with each other. For instance, rules like if you're got a weapons system in your cart, then you can't also put combat boots in the cart, but you can put aircrapt canopies in the cart. Or if you're adding a propeller to your cart, you better only have propellers already in it (or empty). Another customer has wildly different rules such as if you're adding an X to the cart, then automatically add three Ys for every X to the cart. etc. etc.

So the rules don't *change* during runtime, but they are dependent on application configuration and therefore aren't *known* until runtime. If have my controller layer use a factory to produce instances of model objects, than it can produce them of an "enhanced" version of the class, that has the additional logic that needs to run during the various method calls on the objects (such as on Cart.addLineItem()).

The reason enhancement is important rather than using just proxies is that some of the objects have re-entrant methods. For example, if I were to call Cart.doX() on a proxy, the proxy could run the business rule, and then run the underlying doX() if there is no exception. But if the doX() on the real object calls doY() on the object, then the rule that should be enforced when doY() is called is circumvented, because the object didn't call doY() on the proxy. Thus enhancement is the choice over proxies.... but that leads to the issues described in this thread's original message. (of course, using proxies would solve some of the persistence issues, but not all, or at least not elegantly, as you noted - and would have the problems I just pointed out as far as business rules are concerned).

So then you might ask: why not have a biz delegate class in the controller layer of the app have an "addLineItem()" method on it, and have it enforce the rule, and then manipulate the model objects? The simplest answer is that we're trying to adopt the principles of Streamlined Object Modeling - which stresses the importance of keeping model objects responsible for enforcing the rules that are related to them - so the encapsulation is more tightly enforced - keeping the object solely responsible for its own state, rather than puting logic in other objects than manipulate its state. Or, in other words, have a business model (set of objects) that know have to govern themselves, rather than a set of object models that are just data structures (which is what is done in most Java apps - by architects (myself included) having made "manager" type objects that directly manipulate the model objects (setX(), etc.), rather than calling business methods on the model objects (doX(), etc.)).

Anyway, this is our first whole-hearted "go" at trying to apply the advice/techniques of the Streamlined Object Modeling approach, and there have been a number of stumbling blocks trying to get the principles (which sound good on paper) applied into a clean/reasonable implementation, which includes code re-use..

Advice appreciated...
james


Top
 Profile  
 
 Post subject: Re: ! CGLIB conflict - revisited
PostPosted: Wed Sep 17, 2003 4:29 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
jhouse wrote:
1d- provide a new mechanism on the Configuration api for mapping a class at runtime (not getting the mapping from a file / stream / document) - via a simple bean-like object that holds all of the mapping info. I could then set on it the enhanced class (which is known at runtime) as the type for the mapping.

Actually you can build the DOM model at runtime and apply
Code:
Configuration addDocument(org.w3c.dom.Document doc)


Top
 Profile  
 
 Post subject:
PostPosted: Wed Sep 17, 2003 4:48 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
jhouse wrote:
So the rules don't *change* during runtime, but they are dependent on application configuration and therefore aren't *known* until runtime.

Rule processing engine seems to be shared by every particularized cart, so why not store runtime specific rules into a class attribute (initialized at startup) and process it. => no need for sub/enhanced classes.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Sep 17, 2003 10:38 pm 
Beginner
Beginner

Joined: Fri Sep 12, 2003 12:54 pm
Posts: 20
Building a DOM at runtime is a possible solution - as far as providing the mapping goes - thanks for that idea. It still leaves the problem with load(), unless I only use the version of load() where I pass the instance already made, but then I still have problems with instantiating the objects in contained collections...


The suggestion of puting a static member on the classes, that hold links to the rules is a valid one - one I've been considering - however it breaks down a bit if polymorphism is ever introduced.

I appreciate the time you've put reading my fairly lenghtly posts, and making these suggestions - thank you.

Any other suggestions are very welcome.

I'm also wondering if there won't be similar issues with tighter integration with JBoss & Hibernate (with the new relationship announced today) - since JBoss makes use of proxies (interceptors), enhancements, AOP, etc.

comments from any Hibernate folks on the following?:

Would it really be a big deal to:

1) add a construct to Configuration that lets you relate one class's mappings to another (i.e. tell Hibernate that: if you ever have to persist this class, act as if it is this other class that you already have mappings for).

2) add a construct to relate a "factory" to a class's mappings, from which Hibernate should produce new instances of that class during load() operations.

I'd be happy to donate this work if it would be accepted.

thanks,
james


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 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:
cron
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.