Hi,
I get a StackOverflowError when trying to load an object - Hibernate seems to get into an infinite recursion even though I have no hierarchical mappings set up. Basically what I'm trying to do is set up a many-to-many mapping that has the following characteristics:
- the join table has extra fields besides the two foreign keys
- one of the join table's foreign keys is part of its primary key (it's a legacy database ;)
As far as I understand this cannot be done using the @JoinTable annotation, you have to define a separate entity for the join table. Also, because you cannot simply make a foreign key also a primary key (at least Hibernate doesn't seem to support this), I need to define a different entity for the composite primary key, and import it into the join table's entity using @EmbeddedId.
I can save a new company, and the contents of the join table get saved correctly too (the save operation is cascaded). However, if I try to load a company using session.get(class, id), Hibernate begins an infinite recursion, querying the company object again and again, until this results in a StackOverflowException.
Does anybody have any idea what could be wrong? All my other mappings work correctly, but I simply cannot get this one working.
Hibernate version:
Core: 3.2.6
Annotations: 3.3.0
Mapping documents:
Code:
@Entity
@Table(name = "companies")
public class Company {
private Set<CompanyIndustry> companyIndustries;
// ... other properties
@OneToMany(mappedBy = "id.company", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public Set<CompanyIndustry> getCompanyIndustries() {
return companyIndustries;
}
public void setCompanyIndustries(Set<CompanyIndustry> companyIndustries) {
this.companyIndustries = companyIndustries;
}
// ... other getters/setters
}
// The join table
@Entity
@Table(name = "companies_to_industries")
public class CompanyIndustry {
@EmbeddedId
private CompanyIndustryId id;
@ManyToOne
@JoinColumn(name = "industryId")
private Industry industry;
public CompanyIndustryId getId() {
return id;
}
public void setId(CompanyIndustryId id) {
this.id = id;
}
public Industry getIndustry() {
return industry;
}
public void setIndustry(Industry industry) {
this.industry = industry;
}
}
// The composite primary key for the join table
@Embeddable
public class CompanyIndustryId implements Serializable {
private static final long serialVersionUID = 1L;
@ManyToOne
@JoinColumn(name = "companyId")
private Company company;
private String provider;
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
public String getProvider() {
return provider;
}
public void setProvider(String provider) {
this.provider = provider;
}
}
Code between sessionFactory.openSession() and session.close():Code:
new HibernateTemplate(sessionFactory).executeAndTransformExceptions(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
return session.get(Company.class, id);
}
});
Full stack trace of any exception that occurs:
java.lang.StackOverflowError
at java.lang.AbstractStringBuilder.delete(AbstractStringBuilder.java:700)
at java.lang.StringBuffer.delete(StringBuffer.java:374)
at oracle.jdbc.driver.OracleSql.parse(OracleSql.java:1029)
at oracle.jdbc.driver.OracleSql.getSql(OracleSql.java:312)
at oracle.jdbc.driver.OracleSql.getSqlBytes(OracleSql.java:591)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:202)
at oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:810)
at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1039)
at oracle.jdbc.driver.T4CPreparedStatement.executeMaybeDescribe(T4CPreparedStatement.java:850)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1134)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3339)
at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3384)
at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:186)
at org.hibernate.loader.Loader.getResultSet(Loader.java:1787)
at org.hibernate.loader.Loader.doQuery(Loader.java:674)
(and then the following block gets repeated an "infinite" amount of times)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
at org.hibernate.loader.Loader.loadEntity(Loader.java:1860)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:48)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:42)
at org.hibernate.loader.entity.BatchingEntityLoader.load(BatchingEntityLoader.java:82)
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3049)
at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:399)
at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:375)
at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:139)
at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:195)
at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:103)
at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:878)
at org.hibernate.impl.SessionImpl.internalLoad(SessionImpl.java:846)
at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:557)
at org.hibernate.type.EntityType.resolve(EntityType.java:379)
at org.hibernate.type.ComponentType.resolve(ComponentType.java:584)
at org.hibernate.type.ComponentType.nullSafeGet(ComponentType.java:275)
at org.hibernate.loader.Loader.getKeyFromResultSet(Loader.java:1097)
at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:565)
at org.hibernate.loader.Loader.doQuery(Loader.java:701)
(repeated block ends)
Name and version of the database you are using:
Oracle 10g.
Debug level Hibernate log excerpt:
This is what gets displayed before each company query:
2008-03-05 18:21:07,680 [RMI TCP Connection(5)-192.168.34.199] DEBUG org.hibernate.jdbc.AbstractBatcher - preparing statement
2008-03-05 18:21:07,680 [RMI TCP Connection(5)-192.168.34.199] DEBUG org.hibernate.type.LongType - binding '1' to parameter: 1
2008-03-05 18:21:07,711 [RMI TCP Connection(5)-192.168.34.199] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open ResultSet (open ResultSets: 1, globally: 1)
2008-03-05 18:21:07,711 [RMI TCP Connection(5)-192.168.34.199] DEBUG org.hibernate.loader.Loader - processing result set
2008-03-05 18:21:07,711 [RMI TCP Connection(5)-192.168.34.199] DEBUG org.hibernate.loader.Loader - result set row: 0
2008-03-05 18:21:07,711 [RMI TCP Connection(5)-192.168.34.199] DEBUG org.hibernate.event.def.DefaultLoadEventListener - loading entity: [mypackage.Company#1]
2008-03-05 18:21:07,711 [RMI TCP Connection(5)-192.168.34.199] DEBUG org.hibernate.event.def.DefaultLoadEventListener - attempting to resolve: [mypackage.Company#1]
2008-03-05 18:21:07,711 [RMI TCP Connection(5)-192.168.34.199] DEBUG org.hibernate.event.def.DefaultLoadEventListener - object not resolved in any cache: [mypackage.Company#1]
2008-03-05 18:21:07,711 [RMI TCP Connection(5)-192.168.34.199] DEBUG org.hibernate.persister.entity.AbstractEntityPersister - Fetching entity: [mypackage.Company#1]
2008-03-05 18:21:07,711 [RMI TCP Connection(5)-192.168.34.199] DEBUG org.hibernate.loader.Loader - loading entity: [mypackage.Company#1]
2008-03-05 18:21:07,711 [RMI TCP Connection(5)-192.168.34.199] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 2, globally: 2)
2008-03-05 18:21:07,711 [RMI TCP Connection(5)-192.168.34.199] DEBUG org.hibernate.SQL -
Thanks,
Peter