ibatis-user-java mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Clinton Begin <clinton.be...@gmail.com>
Subject Re: Mapping a Complex Object
Date Sun, 03 Jan 2010 15:51:33 GMT
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 <dan-nabble@forwardhome.com> 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: user-java-unsubscribe@ibatis.apache.org
> For additional commands, e-mail: user-java-help@ibatis.apache.org
>
>

-- 
Sent from my mobile device

---------------------------------------------------------------------
To unsubscribe, e-mail: user-java-unsubscribe@ibatis.apache.org
For additional commands, e-mail: user-java-help@ibatis.apache.org


Mime
View raw message