javaspringhibernatehibernate-mapping

expecting IdClass mapping


I am upgrading an ancient Spring 3 app to Spring 6. I have found a number of issues with the same message, but they are mostly actually using annotations. The one that isn't: java/hibernate Exception: expecting IdClass mapping doesn't have an answer.

I have a session factory for hibernate:

<bean id="jxxSessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
  <property name="dataSource" ref="jxxDatabase"/>
  <property name="mappingResources">
    <list>
      <value>mappings/appearance.hbm.xml</value>
      <value>mappings/appearMessage.hbm.xml</value>
      ...lots of other mapping files
    </list>
  </property>
  <property name="hibernateProperties">
    <value>
      hibernate.dialect=org.hibernate.dialect.InformixDialect
      hibernate.show.sql=true
      hibernate.connection.release_mode=after_statement
    </value>
  </property>
</bean>

And I'm getting an error when I start my app:

...long stack trace
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jxxSessionFactory' defined in ServletContext resource [/WEB-INF/conf/spring/dao.xml]: expecting IdClass mapping
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1812) ~[spring-beans-6.2.5.jar:6.2.5]
...long stack trace

Sadly it doesn't specify which mapping.

There are several entities that use composite keys, I just picked one.

and here are the classes:

public class AppearanceKey implements Serializable{
    
    private static final long serialVersionUID = 1L;
    
    /* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
        AppearanceKey aKey=null;
        if (obj instanceof AppearanceKey) {
            aKey = (AppearanceKey) obj;
        }
        else{
            return false;
        }
        
        boolean test=aKey.getParticipantNumber().equals(getParticipantNumber());
        if(test){
            return aKey.getDateAttended().equals(getDateAttended());
        }
        else{
            return false;
        }
    }
    
    /* (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        return toString().hashCode();
    }
    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder sb=new StringBuilder(getParticipantNumber());
        sb.append(":");
        sb.append(DateAttended.get(Calendar.MONTH+1));
        sb.append("/");
        sb.append(DateAttended.get(Calendar.DATE));
        sb.append("/");
        sb.append(DateAttended.get(Calendar.YEAR));
        
        return sb.toString();
    }
    
    Calendar    DateAttended;
    String      ParticipantNumber;
    
    /**
     * @return the dateAttended
     */
    public Calendar getDateAttended() {
        return DateAttended;
    }
    /**
     * @param dateAttended the dateAttended to set
     */
    public void setDateAttended(Calendar dateAttended) {
        DateAttended = dateAttended;
    }
    /**
     * @return the participantNumber
     */
    public String getParticipantNumber() {
        return ParticipantNumber;
    }
    /**
     * @param participantNumber the participantNumber to set
     */
    public void setParticipantNumber(String participantNumber) {
        ParticipantNumber = participantNumber;
    }
}

If I have to switch to annotation mapping, I will, but trying to avoid if possible.

public class Appearance {
    
    Courthouse  CourtLocation;
    Calendar    DateAttended;
    String      ParticipantNumber;
    Float       PaymentAmount;
    
    /**
     * @return the dateAttended
     */
    public Calendar getDateAttended() {
        return DateAttended;
    }
    /**
     * @param dateAttended the dateAttended to set
     */
    public void setDateAttended(Calendar dateAttended) {
        DateAttended = dateAttended;
    }
    /**
     * @return the participantNumber
     */
    public String getParticipantNumber() {
        return ParticipantNumber;
    }
    /**
     * @param participantNumber the participantNumber to set
     */
    public void setParticipantNumber(String participantNumber) {
        ParticipantNumber = participantNumber;
    }
    /**
     * @return the paymentAmount
     */
    public Float getPaymentAmount() {
        if(PaymentAmount==null){
            PaymentAmount=0.0f;
        }
        return PaymentAmount;
    }
    /**
     * @param paymentAmount the paymentAmount to set
     */
    public void setPaymentAmount(Float paymentAmount) {
        PaymentAmount = paymentAmount;
    }
    
    /**
     * @return the courtLocation
     */
    public Courthouse getCourtLocation() {
        return CourtLocation;
    }
    
    /**
     * @param courtLocation the courtLocation to set
     */
    public void setCourtLocation(Courthouse courtLocation) {
        CourtLocation = courtLocation;
    }
}

Solution

  • Turning on trace for hibernate and digging into the source code was a big help in figuring this one out. I finally isolated the classes it was having a problem with, which were all of the ones with composite keys. Evidently the rules have changed some in these versions of hibernate.

    So, to summarize, I had to add @EmbededId:

    @EmbeddedId
    private AppearanceKey   id;
    

    with getters and setters of course.

    I then had to re-work my mapping file:

    <hibernate-mapping>
        <class name="com.gs.jxx.bo.Appearance" table="APPEARANCES">
           <composite-id name = "id" class="com.gs.jxx.bo.AppearanceKey">
              <key-property name="participantNumber" column="PART_NO"/>
              <key-property name="dateAttended" column="ATT_DATE"/>
           </composite-id>
           <property name="paymentAmount" column="AMOUNT"/>
               <many-to-one name="courtLocation" column="LOC_CODE" class="com.gs.juror.bo.Courthouse" lazy="false"/>
        </class>
    </hibernate-mapping>
    

    I used the hibernate 4 documentation to achieve some of this.

    Anyway, that seemed to solve my DAO griefs.