[
http://opensource.atlassian.com/projects/hibernate/browse/ANN-548?page=all ]
Emmanuel Bernard updated ANN-548:
---------------------------------
Component: binder
Summary: Fails to map a FK pointing to a non PK whose columns are held by properies
in component (was: Joins break when using composite primary key classes)
Priority: Minor (was: Major)
So I looked at the issue,
The problem is that HAN does not handle well FK pointing to non primary keys where:
- each non PK column is not referenced by a property (ie property handling multiple
columns)
- the property referencing the PK column(s) is part of a component (ie not a top level
property)
Your case belongs in case 2. Unfortunately, a fix, if possible, would be very complex. As
I said in the forum, your case (fk pointing to a part of the pk, this part being unique)
is pretty uncommon.
If someone wants to give it a try it is related to
BinderHelper.createSyntheticPropertyReference() in the NON_PK_REFERENCE case: it would be
better to redesign this area from scratch.
Fails to map a FK pointing to a non PK whose columns are held by
properies in component
---------------------------------------------------------------------------------------
Key: ANN-548
URL:
http://opensource.atlassian.com/projects/hibernate/browse/ANN-548
Project: Hibernate Annotations
Type: Bug
Components: binder
Versions: 3.2.1, 3.2.2
Environment: JBoss 4.0.5.GA w/ Hibernate 3.2.2 and Annoations 3.2.2 (from SVN). Also
reproduced w/ JBoss 4.0.5.GA w/ stock annotations, hibernate, and entity manager,
unmodified. The attached test-case was run against MSSQL 2000 but is trivial to point at
another database. The issue was also reproduced on a OpenEdge Progress 10.1A database.
Reporter: Vincent Jenks
Priority: Minor
Attachments: CarBean.java, Carz.java, Lotz.java
Joins break when using composite primary key classes
JBoss 4.0.5.GA w/ Hibernate 3.2.2 and Annoations 3.2.2 (from SVN). Also reproduced w/
JBoss 4.0.5.GA w/ stock annotations, hibernate, and entity manager, unmodified. The
attached test-case was run against MSSQL 2000 but is trivial to point at another database.
The issue was also reproduced on a OpenEdge Progress 10.1A database.
In a case where there are composite keys on one (or each) side of a relationship,
Hibernate cannot join the entities together if all of the primary key fields are not used
in the relationship.
A (very) simple example, two entities Carz and Lotz (pardon the names, it's simply
cars and car lots):
@Entity
@Table(name="car")
public class Carz implements Serializable
{
@Id
private Integer id;
@Column(name="make", nullable=false)
private String make;
@Column(name="model", nullable=false)
private String model;
@Column(name="manufactured", nullable=false)
@Temporal(TemporalType.TIMESTAMP)
private Date manufactured;
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumns //Hibernate docs state that @JoinColumns must be used since Lotz has
composite PK, a single @JoinColumn does not deploy anyhow
({
@JoinColumn(name="loc_code", referencedColumnName="loc_code",
insertable=false, updatable=false)
})
private Lotz lot;
...........................
}
@Entity
@Table(name="lot")
public class Lotz implements Serializable
{
@EmbeddedId
protected LotzPK lotPK;
@Column(name="name", nullable=false)
private String name;
@Column(name="location", nullable=false)
private String location;
@OneToMany(mappedBy="lot", fetch=FetchType.LAZY, cascade=CascadeType.ALL)
private List<Carz> cars;
...........................
}
@Embeddable
public class LotzPK implements Serializable
{
@Column(name="id", nullable=false)
private Integer id;
@Column(name="loc_code", nullable=false)
private String locCode;
...........................
}
In this case I need a one-to-many relationship on Carz.id = Lotz.locCode. However, there
doesn't appear to be a way to do this.
When this is deployed, this exception occurs:
org.hibernate.AnnotationException: Column name id of hqb.model.Lotz not found in
JoinColumns.referencedColumnName
at org.hibernate.cfg.annotations.TableBinder.bindFk(TableBinder.java:306)
at org.hibernate.cfg.FkSecondPass.doSecondPass(FkSecondPass.java:64)
at
org.hibernate.cfg.AnnotationConfiguration.processFkSecondPassInOrder(AnnotationConfiguration.java:433)
at
org.hibernate.cfg.AnnotationConfiguration.secondPassCompile(AnnotationConfiguration.java:287)
at org.hibernate.cfg.Configuration.buildMappings(Configuration.java:1115)
at org.hibernate.ejb.Ejb3Configuration.buildMappings(Ejb3Configuration.java:1211)
at
org.hibernate.ejb.EventListenerConfigurator.configure(EventListenerConfigurator.java:154)
at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:847)
at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:385)
at
org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:126)
at
org.jboss.ejb3.entity.PersistenceUnitDeployment.start(PersistenceUnitDeployment.java:264)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.jboss.ejb3.ServiceDelegateWrapper.startService(ServiceDelegateWrapper.java:102)
at
org.jboss.system.ServiceMBeanSupport.jbossInternalStart(ServiceMBeanSupport.java:289)
at
org.jboss.system.ServiceMBeanSupport.jbossInternalLifecycle(ServiceMBeanSupport.java:245)
at sun.reflect.GeneratedMethodAccessor263.invoke(Unknown Source)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
...........................
I can deploy the app if I map both fields in the Lotz PK class like so:
@Entity
@Table(name="car")
public class Carz implements Serializable
{
@Id
private Integer id;
...........................
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumns
({
@JoinColumn(name="loc_code", referencedColumnName="loc_code",
insertable=false, updatable=false),
@JoinColumn(name="", referencedColumnName="id", insertable=false,
updatable=false) //INCORRECTLY DEFINED RELATIONSHIP!
})
private Lotz lot;
...........................
}
But obviously this is no good, it generates bad SQL at runtime:
/* select
c
from
Carz c
left join
fetch c.lot */ select
carz0_.id as id198_0_,
lotz1_.id as id199_1_,
lotz1_.loc_code as loc2_199_1_,
carz0_.make as make198_0_,
carz0_.model as model198_0_,
carz0_.manufactured as manufact4_198_0_,
carz0_.lot_id as lot5_198_0_,
carz0_.loc_code as loc6_198_0_, --no such column...
lotz1_.name as name199_1_,
lotz1_.location as location199_1_
from
car carz0_
left outer join
lot lotz1_
on carz0_.lot_id=lotz1_.id --oops! Hibernate defaulted a relationship
here...
and carz0_.loc_code=lotz1_.loc_code
In reality, this is the SQL I'm trying to achieve, by repairing the above query
manually:
select
carz0_.id as id198_0_,
lotz1_.id as id199_1_,
lotz1_.loc_code as loc2_199_1_,
carz0_.make as make198_0_,
carz0_.model as model198_0_,
carz0_.manufactured as manufact4_198_0_,
carz0_.loc_code as loc6_198_0_,
lotz1_.name as name199_1_,
lotz1_.location as location199_1_
from
car carz0_
left outer join
lot lotz1_
on carz0_.loc_code=lotz1_.loc_code
This is consistent w/ the EJB 3.0 spec (and Glassfish+Toplink) but that's only
because the spec doesn't define exacltly how to handle this. However, that being
said, this seems important enough for Hibernate to be able to manage?
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
http://opensource.atlassian.com/projects/hibernate/secure/Administrators....
-
For more information on JIRA, see:
http://www.atlassian.com/software/jira