You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user-java@ibatis.apache.org by Dan Forward <da...@forwardhome.com> on 2010/01/02 23:51:49 UTC

Re: Mapping a Complex Object

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
-- 
View this message in context: http://old.nabble.com/Mapping-a-Complex-Object-tp26961927p26997280.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


Re: Mapping a Complex Object

Posted by Dan Forward <da...@forwardhome.com>.

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>

-- 
View this message in context: http://old.nabble.com/Mapping-a-Complex-Object-tp26961927p27020853.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


Re: Mapping a Complex Object

Posted by Dan Forward <da...@forwardhome.com>.

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.

-- 
View this message in context: http://old.nabble.com/Mapping-a-Complex-Object-tp26961927p27020976.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


Re: Mapping a Complex Object

Posted by Martin Ellis <el...@gmail.com>.
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 <na...@gmail.com>:
> 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 <da...@forwardhome.com>
> 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: user-java-unsubscribe@ibatis.apache.org
>> For additional commands, e-mail: user-java-help@ibatis.apache.org
>>
>
>

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


Re: Mapping a Complex Object

Posted by Nathan Maves <na...@gmail.com>.
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 <da...@forwardhome.com>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: user-java-unsubscribe@ibatis.apache.org
> For additional commands, e-mail: user-java-help@ibatis.apache.org
>
>

Re: Mapping a Complex Object

Posted by Niko Ustinov <nu...@gmail.com>.
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[Field]]) 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
> 
> 

-- 
View this message in context: http://old.nabble.com/Mapping-a-Complex-Object-tp26961927p28693487.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


Re: Mapping a Complex Object

Posted by Clinton Begin <cl...@gmail.com>.
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 <da...@forwardhome.com>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: user-java-unsubscribe@ibatis.apache.org
> For additional commands, e-mail: user-java-help@ibatis.apache.org
>
>

Re: Mapping a Complex Object

Posted by Dan Forward <da...@forwardhome.com>.

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: user-java-unsubscribe@ibatis.apache.org
For additional commands, e-mail: user-java-help@ibatis.apache.org


Re: Mapping a Complex Object

Posted by Clinton Begin <cl...@gmail.com>.
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 <da...@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


Re: Mapping a Complex Object

Posted by Dan Forward <da...@forwardhome.com>.

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


Re: Mapping a Complex Object

Posted by Guy Rouillier <gu...@burntmail.com>.
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: user-java-unsubscribe@ibatis.apache.org
For additional commands, e-mail: user-java-help@ibatis.apache.org