[hibernate-issues] [Hibernate-JIRA] Created: (ANN-548) Joins break when using composite primary key classes

Vincent Jenks (JIRA) noreply at atlassian.com
Tue Jan 30 14:09:40 EST 2007


Joins break when using composite primary key classes
----------------------------------------------------

         Key: ANN-548
         URL: http://opensource.atlassian.com/projects/hibernate/browse/ANN-548
     Project: Hibernate Annotations
        Type: Bug

    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
 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.jspa
-
For more information on JIRA, see:
   http://www.atlassian.com/software/jira




More information about the hibernate-issues mailing list