|
From: <Ron...@to...> - 2006-03-29 00:48:05
|
Hi Emmanuel,
I think we might have found a bug in BindHelper.bindPropertiesByColumns,
want to run by you to see if it's worth while submitting to Jira.
What we are trying to do is to associate two attributes of the same type
to an object.
i.e.,
class Deal {
@ManyToOne
@JoinColumn(name = "a_customer_id", referencedColumnName =
"customer_id", nullable = true)
Customer a;
@ManyToOne
@JoinColumn(name = "b_customer_id", referencedColumnName = "customer_id",
nullable = true)
Customer b;
}
// note: customer id is not a pk
class Customer {
@Column(name = "customer_id")
String customerId;
}
When loading deal object, customer a is always populated correctly but
customer b will only be populated occasionally (depending on your luck!).
After digging through Hibernate annotation source, I think I've found the
cause of the problem in BindHelper.bindPropertiesByColumns where HashSet
is being used.
From looking through the source, my understanding is that hibernate will
create a synthetic property for each of the customer Deal.a and Deal.b and
add it to a list of properties type mapping within Customer persistent
class object. This type mapping is then used to hydrate Deal.a and Deal.b
when Deal object is being resolved.
When synthetic property for Deal.a is being created, the propertymapping
is straight forward because only customerId property is available in the
Customer persistent class. Deal.a is then mapped simply to a simple type
of Customer.customerId.
Subsequently, when synthetic property for Deal.b is being created
(BindHelper:97), the property mapping has two possible values - one is the
Customer.customerId, the other is the recently created synthetic mapping
(_Deal_a). These two values are extracted out and put into a HashSet
(BindHelper.bindPropertiesByColumn:190). The hashset is then the being
iterated through to return the first value of iterator (which the order is
not guaranteed). Obviously it would be wrong to create a synthetic
property for Deal.b with synthetic property _Deal_a type mapping (which is
what occasionally happens).
Upon load, hibernate core will then attempt to hydrate Deal.a by
instantiating Customer (_Deal_a) type object and inject a_customer_id into
an instance of _Deal_a type. This will result in a correct load.
When hibernate core attempt to hydrate Deal.b, it will instantiate
Customer (_Deal_b) type object. However, _Deal_b type object mapps to an
embedded _Deal_a type. So it will then recurse through the embedded type,
instantiate Customer (_Deal_a) type object, assign b_customer_id into
embedded Customer.customerId after which instance of Customer(_Deal_a)
will be assigned to Customer.customerId (_Deal_b). This assignment is done
via EmbeddedPropertyAccessor.EmbeddedSetter.set method - which looks like
a null implementation. The result is that Deal.b won't be set and no
errors is being reported at all. What worst is that the system can appear
to have been working most of the time, but if you add additional attribute
to class Deal to disturb the balance of the hashcode generation, then all
of the sudden it won't work.
I can think of a couple of fixes in BindHelper.bindPropertiesByColumns:
1. Instead of using HashSet we use an ordered set to guarantee the order,
this way the native properties will always be returned first.
2. Use List instead of Set to guarantee the order - the values being added
in to the columnsToProperty map can never be null or repeated anyway.
3. Filter out all of the synthetic properties from columnsToProperty
mapping (this propbably wouldn't help for muti column mapping?).
BTW: It also perplex me why EmbeddedPropertyAccessor.EmbeddedSetter.set
method would do nothing and report no error when it can't set the
property.
Sorry for the lengthy email but it's a fairly subtle bug that can't always
be replicated so I thought it's worth while explaining in details. Let me
know if you want me to submit this to Jira. We have opted for option 2 on
local fix and it appear to be working well.
Regards,
rOnn c.
######################################################################
DISCLAIMER:
This email and any attachment may contain confidential information.
If you are not the intended recipient you are not authorized to copy
or disclose all or any part of it without the prior written consent
of Toyota.
Opinions expressed in this email and any attachments are those of the
sender and not necessarily the opinions of Toyota.
Please scan this email and any attachment(s) for viruses.
Toyota does not accept any responsibility for problems caused by
viruses, whether it is Toyota's fault or not.
######################################################################
|