Mapping a Complex Object

classic Classic list List threaded Threaded
15 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Mapping a Complex Object

Dan Forward
I am new to iBATIS and I am having difficulty mapping our User object, which consists of several custom objects, some of which span multiple columns in the database. However, all the data for the user is stored in one table.

These are the properties of my User object:

private UserID id;
private Name name;
private Gender gender;
private EmailAddress email;
private TelephoneNumber phone;
private LocalDate birthDate;
private Hash passwordHash;
private StaticFileID avatarID;
private OrganizationID organizationID;
private int version;

The database schema looks like this:

create table user (
    id char(22) not null,
    first_name varchar(255),
    middle_name varchar(255),
    last_name varchar(255),
    suffix varchar(255),
    gender enum('Male', 'Female'),
    email varchar(255),
    phone char(14),
    birth_date date,
    password_hash char(22),
    avatar_id char(22),
    organization_id char(22),
    version int(11),
    primary key (id),
    unique key (email)
)

I think I can handle the single-column values by implementing TypeHandlerCallback, but I am baffled how to handle the Name object, which consists of four columns. Any help would be very much appreciated.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Mapping a Complex Object

Guy Rouillier-2
On 12/29/2009 6:22 PM, Dan Forward wrote:

>
> I am new to iBATIS and I am having difficulty mapping our User object, which
> consists of several custom objects, some of which span multiple columns in
> the database. However, all the data for the user is stored in one table.
>
> These are the properties of my User object:
>
> private UserID id;
> private Name name;
> private Gender gender;
> private EmailAddress email;
> private TelephoneNumber phone;
> private LocalDate birthDate;
> private Hash passwordHash;
> private StaticFileID avatarID;
> private OrganizationID organizationID;
> private int version;
>
> The database schema looks like this:
>
> create table user (
>      id char(22) not null,
>      first_name varchar(255),
>      middle_name varchar(255),
>      last_name varchar(255),
>      suffix varchar(255),
>      gender enum('Male', 'Female'),
>      email varchar(255),
>      phone char(14),
>      birth_date date,
>      password_hash char(22),
>      avatar_id char(22),
>      organization_id char(22),
>      version int(11),
>      primary key (id),
>      unique key (email)
> )
>
> I think I can handle the single-column values by implementing
> TypeHandlerCallback, but I am baffled how to handle the Name object, which
> consists of four columns. Any help would be very much appreciated.

You don't say what version of iBATIS you are using.  Since you say you
are new, so I'm guessing you are using iBATIS 3.  You shouldn't need any
callbacks; from your description, a simple ResultMap should suffice.
Take a look at the iBATIS 3 User Guide, particularly the section titled
Advanced Result Mapping.  The example mirrors what you are attempting,
so hopefully you will be able to identify what you need to do.
Specifically, you can map multiple name columns to a Name object using
an association.  Though in reality, since only people have names, I
don't really understand the need to have a separate Name object type.

Write back with specific questions after you've digested the documentation.

--
Guy Rouillier

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Mapping a Complex Object

Dan Forward
Guy Rouillier-2 wrote
You don't say what version of iBATIS you are using.  Since you say you
are new, so I'm guessing you are using iBATIS 3.  You shouldn't need any
callbacks; from your description, a simple ResultMap should suffice.
Take a look at the iBATIS 3 User Guide, particularly the section titled
Advanced Result Mapping.  The example mirrors what you are attempting,
so hopefully you will be able to identify what you need to do.
Specifically, you can map multiple name columns to a Name object using
an association.  Though in reality, since only people have names, I
don't really understand the need to have a separate Name object type.

Write back with specific questions after you've digested the documentation.
Thank you, Guy. You are right, I am using iBATIS 3. It looks like iBATIS 3 uses the TypeHandler interface instead of the TypeHandlerCallBack interface I saw in some online examples. Still, this only works for single-column objects, like the IDs.

The reason for the special Name object is to make it easier to display and sort names, e.g. "Last, First Middle Suffix", "First Middle Last, Suffix", or "First M. Last".

I have other scenarios like this, such as storing dates with millisecond precision. MySQL has only second precision using timestamp columns, so I have to store the millisecond portion in a separate column and use both columns to reconstruct the Date.

The Association tag looks promising, but I am confused by the column attribute. It wants an ID for the Name, but Names are not first-class data objects and have no ID. The Association tag seems to be intended for a one-to-many or many-to-many join. I could query the UserID twice with different aliases and use one for the Name object, but it feels like a hack. Will iBATIS then cache Name objects independent of User objects? I really do not want them cached separately. It is a one-to-one mapping with a User. Is this an uncommon scenario?

I will try implementing it this way, but would still appreciate any insight someone may have who has done a one-to-one, same-table mapping before.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Mapping a Complex Object

Guy Rouillier-2
On 12/30/2009 1:39 PM, Dan Forward wrote:

> The Association tag looks promising, but I am confused by the column
> attribute. It wants an ID for the Name, but Names are not first-class data
> objects and have no ID. The Association tag seems to be intended for a
> one-to-many or many-to-many join. I could query the UserID twice with
> different aliases and use one for the Name object, but it feels like a hack.
> Will iBATIS then cache Name objects independent of User objects? I really do
> not want them cached separately. It is a one-to-one mapping with a User. Is
> this an uncommon scenario?

Associations are made for 1-to-1 relationships.  1-to-many are handled
with collections.  So, using a Name object via association would appear
to work well here since you want to keep the name fields in a separate
object type.  You don't need to retrieve any columns twice.  If
necessary, you can repeat a single database column in a result map, and
do different things with each of the multiple occurrences.  In my case,
or example, I have a site_id column that I map twice; once I store the
retrieved value as is, and in the second occurrence I map it to a Site
object via an association.

However, from your description, you do not want to store an ID with the
Name object, and you do not have to do so.  If you reread the
documentation, it states that you can leave out the ID column.  It
cautions about a performance penalty, but that warning is based on
retrieving additional results from the database via a separate SELECT.
Since you already have all your results from the original SELECT, you
should not incur a performance penalty.

--
Guy Rouillier

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Mapping a Complex Object

Dan Forward
Thank you, Guy, you have been a big help!

This is what I came up with. It works, but not as well as I had hoped.

<resultMap type="User" id="userMap">
        <constructor>
                <idArg column="id" javaType="UserID" typeHandler="UserIDHandler"/>
                <arg column="gender" javaType="Gender" typeHandler="GenderHandler"/>
                <arg column="email" javaType="EmailAddress" typeHandler="EmailAddressHandler"/>
                <arg column="phone" javaType="TelephoneNumber" typeHandler="TelephoneNumberHandler"/>
                <arg column="birth_date" javaType="LocalDate" typeHandler="LocalDateHandler"/>
                <arg column="password_hash" javaType="SHA1" typeHandler="SHA1Handler"/>
                <arg column="avatar_id" javaType="StaticFileID" typeHandler="StaticFileIDHandler"/>
                <arg column="organization_id" javaType="OrganizationID" typeHandler="OrganizationIDHandler"/>
                <arg column="version" javaType="int"/>
        </constructor>
        <association property="name" javaType="Name">
                <constructor>
                        <arg column="first_name" javaType="String"/>
                        <arg column="middle_name" javaType="String"/>
                        <arg column="last_name" javaType="String"/>
                        <arg column="suffix" javaType="String"/>
                </constructor>
        </association>
</resultMap>

I was surprised that I had to specify the javaType for every parameter. Otherwise, iBATIS treated everything as an Object and could not find a corresponding constructor. I then discovered that iBATIS was looking for an Integer argument for the version even though I specified the javaType to be an int. Finally, I had to remove name from the constructor since constructor tags do not support child association tags.

As a side note, I try to follow the recommendation by Joshua Bloch in Effective Java to use static factory methods instead of constructors, so I only have private constructors. I used DefaultObjectFactory as a model to create my own ObjectFactory that first looks for a matching static factory method before looking for a constructor.

One of the reasons I chose iBATIS was that Hibernate put too many constraints on my domain model. It isn't really a POJO if you say it has to have a public constructor, an empty constructor, and setters for every property. iBATIS is less strict, but still has some hoops to jump through. Wouldn't it be nice to have a persistence layer that transparently accommodated the domain model? What if I wanted to use a separate Factory class to create my User objects?

I am willing to help make these changes to iBATIS if I am not missing something that may already be there.

Sincerely,

Dan Forward
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Mapping a Complex Object

Guy Rouillier-2
Comments inline.  Overall, you seem to have made this much more
complicated than it needs to be.  Looking at your database schema from
your original message, all the table columns are simple strings or
number, except for the gender enum.  But you've elected to make every
column a distinct object type.  That is why your solution is more
complex than you might wish.

Because you've elected to transform every column into a distinct object
type, you have to tell iBATIS what those object types are.  It can't
possibly guess at such things.  And that is why you are having to
specify all the javaType's.  If you would have used simple strings and
numbers, you would not have to do so.

On 1/2/2010 5:51 PM, Dan Forward wrote:

>
> Thank you, Guy, you have been a big help!
>
> This is what I came up with. It works, but not as well as I had hoped.
>
> <resultMap type="User" id="userMap">
> <constructor>
> <idArg column="id" javaType="UserID" typeHandler="UserIDHandler"/>
> <arg column="gender" javaType="Gender" typeHandler="GenderHandler"/>
> <arg column="email" javaType="EmailAddress"
> typeHandler="EmailAddressHandler"/>
> <arg column="phone" javaType="TelephoneNumber"
> typeHandler="TelephoneNumberHandler"/>
> <arg column="birth_date" javaType="LocalDate"
> typeHandler="LocalDateHandler"/>
> <arg column="password_hash" javaType="SHA1" typeHandler="SHA1Handler"/>
> <arg column="avatar_id" javaType="StaticFileID"
> typeHandler="StaticFileIDHandler"/>
> <arg column="organization_id" javaType="OrganizationID"
> typeHandler="OrganizationIDHandler"/>
> <arg column="version" javaType="int"/>
> </constructor>
> <association property="name" javaType="Name">
> <constructor>
> <arg column="first_name" javaType="String"/>
> <arg column="middle_name" javaType="String"/>
> <arg column="last_name" javaType="String"/>
> <arg column="suffix" javaType="String"/>
> </constructor>
> </association>
> </resultMap>
>
> I was surprised that I had to specify the javaType for every parameter.
> Otherwise, iBATIS treated everything as an Object and could not find a
> corresponding constructor. I then discovered that iBATIS was looking for an
> Integer argument for the version even though I specified the javaType to be
> an int. Finally, I had to remove name from the constructor since constructor
> tags do not support child association tags.

Already discussed the need for all the javaType's.  I don't know about
the int; I've used them successfully without issue.  I don't understand
what you mean by "I had to remove name from the constructor"; I don't
see a column called "name" in your table.

>
> As a side note, I try to follow the recommendation by Joshua Bloch in
> Effective Java to use static factory methods instead of constructors, so I
> only have private constructors. I used DefaultObjectFactory as a model to
> create my own ObjectFactory that first looks for a matching static factory
> method before looking for a constructor.

I just use JavaBeans instead of trying to do all the data filling from
the constructor.  iBATIS will use the empty constructor automatically.
Any particular reason you want to do the data assignments via constructors?

>
> One of the reasons I chose iBATIS was that Hibernate put too many
> constraints on my domain model. It isn't really a POJO if you say it has to
> have a public constructor, an empty constructor, and setters for every
> property. iBATIS is less strict, but still has some hoops to jump through.
> Wouldn't it be nice to have a persistence layer that transparently
> accommodated the domain model? What if I wanted to use a separate Factory
> class to create my User objects?

Look at page 14 in the documentation.  You can supply your own
ObjectFactory.

As you've discovered, no persistence layer is perfect.  Unless you write
your own, every pre-packaged persistence approach will have some
compromises in order to appeal to a significantly large audience.

If you Google, you'll find several articles comparing Hibernate, JPA and
iBATIS.  If you are starting from scratch and have complete control over
both your database implementation and your Java frameworks, then
something like Hibernate might be appropriate.  In our situation, we
have a pre-existing, not-well-normalized database; iBATIS removes all
the grunt work but still allows us complete control over how we access
our data.

--
Guy Rouillier

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Mapping a Complex Object

Dan Forward
Guy Rouillier-2 wrote
Comments inline.  Overall, you seem to have made this much more
complicated than it needs to be.  Looking at your database schema from
your original message, all the table columns are simple strings or
number, except for the gender enum.  But you've elected to make every
column a distinct object type.  That is why your solution is more
complex than you might wish.

Because you've elected to transform every column into a distinct object
type, you have to tell iBATIS what those object types are.  It can't
possibly guess at such things.  And that is why you are having to
specify all the javaType's.  If you would have used simple strings and
numbers, you would not have to do so.
Thank you for your forthright comments, Guy. I know I could have used Strings and ints, but there is something to be said for type safety and compile-time checking of parameters. Here is the method signature of the sole constructor I am using:

private User(UserID id, Name name, Gender gender, EmailAddress email, TelephoneNumber phone, LocalDate birthDate, SHA1 passwordHash, StaticFileID avatarID, OrganizationID organizationID, int version)

It is called by this static factory method:

public static User getInstance(UserID id, Gender gender, EmailAddress email, TelephoneNumber phone, LocalDate birthDate, SHA1 passwordHash, StaticFileID avatarID, OrganizationID organizationID, Integer version) {
        return new User(id, null, gender, email, phone, birthDate, passwordHash, avatarID, organizationID, version);
}

Had I used a collection of Strings and ints, it would be easy to accidentally swap the order of the email address and phone number when calling the constructor and the compiler would not say a word. With a strongly-typed approach, I know I have a valid email address and a valid phone number when this constructor is called. The domain layer then becomes very safe and easy to work with. However, it does appear to complicate the persistence layer.

When describing the javaType attribute of the constructor element on page 32, the manual states, "iBATIS can usually figure out the type if you're mapping to a JavaBean." I think this was an unfortunate copy and paste from the previous section, because, as you indicated, it really has little idea what the type is in the context of the constructor. It could make a good guess by comparing column names to the corresponding getters, but it would only be a guess.

[snip]

> I was surprised that I had to specify the javaType for every parameter.
> Otherwise, iBATIS treated everything as an Object and could not find a
> corresponding constructor. I then discovered that iBATIS was looking for an
> Integer argument for the version even though I specified the javaType to be
> an int. Finally, I had to remove name from the constructor since constructor
> tags do not support child association tags.

Already discussed the need for all the javaType's.  I don't know about
the int; I've used them successfully without issue.  I don't understand
what you mean by "I had to remove name from the constructor"; I don't
see a column called "name" in your table.
You can now see how the constructor takes a Name object. A Name is constructed from the first_name, middle_name, last_name, and suffix columns in the table. (This is why I had to use the association element in the mapper configuration.) The constructor element can only have idArg and arg children, not association elements, so the Name has to be added with setName(name). I see no way to have iBATIS inject the associated Name into the constructor.

When I added a version column to implement optimistic locking, my unit test failed. The exception said my ObjectFactory could not find a matching constructor (static factory). The exception listed the types it was checking for, so I saw that it was trying to use Integer for the version, not int. When I changed my static factory to use an Integer (and with no other changes to the class or the Mapper), it worked again. The class still uses an int internally and the getter returns an int, but the static factory requires an Integer just for iBATIS. This is strange because the example on page 32 of the manual shows a constructor that takes an int and a String.

> As a side note, I try to follow the recommendation by Joshua Bloch in
> Effective Java to use static factory methods instead of constructors, so I
> only have private constructors. I used DefaultObjectFactory as a model to
> create my own ObjectFactory that first looks for a matching static factory
> method before looking for a constructor.

I just use JavaBeans instead of trying to do all the data filling from
the constructor.  iBATIS will use the empty constructor automatically.
Any particular reason you want to do the data assignments via constructors?
I like to use immutable objects where possible (which is another recommendation from Effective Java), so I only write setters for properties that can change. For example, the ID of the object should never change, so there is no setter.

I admit, if I had used JavaBeans with empty constructors and getters and setters for every property, iBATIS would be a breeze, but I believe that is a bad practice in the domain layer.

I know iBATIS can use private setters, but the very idea of an external class even knowing about private fields and methods of another class goes contrary to the principle of encapsulation. I want to avoid it.

It all boils down to why should I have to change my chosen domain model to suit the whims of my persistence layer? Why not have the persistence layer be accommodating of several well-known domain approaches? Why not, as a catch-all, have the user implement some method of this form:?

public MyObject getInstance(ResultSet rs);

iBATIS could call it, cache the result, and behave normally without the need a ResultMap. If that was too risky, the ResultSet could be replaced with a HashMap.

These are rhetorical questions, not practical ones. The fact remains that I have a project that needs to persist objects and so I have to jump through hoops to get there with iBATIS or write my own persistence layer if I wish to be true to best practices in the domain layer.

At first I attempted the Hibernate route, but believe me, there were many more hoops to jump through and there seemed to be too much black magic going on, so that when things failed, it was a monumental effort to discover why.

> One of the reasons I chose iBATIS was that Hibernate put too many
> constraints on my domain model. It isn't really a POJO if you say it has to
> have a public constructor, an empty constructor, and setters for every
> property. iBATIS is less strict, but still has some hoops to jump through.
> Wouldn't it be nice to have a persistence layer that transparently
> accommodated the domain model? What if I wanted to use a separate Factory
> class to create my User objects?

Look at page 14 in the documentation.  You can supply your own
ObjectFactory.
In fact I am supplying my own ObjectFactory. It is just a copy and paste job from DefaultObjectFactory that first checks for static factory methods on the class, so it is for general use. It looks like there can be only one ObjectFactory per iBATIS configuration. I suppose I could inspect the type being passed in and call custom factories for every difficult class. If I could only get the Name class to be included in the "constructor," this would be a very appealing approach.

Once again, thank you for your thoughtful replies. They have been a tremendous help in getting to this point.

Sincerely,

Dan Forward
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Mapping a Complex Object

Clinton Begin
Dan,

Why should your domain layer bend to the whims of the persistence
layer?  Because you also chose the persistence layer.  Frameworks are
inherently composed of many assumptions and constraints. Despite your
assertions, the design you propose is atypical.

I'm going to be totally honest with you.  Given the design choices
you've made, iBATIS is the wrong solution for you.  I recommend you
seek another, or write your own.

Cheers,
Clinton

On 2010-01-03, Dan Forward <[hidden email]> wrote:

>
>
> Guy Rouillier-2 wrote:
>>
>> Comments inline.  Overall, you seem to have made this much more
>> complicated than it needs to be.  Looking at your database schema from
>> your original message, all the table columns are simple strings or
>> number, except for the gender enum.  But you've elected to make every
>> column a distinct object type.  That is why your solution is more
>> complex than you might wish.
>>
>> Because you've elected to transform every column into a distinct object
>> type, you have to tell iBATIS what those object types are.  It can't
>> possibly guess at such things.  And that is why you are having to
>> specify all the javaType's.  If you would have used simple strings and
>> numbers, you would not have to do so.
>>
>
> Thank you for your forthright comments, Guy. I know I could have used
> Strings and ints, but there is something to be said for type safety and
> compile-time checking of parameters. Here is the method signature of the
> sole constructor I am using:
>
> private User(UserID id, Name name, Gender gender, EmailAddress email,
> TelephoneNumber phone, LocalDate birthDate, SHA1 passwordHash, StaticFileID
> avatarID, OrganizationID organizationID, int version)
>
> It is called by this static factory method:
>
> public static User getInstance(UserID id, Gender gender, EmailAddress email,
> TelephoneNumber phone, LocalDate birthDate, SHA1 passwordHash, StaticFileID
> avatarID, OrganizationID organizationID, Integer version) {
> return new User(id, null, gender, email, phone, birthDate, passwordHash,
> avatarID, organizationID, version);
> }
>
> Had I used a collection of Strings and ints, it would be easy to
> accidentally swap the order of the email address and phone number when
> calling the constructor and the compiler would not say a word. With a
> strongly-typed approach, I know I have a valid email address and a valid
> phone number when this constructor is called. The domain layer then becomes
> very safe and easy to work with. However, it does appear to complicate the
> persistence layer.
>
> When describing the javaType attribute of the constructor element on page
> 32, the manual states, "iBATIS can usually figure out the type if you're
> mapping to a JavaBean." I think this was an unfortunate copy and paste from
> the previous section, because, as you indicated, it really has little idea
> what the type is in the context of the constructor. It could make a good
> guess by comparing column names to the corresponding getters, but it would
> only be a guess.
>
> [snip]
>
>
>
>>
>>> I was surprised that I had to specify the javaType for every parameter.
>>> Otherwise, iBATIS treated everything as an Object and could not find a
>>> corresponding constructor. I then discovered that iBATIS was looking for
>>> an
>>> Integer argument for the version even though I specified the javaType to
>>> be
>>> an int. Finally, I had to remove name from the constructor since
>>> constructor
>>> tags do not support child association tags.
>>
>> Already discussed the need for all the javaType's.  I don't know about
>> the int; I've used them successfully without issue.  I don't understand
>> what you mean by "I had to remove name from the constructor"; I don't
>> see a column called "name" in your table.
>>
>>
>
> You can now see how the constructor takes a Name object. A Name is
> constructed from the first_name, middle_name, last_name, and suffix columns
> in the table. (This is why I had to use the association element in the
> mapper configuration.) The constructor element can only have idArg and arg
> children, not association elements, so the Name has to be added with
> setName(name). I see no way to have iBATIS inject the associated Name into
> the constructor.
>
> When I added a version column to implement optimistic locking, my unit test
> failed. The exception said my ObjectFactory could not find a matching
> constructor (static factory). The exception listed the types it was checking
> for, so I saw that it was trying to use Integer for the version, not int.
> When I changed my static factory to use an Integer (and with no other
> changes to the class or the Mapper), it worked again. The class still uses
> an int internally and the getter returns an int, but the static factory
> requires an Integer just for iBATIS. This is strange because the example on
> page 32 of the manual shows a constructor that takes an int and a String.
>
>
>
>>
>>> As a side note, I try to follow the recommendation by Joshua Bloch in
>>> Effective Java to use static factory methods instead of constructors, so
>>> I
>>> only have private constructors. I used DefaultObjectFactory as a model to
>>> create my own ObjectFactory that first looks for a matching static
>>> factory
>>> method before looking for a constructor.
>>
>> I just use JavaBeans instead of trying to do all the data filling from
>> the constructor.  iBATIS will use the empty constructor automatically.
>> Any particular reason you want to do the data assignments via
>> constructors?
>>
>>
>
> I like to use immutable objects where possible (which is another
> recommendation from Effective Java), so I only write setters for properties
> that can change. For example, the ID of the object should never change, so
> there is no setter.
>
> I admit, if I had used JavaBeans with empty constructors and getters and
> setters for every property, iBATIS would be a breeze, but I believe that is
> a bad practice in the domain layer.
>
> I know iBATIS can use private setters, but the very idea of an external
> class even knowing about private fields and methods of another class goes
> contrary to the principle of encapsulation. I want to avoid it.
>
> It all boils down to why should I have to change my chosen domain model to
> suit the whims of my persistence layer? Why not have the persistence layer
> be accommodating of several well-known domain approaches? Why not, as a
> catch-all, have the user implement some method of this form:?
>
> public MyObject getInstance(ResultSet rs);
>
> iBATIS could call it, cache the result, and behave normally without the need
> a ResultMap. If that was too risky, the ResultSet could be replaced with a
> HashMap.
>
> These are rhetorical questions, not practical ones. The fact remains that I
> have a project that needs to persist objects and so I have to jump through
> hoops to get there with iBATIS or write my own persistence layer if I wish
> to be true to best practices in the domain layer.
>
> At first I attempted the Hibernate route, but believe me, there were many
> more hoops to jump through and there seemed to be too much black magic going
> on, so that when things failed, it was a monumental effort to discover why.
>
>
>
>>
>>> One of the reasons I chose iBATIS was that Hibernate put too many
>>> constraints on my domain model. It isn't really a POJO if you say it has
>>> to
>>> have a public constructor, an empty constructor, and setters for every
>>> property. iBATIS is less strict, but still has some hoops to jump
>>> through.
>>> Wouldn't it be nice to have a persistence layer that transparently
>>> accommodated the domain model? What if I wanted to use a separate Factory
>>> class to create my User objects?
>>
>> Look at page 14 in the documentation.  You can supply your own
>> ObjectFactory.
>>
>>
>
> In fact I am supplying my own ObjectFactory. It is just a copy and paste job
> from DefaultObjectFactory that first checks for static factory methods on
> the class, so it is for general use. It looks like there can be only one
> ObjectFactory per iBATIS configuration. I suppose I could inspect the type
> being passed in and call custom factories for every difficult class. If I
> could only get the Name class to be included in the "constructor," this
> would be a very appealing approach.
>
> Once again, thank you for your thoughtful replies. They have been a
> tremendous help in getting to this point.
>
> Sincerely,
>
> Dan Forward
>
> --
> View this message in context:
> http://old.nabble.com/Mapping-a-Complex-Object-tp26961927p27002148.html
> Sent from the iBATIS - User - Java mailing list archive at Nabble.com.
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
>

--
Sent from my mobile device

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Mapping a Complex Object

Dan Forward
Clinton Begin wrote
Dan,

Why should your domain layer bend to the whims of the persistence
layer?  Because you also chose the persistence layer.  Frameworks are
inherently composed of many assumptions and constraints. Despite your
assertions, the design you propose is atypical.

I'm going to be totally honest with you.  Given the design choices
you've made, iBATIS is the wrong solution for you.  I recommend you
seek another, or write your own.

Cheers,
Clinton
Thank you, Clinton, for your assessment. I wonder what framework you think would be a better fit? I was prepared to plow forward with iBATIS regardless, simply because it is the most flexible I've found.

In my current project, we have total control of the application and database design. Initially we tried a Spring/Hibernate approach, which required empty constructors and setters for every property and could not accommodate things like MySQL enum types. We got pretty far with that approach until the database started running out of connections. When we found a solution to that problem, transactions stopped working. We then hired a Spring/Hibernate expert to check things over and after two weeks still did not have it working. To make a long story short, we wanted to find a simpler solution, one that would allow us better insight into what was going on under the hood. That led us to Guice/iBATIS.

The scenario I first presented in this thread is a proof-of-concept. It isn't our real User object, but it encapsulates the toughest problems we faced with Hibernate and follows what I believe are best practices in Java development. Perhaps good coding practices are atypical. :-)

In a former project, I wrote a persistence layer that allowed this type of domain programming. However, it is proprietary code for that company and I did not really want to have to start from scratch again if there was a mainstream product that could help us accomplish the same goals. I would rather compromise design principles in the domain layer than try to build a new persistence layer from scratch, as sad as that may be.

Thank you for building such a great tool! Despite my moanings, I think you have done an outstanding job with it. If at all possible, I hope I can contribute back to iBATIS and make it better.

Sincerely,

Dan Forward
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Mapping a Complex Object

nmaves
Dan,

In short I think the largest issue I have seen with your thought process is over design issues.  You reference that you want to use Guice but the Guice team would disagree with your approach to use static factory methods instead of constructors.  DI frameworks like Guice have almost removed all factory methods in our entire codebase, which makes testing a dream.  You choice to wrap every simple object into a Class is something I have never seen done all my years of development.  I think you are breaking basic OO design if your EmailAddress class does nothing more then act like a String.  If your object is a String the use it.  You only reference one Java book like it is the bible.  The first edition of that book was almost 10 years ago.  Many views have changed and you might look into a few of these new ideas.  

These are my personal thoughts which are not meant to offend.  Your problems would be easily solved with a reduction of design complications that you have committed to.

Nathan  

On Mon, Jan 4, 2010 at 2:27 PM, Dan Forward <[hidden email]> wrote:


Clinton Begin wrote:
>
> Dan,
>
> Why should your domain layer bend to the whims of the persistence
> layer?  Because you also chose the persistence layer.  Frameworks are
> inherently composed of many assumptions and constraints. Despite your
> assertions, the design you propose is atypical.
>
> I'm going to be totally honest with you.  Given the design choices
> you've made, iBATIS is the wrong solution for you.  I recommend you
> seek another, or write your own.
>
> Cheers,
> Clinton
>

Thank you, Clinton, for your assessment. I wonder what framework you think
would be a better fit? I was prepared to plow forward with iBATIS
regardless, simply because it is the most flexible I've found.

In my current project, we have total control of the application and database
design. Initially we tried a Spring/Hibernate approach, which required empty
constructors and setters for every property and could not accommodate things
like MySQL enum types. We got pretty far with that approach until the
database started running out of connections. When we found a solution to
that problem, transactions stopped working. We then hired a Spring/Hibernate
expert to check things over and after two weeks still did not have it
working. To make a long story short, we wanted to find a simpler solution,
one that would allow us better insight into what was going on under the
hood. That led us to Guice/iBATIS.

The scenario I first presented in this thread is a proof-of-concept. It
isn't our real User object, but it encapsulates the toughest problems we
faced with Hibernate and follows what I believe are best practices in Java
development. Perhaps good coding practices are atypical. :-)

In a former project, I wrote a persistence layer that allowed this type of
domain programming. However, it is proprietary code for that company and I
did not really want to have to start from scratch again if there was a
mainstream product that could help us accomplish the same goals. I would
rather compromise design principles in the domain layer than try to build a
new persistence layer from scratch, as sad as that may be.

Thank you for building such a great tool! Despite my moanings, I think you
have done an outstanding job with it. If at all possible, I hope I can
contribute back to iBATIS and make it better.

Sincerely,

Dan Forward

--
View this message in context: http://old.nabble.com/Mapping-a-Complex-Object-tp26961927p27019604.html
Sent from the iBATIS - User - Java mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Mapping a Complex Object

Martin Ellis-6
Perhaps an alternative to individual classes for each data type would
be to use the builder pattern?  That would avoid having many, many
parameters on the constructor for the User class.  This might help
with the problem of figuring out whether arguments at the call site
match the formal parameters.

At ten parameters, this must start to get a bit fiddly:

  private User(UserID id, Name name, Gender gender, EmailAddress email,
TelephoneNumber phone, LocalDate birthDate, SHA1 passwordHash, StaticFileID
avatarID, OrganizationID organizationID, int version)


I'm not sure the extent to which iBATIS would support using a builder,
specifically  in terms of specifying how to use a builder-like object
to create objects built from a ResultSet.  Probably an ObjectFactory
would work.  If it's not possible, it'd be possible to extend iBATIS
to do so.

My own solution?  Getters and setters on the persistence model
classes, but only getters in the interface that they implement.
Hence, most of the application never sees the setters, and the objects
appear immutable.  I don't claim it's beautiful, but it does the job,
and it's a lot simpler than going 'against the grain'.

Martin


2010/1/4 Nathan Maves <[hidden email]>:

> Dan,
> In short I think the largest issue I have seen with your thought process is
> over design issues.  You reference that you want to use Guice but the Guice
> team would disagree with your approach to use static factory methods instead
> of constructors.  DI frameworks like Guice have almost removed all factory
> methods in our entire codebase, which makes testing a dream.  You choice to
> wrap every simple object into a Class is something I have never seen done
> all my years of development.  I think you are breaking basic OO design if
> your EmailAddress class does nothing more then act like a String.  If your
> object is a String the use it.  You only reference one Java book like it is
> the bible.  The first edition of that book was almost 10 years ago.  Many
> views have changed and you might look into a few of these new ideas.
> These are my personal thoughts which are not meant to offend.  Your problems
> would be easily solved with a reduction of design complications that you
> have committed to.
> Nathan
>
> On Mon, Jan 4, 2010 at 2:27 PM, Dan Forward <[hidden email]>
> wrote:
>>
>>
>> Clinton Begin wrote:
>> >
>> > Dan,
>> >
>> > Why should your domain layer bend to the whims of the persistence
>> > layer?  Because you also chose the persistence layer.  Frameworks are
>> > inherently composed of many assumptions and constraints. Despite your
>> > assertions, the design you propose is atypical.
>> >
>> > I'm going to be totally honest with you.  Given the design choices
>> > you've made, iBATIS is the wrong solution for you.  I recommend you
>> > seek another, or write your own.
>> >
>> > Cheers,
>> > Clinton
>> >
>>
>> Thank you, Clinton, for your assessment. I wonder what framework you think
>> would be a better fit? I was prepared to plow forward with iBATIS
>> regardless, simply because it is the most flexible I've found.
>>
>> In my current project, we have total control of the application and
>> database
>> design. Initially we tried a Spring/Hibernate approach, which required
>> empty
>> constructors and setters for every property and could not accommodate
>> things
>> like MySQL enum types. We got pretty far with that approach until the
>> database started running out of connections. When we found a solution to
>> that problem, transactions stopped working. We then hired a
>> Spring/Hibernate
>> expert to check things over and after two weeks still did not have it
>> working. To make a long story short, we wanted to find a simpler solution,
>> one that would allow us better insight into what was going on under the
>> hood. That led us to Guice/iBATIS.
>>
>> The scenario I first presented in this thread is a proof-of-concept. It
>> isn't our real User object, but it encapsulates the toughest problems we
>> faced with Hibernate and follows what I believe are best practices in Java
>> development. Perhaps good coding practices are atypical. :-)
>>
>> In a former project, I wrote a persistence layer that allowed this type of
>> domain programming. However, it is proprietary code for that company and I
>> did not really want to have to start from scratch again if there was a
>> mainstream product that could help us accomplish the same goals. I would
>> rather compromise design principles in the domain layer than try to build
>> a
>> new persistence layer from scratch, as sad as that may be.
>>
>> Thank you for building such a great tool! Despite my moanings, I think you
>> have done an outstanding job with it. If at all possible, I hope I can
>> contribute back to iBATIS and make it better.
>>
>> Sincerely,
>>
>> Dan Forward
>>
>> --
>> View this message in context:
>> http://old.nabble.com/Mapping-a-Complex-Object-tp26961927p27019604.html
>> Sent from the iBATIS - User - Java mailing list archive at Nabble.com.
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: [hidden email]
>> For additional commands, e-mail: [hidden email]
>>
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Mapping a Complex Object

Dan Forward
In reply to this post by nmaves
nmaves wrote
Dan,

In short I think the largest issue I have seen with your thought process is
over design issues.  You reference that you want to use Guice but the Guice
team would disagree with your approach to use static factory methods instead
of constructors.  DI frameworks like Guice have almost removed all factory
methods in our entire codebase, which makes testing a dream.  You choice to
wrap every simple object into a Class is something I have never seen done
all my years of development.  I think you are breaking basic OO design if
your EmailAddress class does nothing more then act like a String.  If your
object is a String the use it.  You only reference one Java book like it is
the bible.  The first edition of that book was almost 10 years ago.  Many
views have changed and you might look into a few of these new ideas.

These are my personal thoughts which are not meant to offend.  Your problems
would be easily solved with a reduction of design complications that you
have committed to.

Nathan
The design I presented is one that I have used and liked for many years. I started a successful startup company seven years ago with this approach. Now, with a new startup I would like to use more of the existing frameworks, but am finding that they do not accommodate this technique very well.

My EmailAddress class is more than a String. It validates that it is in the proper format when it is constructed. When sending an email, I know I have a valid email address when the parameter is an EmailAddress and not a String. It is the same principle as the java.net.URL class and the java.io.File class. They are both just a String underneath, but additional type safety and validation occurs when these custom classes are used.

The second edition of Effective Java was published in 2006. The author wrote the Java Collections classes when he worked for Sun and he is now the Chief Java Architect at Google. It is one of the best books on Java development I have read.

I am okay with writing custom handlers for my classes. It was also pretty simple to accommodate the static factories by writing my own ObjectFactory. My main suggestion for iBATIS would be to allow association elements and primitives under the constructor element in a result map, like this:

<resultMap type="User" id="userMap">
        <constructor>
                <idArg column="id" javaType="UserID" typeHandler="UserIDHandler"/>
                <association property="name" javaType="Name">
                        <constructor>
                                <arg column="first_name" javaType="String"/>
                                <arg column="middle_name" javaType="String"/>
                                <arg column="last_name" javaType="String"/>
                                <arg column="suffix" javaType="String"/>
                        </constructor>
                </association>
                <arg column="gender" javaType="Gender" typeHandler="GenderHandler"/>
                <arg column="email" javaType="EmailAddress" typeHandler="EmailAddressHandler"/>
                <arg column="phone" javaType="TelephoneNumber" typeHandler="TelephoneNumberHandler"/>
                <arg column="birth_date" javaType="LocalDate" typeHandler="LocalDateHandler"/>
                <arg column="password_hash" javaType="SHA1" typeHandler="SHA1Handler"/>
                <arg column="avatar_id" javaType="StaticFileID" typeHandler="StaticFileIDHandler"/>
                <arg column="organization_id" javaType="OrganizationID" typeHandler="OrganizationIDHandler"/>
                <arg column="version" javaType="int"/>
        </constructor>
</resultMap>
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Mapping a Complex Object

Dan Forward
In reply to this post by Martin Ellis-6
Martin Ellis-6 wrote
Perhaps an alternative to individual classes for each data type would
be to use the builder pattern?  That would avoid having many, many
parameters on the constructor for the User class.  This might help
with the problem of figuring out whether arguments at the call site
match the formal parameters.
It is funny that you should mention the Builder pattern, because we have started using that pattern on some of our classes. I was reluctant to mention it as I did not want to complicate the discussion. They could easily be accommodated in an ObjectFactory. The only issue I run into is that I cannot pass an association into the "constructor." Only parameters in the constructor element of the result map are passed into the main ObjectFactory method:

public Object create(Class type, List<Class> constructorArgTypes, List constructorArgs);

[snip]

My own solution?  Getters and setters on the persistence model
classes, but only getters in the interface that they implement.
Hence, most of the application never sees the setters, and the objects
appear immutable.  I don't claim it's beautiful, but it does the job,
and it's a lot simpler than going 'against the grain'.
I may need to go that route as well. I just wanted to see if it could be done beautifully first.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Mapping a Complex Object

Clinton Begin
In reply to this post by Dan Forward
I'm not aware of any O/R mapper that would elegantly handle a micro-typed domain.  Since frameworks are largely built for commodity application of common practices, I'm not sure one will be easily found.  This is a unique design practice, and thus will require a unique solution.

Clinton

On Mon, Jan 4, 2010 at 2:27 PM, Dan Forward <[hidden email]> wrote:


Clinton Begin wrote:
>
> Dan,
>
> Why should your domain layer bend to the whims of the persistence
> layer?  Because you also chose the persistence layer.  Frameworks are
> inherently composed of many assumptions and constraints. Despite your
> assertions, the design you propose is atypical.
>
> I'm going to be totally honest with you.  Given the design choices
> you've made, iBATIS is the wrong solution for you.  I recommend you
> seek another, or write your own.
>
> Cheers,
> Clinton
>

Thank you, Clinton, for your assessment. I wonder what framework you think
would be a better fit? I was prepared to plow forward with iBATIS
regardless, simply because it is the most flexible I've found.

In my current project, we have total control of the application and database
design. Initially we tried a Spring/Hibernate approach, which required empty
constructors and setters for every property and could not accommodate things
like MySQL enum types. We got pretty far with that approach until the
database started running out of connections. When we found a solution to
that problem, transactions stopped working. We then hired a Spring/Hibernate
expert to check things over and after two weeks still did not have it
working. To make a long story short, we wanted to find a simpler solution,
one that would allow us better insight into what was going on under the
hood. That led us to Guice/iBATIS.

The scenario I first presented in this thread is a proof-of-concept. It
isn't our real User object, but it encapsulates the toughest problems we
faced with Hibernate and follows what I believe are best practices in Java
development. Perhaps good coding practices are atypical. :-)

In a former project, I wrote a persistence layer that allowed this type of
domain programming. However, it is proprietary code for that company and I
did not really want to have to start from scratch again if there was a
mainstream product that could help us accomplish the same goals. I would
rather compromise design principles in the domain layer than try to build a
new persistence layer from scratch, as sad as that may be.

Thank you for building such a great tool! Despite my moanings, I think you
have done an outstanding job with it. If at all possible, I hope I can
contribute back to iBATIS and make it better.

Sincerely,

Dan Forward

--
View this message in context: http://old.nabble.com/Mapping-a-Complex-Object-tp26961927p27019604.html
Sent from the iBATIS - User - Java mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Mapping a Complex Object

Niko Ustinov
Well, not so unique design practice now, i think.
I'm trying now to implement solution based on scala & iBatis and want to use scala's case classes for domain model. But common way to initialize such objects is parametrizing through constructor.
so, i have (for example) such definitions:

trait BaseDTO
case class Item (id: Int, Name: String, Caption: String) extends BaseDTO
case class Order (id: Int, Name: String, Caption: String, Items: Option[Array[Item]]) extends BaseDTO

so because class members (by default) are val's here (and immutable), i need to pass array of constructed items to Order's constructor.

Do you have any suggestion how i can do it with iBatis?

Thanks. Nikolay

Clinton Begin wrote
I'm not aware of any O/R mapper that would elegantly handle a micro-typed
domain.  Since frameworks are largely built for commodity application of
common practices, I'm not sure one will be easily found.  This is a unique
design practice, and thus will require a unique solution.

Clinton
Loading...