-->
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.  [ 16 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Best Practice using UserTypes as components (values)
PostPosted: Sun Dec 05, 2004 2:10 am 
Beginner
Beginner

Joined: Thu Jun 17, 2004 11:25 pm
Posts: 21
Location: Los Angeles
Hibernate version:
2.17

I've read the docs (e.g. http://www.hibernate.org/hib_docs/reference/en/html/), I've read the FAQs, I've read Hibernate In Action, I've looked through the forums, but I still haven't found a good answer to my question. Or, more likely, I'm just unwilling to accept the answer I've been given and just want to make sure there is not a better way. I would appreciate any help on any of the questions I raise below. If you want to tell me to RTFM, please give me an URL to go to.

I'm trying to create a smart Domain Model. I've got a business object of Customer. Customer has Name, Address, PhoneNumber. all 3 of which are values, not entities. All 3 also have validation semantics, e.g. you cannot create a PhoneNumber from a String that only has 4 digits.

Now I have some choices. Should PhoneNumber be immutable? I think so, since there are no operations on it in this application other than set, read, and query. In fact, it is merely a String with a validation rule and canonicalization rule wrapped around it. Name and Address are similar, except they are composed of other smart domain objects, like Street and ZipCode. Nearly (but not entirely) all the domain objects are Strings at the finest grain.

Another good thing about them being immutable is that I don't have to worry about having an invalid ZipCode object. If I have a ZipCode object, it represents a valid ZipCode. The only way to set a new ZipCode is with another ZipCode or by creating one with a String, but if the String doesn't represent a valid ZipCode, the create fails and you are left with null. Otherwise, I have to create an internal state about whether or not this ZipCode is valid and check it everywhere I use it.


The thing is, with Hibernate, even though I'm using these only as values, if I make them immutable, then as far as I can tell I have to create UserTypes for them, and the UserTypes are more complicated than the types themselves. I can't just map them as component properties, even with access=field, because they don't have default constructors. (If they did have default constructors, I'd be back to having to worry about the objects being invalid.) Hibernate In Action (p. 40) tells me I should use the Validatable interface, either.


* Is it true that the best practice is for me to write UserTypes for every one of these types (Name, Address, ZipCode, PhoneNumber)? Isn't their an easier way? If there is, please tell me about it.

If I really do have to create all thouse UserTypes:

* What should I do with nulls in nullSafeSet/Get()? Is it OK to return null from nullSafeGet()? Is it OK to throw an exception from nullSafeSet()? If so, what exception should I throw? Remember, I want to avoid creating an object with an invalid state.

* Does that mean that Address is a CompositeUserType (since it includes a ZipCode)?

* Why does the JavaDoc for CompositUserType.disassemble() say that it should perform a deep copy? If the object to be cached is Serializable then why can't I just return it?


Thank you in advance for your help.


Top
 Profile  
 
 Post subject: Ooops...
PostPosted: Sun Dec 05, 2004 2:16 am 
Beginner
Beginner

Joined: Thu Jun 17, 2004 11:25 pm
Posts: 21
Location: Los Angeles
Hibernate In Action (p. 40) tells me I should not use the Validatable interface, either


Top
 Profile  
 
 Post subject: Re: Best Practice using UserTypes as components (values)
PostPosted: Wed Dec 08, 2004 12:47 am 
Newbie

Joined: Tue Dec 07, 2004 12:16 pm
Posts: 4
Location: Dallas, Texas, US
Quote:
Should a phone number be immutable?

If any Customer information is likely to change, it would be the phone number.

However, check out p.80 in the book. I discusses the insert and update attributes of the property element. As I understand it, if these are set to false then Hibernate will not alter the corresponding values in the database. I would set the insert to true in order to create the object and update to false so that it could not be changed in the future.


Top
 Profile  
 
 Post subject: (Im)mutable objects vs (im)mutable data
PostPosted: Wed Dec 08, 2004 2:40 pm 
Beginner
Beginner

Joined: Thu Jun 17, 2004 11:25 pm
Posts: 21
Location: Los Angeles
I am not talking about immutable data, i.e. data that will never change in the database and for which therefore updates are not allowed. I am talking about immutable objects to hold the data. Immutable objects (like the String class) are inherently threadsafe and much easier to deal with than mutable objects (like the Date class) in a multi-threaded environment.

Unfortunately, Hibernate cannot create immutable objects directly. Hibernate constructs an object with a no-arg constructor and then initializes it with setter calls. The existance of those setter calls makes the object mutable. This would still be OK of the constructor and mutators could all be made private and all the data types had reasonable null values, but Hibernate requires a non-private constructor (which would lead other people to use it) and between the time the no-arg constructor is called and the setter is called, the object is still (from a class invariant standpoint) unitialized without even a valid null value. (A ZipCode should always have 5 or 9 digits. A null ZipCode should be represented by a null object, not a ZipCode with no digits or all zero digits.) Bad things would happen if Hibernate delivered the object without calling set().


It would be much handier if Hibernate could include an access type of "constructor" in addition to "field" and "property" to indicate that data will be set by calling a constructor with the given data. Until then, my choices seem to be to use mutable objects and pay the thread safety costs or use immutable objects and pay the cost of creating custom UserTypes for each.

I'm hoping there is a better way that I'm simply overlooking. If not, I'd like someone with a lot more experience than I have with Hibernate to say so. I'd also like to hear what people think of adding this as a feature, since immutable value objects is a pattern with a lot of power and popularity that is worth some effort to ease its implementation.

Thanks.
=Jeremy=


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 08, 2004 3:04 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Interceptor.instantiate()


Top
 Profile  
 
 Post subject: Values, not entities
PostPosted: Wed Dec 08, 2004 3:36 pm 
Beginner
Beginner

Joined: Thu Jun 17, 2004 11:25 pm
Posts: 21
Location: Los Angeles
Interceptor.instantiate() will let me create new, mostly uninitialized entities, setting only their ID. I'm trying to extend the value type system and the objects I'm talking about do not have IDs.

I am pretty sure the way I'm "supposed" to do it is by generating a parallel set of UserTypes (one for each of my domain types), but that seems counter to the philosophy of Hibernate being unobtrusive.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 08, 2004 3:40 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Why? Writing a UserType not intrusive in any way to your domain model.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 08, 2004 3:41 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
"obtrusive"... OK


Top
 Profile  
 
 Post subject: Perhaps I should say "less than ideal"
PostPosted: Thu Dec 09, 2004 3:34 pm 
Beginner
Beginner

Joined: Thu Jun 17, 2004 11:25 pm
Posts: 21
Location: Los Angeles
I grant that UserTypes are unintrusive, and this is a good thing. This is, in fact, a very good aspect of Hibernate's design and I thank you for it.

My disappointment is that when I have already taken the time to build a rich set of domain types and I now want to use Hibernate, I have to generate 1-for-1 UserTypes for all those elements in what amounts to a penalty for having decided to make my domain types immutables. It seems to me like that is an unnecessary price to pay to adopt Hibernate.

For CompositeUserTypes I feel you are quite justified in imposing the extra requirements because mapping to and from a composite is complex enought that you want to have thoughtful intervention. For single user types that map to single columns in the database, though, I would much rather be able to simply map them.

In other words, I should be able to say to Hibernate, treat ZipCode like a String and have Hibernate construct my ZipCodes with Strings from the database and persist them as Strings. I should be able to map my Age class to an int and have Hibernate by default use Age.toInt() to get the int value and/or be able to specify the method on Age which returns the mapped type. This seems more in the spirit of Hibernate, a lighter touch than requiring UserTypes all around.


I don't know, maybe what I'm doing is too rare, but I think (especially with Java 1.5/5/Tiger making strong typing more useful) it should be fairly common to create lots of custom vaule types as immutables. I think it is worth considering this kind of support for Hibernate 3 if it doesn't exist already. If it does exist already, it deserves better documentation. :-)


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 09, 2004 6:17 pm 
Pro
Pro

Joined: Tue Aug 26, 2003 8:07 pm
Posts: 229
Location: Brisbane, Australia
Can you query on UserTypes yet? Last time I used UserType, I had to convert it to a CompositeUserType in order to be able to use it's properties in a query. I looked in the manual, but it doesn't seem to say anything.

I suspect you'd be a little upset if you went to all this trouble and then found out you couldn't query on any of your domain model, yah?

_________________
Cheers,
Shorn.


Top
 Profile  
 
 Post subject: Not yet
PostPosted: Fri Dec 10, 2004 1:14 am 
Beginner
Beginner

Joined: Thu Jun 17, 2004 11:25 pm
Posts: 21
Location: Los Angeles
I haven't tried a query on a UserType yet, I had just assumed they worked. On reason was that it says so in Hibernate in Action at the bottom of page 210:

Code:
Query q = session.CreateQuery("from Comment c where c.rating = :rating");
q.setParamter("rating", Rating.LOW, Hibernate.custom(RatingUserType.class));


The other reason I haven't tried it yet is that most of our data types are restrictions on String (e.g. Name, Street, PhoneNumber) and our queries are user-driven queries that check multiple columns, so we are hand-writing queries and using a String as the parameter. That's because I'm on a mission to make computers figure out how people work rather than force people to figure out how computers work. One step is to figure out what the user is searching for (e.g. Last Name vs. Email) by looking at the what the user typed (e.g. does it have an '@' in it).


Top
 Profile  
 
 Post subject: Re: Best Practice using UserTypes as components (values)
PostPosted: Sat Dec 11, 2004 12:09 am 
Newbie

Joined: Tue Dec 07, 2004 12:16 pm
Posts: 4
Location: Dallas, Texas, US
I'm not sure why you can't provide a contructor with a String argument and a no argument constructor along with non-public setters. Even the String class has a no argument contructor that initializes the argument to a default value. If the users of your API want a domain obect with meaningful values they must use the one with the String argument. Hibernate will use the no argument constructor and set valid values (assuming values in the database are valid).

If this is unacceptable, why not create your own Validatable interface and implement it in your domain objects instead of validating in the constructors. You could call validate() after instantiating a new object or, better yet, implement an Interceptor that calls validate() in the onSave() method. This would be less work than writing a UserType for every domain object.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Dec 11, 2004 4:40 am 
Beginner
Beginner

Joined: Thu Jun 17, 2004 11:25 pm
Posts: 21
Location: Los Angeles
jmcquirt wrote:
Quote:
I'm not sure why you can't provide a contructor with a String argument and a no argument constructor along with non-public setters.


What should
Code:
System.out.println(new Street());
System.out.println(new State());
System.out.println(new ZipCode());
System.out.println(new Email());
System.out.println(new Age());

print out? I don't think it should be allowed, hence I cannot allow a non-private no-arg constructor.


jmcquirt wrote:
Quote:
If this is unacceptable, why not create your own Validatable interface and implement it in your domain objects instead of validating in the constructors.


I have done something like that for other reasons. By itself it doesn't prevent invalid objects from being created and doesn't eliminate the need for UserTypes.

Hibernate, by design choice (for which there is a lot of merit) instatiates an object with the default constructor and then initializes its members either with accessors or by direct assignment to the fields. Most of my value types do not have meaningful default states and all of them are immutable. String is an exception in providing a no-arg constructor. (String, with it's special status in the language, is exceptional in many ways) Integer, Long, etc. do not provide no-arg constructors. It's the standard immutable pattern.

I've gone ahead and written the UserTypes. Tedious, but not difficult. What I would have preferred is to map my types to Hibernate's built-in types and have Hibernate create them with constructors that take the mapped type. For example, if I mapped Age to Integer, Hibernate would know to create a new Age object with new Age(Integer). Unfortunately, the reverse direction is less clear, but I'd have accepted a requirement for a (possibly private) toInteger() method. That seems more reasonable to me than a UserType while not precluding me from using a UserType if I don't like that requirement.

Of course, I'm not familiar with the internals of Hibernate enough to know how much pain that would be to implement and what it would break. I trust that Christian will consider the proposal and estimate the costs accurately, but only through participation on a forum like this will anyone be able to get an idea of the benefits.


Top
 Profile  
 
 Post subject: Immutable entities
PostPosted: Wed Aug 24, 2005 7:14 pm 
Newbie

Joined: Mon Jun 06, 2005 11:21 pm
Posts: 10
JGRO, you mention excellent design considerations. I was wondering if this is a problem for values or if a parallel problem exists for entities. Let us say I am making an Order class. I would like my orders to be both read-only from the database (no update privileges) but also immutable as a Java class. Let us say Order does not contain any value types besides strings.

Being immutable, my class should fully establish its state by means of its constructor, and all of its fields should be declared final. The problem comes in loading the class.

As I understand it, Interceptor.instantiate() does no good here because I don't have access to the data. Interceptor.onLoad() does no good as well because it already passes me an instance of the class. But any class instance cannot further be mutated.

Any insight as to how to get around the immutable entity problem (in addition to JGRO's immutable value problem) would be greatly appreciated.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 24, 2005 10:06 pm 
Senior
Senior

Joined: Thu May 12, 2005 11:40 pm
Posts: 125
Location: Canada
I do know that Hibernate will use a private no-arg constructor without complaint in an entity mapping. Unfortunately, a private constructor has the significant downside of disabling proxying.

A few days ago I tried mapping components with a private constructor, but I was not successful -- Hibernate complained of not being able to find the constructor. I am stricken by a strange and uncharacteristic vagueness about this though, so I may just have dreamed it or something.

If you want a really stupid solution to your problem, you can have a public no-arg constructor and within it, abuse the SecurityManager to check that the calling stack frame is in package org.hibernate ^_^


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 16 posts ]  Go to page 1, 2  Next

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.