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;
}
}
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.