-->
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: TemporalObject elegantly mapped
PostPosted: Mon Dec 10, 2007 7:28 am 
Newbie

Joined: Mon Dec 10, 2007 6:05 am
Posts: 2
Hi all,

Excerpt:
I can't elegantly map a TemporalObject using Hibernate annotations without importing the database design in my object design.

Long story:
I've run into a difficult mapping problem, and as a long time Hibernate user, I feel that Hibernate should be able to solve it elegantly. I just can't find how to do it with the JPA annotations. I've bought the latest Hibernate in Action book (and read it), but I still can't find a solution...
Please bear with me.

So, the problem is simple: A person has an address, but the address self is immutable. A person lives for a certain time at that address and then moves on. So I want the current addresses of everyone, and the historical addresses with the time period that a person lives somewhere. On top of that, two or more persons can live at the same address at the same time.

So, I figured out I needed a TemporalAddress, as Fowler calls it, and the database design looks like:

Person 1 <---> n TemporalAddress n <---> 1 Address

TemporalAddress is just an in between table with the following fields:

TemporalAddress
--------------------
Id
personId
addressId
startDate
endDate

If the endDate is NULL, it's the address where the person is currently living. If the endDate is NOT NULL, it's a historical address.
An address is immutable... So changing a person's address results in a new Address record (if that address is not already recorded) and a new TemporalAddress record.

Now, with JPA annotations, I'd like to 'include' the address fields in the person object. It's not elegant to have to use the person.getTemporalAddress().getAddress().setStreet(...) stuff, as this would import the database design into my object design.

So, a Person is defined like:

class Person {
private int id;
private CurrentAddress address;
...
}

And CurrentAddress is a class with the fields of both the TemporalAddress and the Address fields:

class CurrentAddress {
private Date startDate; // From the Temporal table
...
private String street; // From the Address table
private Person person; // Parent reference
}

Moreover, changing an address requires the, creation of a new Address and TemporalAddres object (and record). This would also require isDirty checks etc. (btw, there's no such thing as to get Hibernate's isDirty state for a pojo, is there?).

Main reason I need to do this elegantly is to avoid an extra query for every person I retrieve. (i.e. a select for every address). If I retrieve a person, I'll need that address to be retrieved eagerly (without another sql query).

So I've tried:
1) SecondaryTables: The @Where clause is unsupported for a secondary table.
I could use a SecondaryTable referencing the TemporalAddress table in the Person object like:

@SecondaryTable(
name="TemporalAddress",
pkJoinColumns={@PrimaryKeyJoinColumn(name="personId", referencedColumnName="id")}
)
class Person {
private int id;
private CurrentAddress address;
...
}

@Entity(name="TemporalAddress")
@Where(clause="endDate is null")
class CurrentAddress {
...
}

This doesn't work as hibernate's @Where annotation is not currently supported in combination with secondary tables.

2) In the person table, add a @many-to-one relation to TemporalAddress. This retrieves the temporal address and the address eagerly, but causes the database design to leak into my object model.

3) The primary keys of the Person, the TemporalAddress and the address do not have to have the same value, so a number of one-to-one mappings cannot be used.

So the only option I'm seeing right now is to either customize all SQL (bah!) or to write my own interceptors and tupulizers to transform the values from and to my objects... Which I thing Hibernate is made for...

And all this to be able to use HQL like:

"select p.id, p.name, p.address.startDate, p.address.city from Person"

where the sql results in something like

select p.address, p.name, t.startDate, a.city
from Person p
inner join TemporalAddress t on t.personId = p.id
inner join Address a on a.id = t.addressId
where and t.endDate is null

If anyone has solved this temporal problem elegantly, and is willing to share the solution, I would appreciate that very, very much.

Ronaldo


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 10, 2007 11:13 am 
Newbie

Joined: Mon Oct 29, 2007 2:33 pm
Posts: 16
Location: Cambridge, MA
If you convert Addresses to value types, you can add the startDate to the Address, since it's privately held. I'm hoping that sharing Address objects is not crucial. Then the Address type is good for the current address too. Just link the current address from the appropriate collection's Address object, and update this link as needed.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 10, 2007 11:18 am 
Newbie

Joined: Mon Oct 29, 2007 2:33 pm
Posts: 16
Location: Cambridge, MA
sorry, accidentally reposted--how do you delete a posting?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 17, 2007 6:02 am 
Newbie

Joined: Mon Dec 10, 2007 6:05 am
Posts: 2
eoneil wrote:
If you convert Addresses to value types, you can add the startDate to the Address, since it's privately held. I'm hoping that sharing Address objects is not crucial. Then the Address type is good for the current address too. Just link the current address from the appropriate collection's Address object, and update this link as needed.


Hi,
Thanks for the reply. I'm not sure what you mean though by changing Address to value types... I mean, the address itself is an object with value typed properties (Strings), isn't it?
And an address can be shared if two or more persons live or have lived at that address (defined by the start- and endDate in the temporalAddress table).

For the current address of a person, I'd love to refer to a *Current* address from the person:

Class person {
private CurrentAddress address;
...
}

Where the CurrentAddress extends the Address class, but uses a secondary table to include the TemporalAddress. Something like


// Not sure if I have all the annotations syntactically right, but you'll get the idea
@SecondaryTable (name="TemporalAddress")
@Where(clause="temporalAddress.endDate IS NULL and person=?personId?")
Class CurrentAddress extends Address {
private Date startDate;

@Table(name="TemporalAddress")
public Date getStartDate() {
return startDate;
}
}

But to find the temporalAddress belonging to that Address, you have to
apply the where/filter
"TemporalAddress.endDate is null and TemporalAddress.personId = Person.id" and that @Where clause is not supported yet in combination with secondary tables.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 17, 2007 6:55 am 
Regular
Regular

Joined: Sat Nov 25, 2006 11:37 am
Posts: 72
Not sure if your concept of sharing the address between people will really be working well. What happens if only one of the persons sharing the address leaves. And then moves to another address you already have associated with another person. In my mind you seem to mix together the concept of an address, which is a physical place that changes its identity rarely, and the association of a person with that place for a period of time.

But that is largely beside the point. Have a look if entity hierarchies could solve your problem. Have a base entity "Address" and two subclasses "CurrentAddress" and "HistoricalAddress". Either use the enddate as discriminator (JPA wouldn't allow you to use that field but Hibernate with a custom where clause may) or add a discriminator field.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 20, 2007 6:50 pm 
Newbie

Joined: Mon Oct 29, 2007 2:33 pm
Posts: 16
Location: Cambridge, MA
Let me try again on this topic. Earlier I suggested a collection of value objects, but I see now that this is not supported by JPA, and anyway, does not follow all your modeling setup. So back to considering Address entities, sharable between HistoricalAddresses, and a Person with multiple HistoricalAddresses.

I believe that the class CurrentAddress, with its String street field, should not be part of the domain object graph, because it would necessarily duplicate the string, and also the startDate, from the HistoricalAddress data that needs to be there in general. Consider the normalized object graph is as you originally suggested, with Person <--> HistoricalAddress <--> Address. Also have a ref from Person to one HistoricalAddress for the currentAddress. Then, if the callers want a CurrentAddress object for a Person, just create it on the fly and return it, and in the implementation of this business method, hide away the messy details of navigating the object graph. The whole business of persistence is really part of object implementation, and does not always directly provide the interface used by callers.


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:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.