I'm posting a follow-up because I'm experiencing the same symptom
with openjpa-1.0.2.
My scenario is a little different to the previous post, and I'll
try to summarise here.
If I need to post more detail, just let me know. There is something
strange happening here if
you can make it through to the end where I describe a "work-around"
to the out-of-memory condition.
First, here is the tail-end of the trace when I startup my
application.
I'll reproduce the comment in the OpenJPA code for
MetaDataRepository.processBuffer - it sounds relevant:
// continually pop a metadata and process it until we run
out; note
// that each processing call might place more metas in the
buffer as
// one class tries to access metadata for another; also note
that the
// buffer orders itself from least to most derived
Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError:
Java heap space
at java.util.ArrayList.ensureCapacity(ArrayList.java:169)
at java.util.ArrayList.add(ArrayList.java:351)
at
org
.apache
.openjpa
.meta.MetaDataRepository.processBuffer(MetaDataRepository.java:676)
at
org
.apache
.openjpa
.meta.MetaDataRepository.resolveMeta(MetaDataRepository.java:575)
at
org
.apache
.openjpa.meta.MetaDataRepository.resolve(MetaDataRepository.java:500)
at
org
.apache
.openjpa
.meta.MetaDataRepository.getMetaData(MetaDataRepository.java:302)
at
org.apache.openjpa.enhance.PCEnhancer.<init>(PCEnhancer.java:241)
at
org.apache.openjpa.enhance.PCEnhancer.<init>(PCEnhancer.java:212)
at
org.apache.openjpa.enhance.PCEnhancer.<init>(PCEnhancer.java:182)
at
org
.apache
.openjpa
.enhance
.ManagedClassSubclasser
.prepareUnenhancedClasses(ManagedClassSubclasser.java:119)
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:585)
at
org
.apache
.openjpa
.kernel
.AbstractBrokerFactory
.loadPersistentTypes(AbstractBrokerFactory.java:297)
at
org
.apache
.openjpa
.kernel.AbstractBrokerFactory.newBroker(AbstractBrokerFactory.java:
199)
at
org
.apache
.openjpa
.kernel
.DelegatingBrokerFactory.newBroker(DelegatingBrokerFactory.java:142)
at
org
.apache
.openjpa
.persistence
.EntityManagerFactoryImpl
.createEntityManager(EntityManagerFactoryImpl.java:192)
at
org
.apache
.openjpa
.persistence
.EntityManagerFactoryImpl
.createEntityManager(EntityManagerFactoryImpl.java:145)
at
org
.apache
.openjpa
.persistence
.EntityManagerFactoryImpl
.createEntityManager(EntityManagerFactoryImpl.java:56)
at
au
.com.crabhill.hydrogeoreports.ORMHelper.openSession(ORMHelper.java:
38)
at
au
.com
.crabhill
.hydrogeoreports.ClientModel.reloadClients(ClientModel.java:206)
at
au.com.crabhill.hydrogeoreports.ClientModel.<init>(ClientModel.java:
31)
at
au
.com
.crabhill
.hydrogeoreports
.HydrogeoReportsView.<init>(HydrogeoReportsView.java:69)
at
au
.com
.crabhill
.hydrogeoreports.HydrogeoReportsApp.startup(HydrogeoReportsApp.java:
19)
at org.jdesktop.application.Application
$1.run(Application.java:171)
at
java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:461)
at
java
.awt
.EventDispatchThread
.pumpOneEventForHierarchy(EventDispatchThread.java:242)
at
java
.awt
.EventDispatchThread
.pumpEventsForHierarchy(EventDispatchThread.java:163)
at
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:157)
at
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:149)
OK, so I seem to have the PCEnhancer thrashing about trying to work
out my class metadata,
so here are the relevant bits (at least I hope the relevant bits):
@MappedSuperclass
public abstract class ContactMech extends AbstractBean implements
Serializable {
// @Id field omitted for brevity
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH})
@ForeignKey
@JoinColumn(name = "client_id", referencedColumnName =
"client_id", nullable=false)
protected Client clientId;
// This is a bidirectional parent-child mapping and the parent
class (Client)
// includes the required 'mappedBy' attribute in the corresponding
// @OneToMany
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH})
@ForeignKey
@JoinColumn(name = "contact_mech_type_id", referencedColumnName =
"type_id", nullable=false)
protected ContactMechType contactMechTypeId;
// This is a unidirectional mapping, so no 'mappedBy' in TypeClass
...
}
@Entity
@Table(name = "client")
public class Client implements Serializable {
// @Id field omitted for brevity
// See below for the owning side of these bidirectional
relationships...
@OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE,
CascadeType.REMOVE, CascadeType.REFRESH}, mappedBy = "clientId")
@OrderBy("emailAddress ASC")
private List<CMEmail> emails;
@OneToMany(fetch = FetchType.LAZY, cascade =
{CascadeType.MERGE, CascadeType.REMOVE, CascadeType.REFRESH},
mappedBy = "clientId")
@OrderBy("areaCode ASC, phoneNum ASC")
private List<CMTelecom> telecoms;
@OneToMany(fetch = FetchType.LAZY, cascade =
{CascadeType.MERGE, CascadeType.REMOVE, CascadeType.REFRESH},
mappedBy = "clientId")
private List<CMAddress> addresses;
...
}
Note in the following there is a self-referencing mapping
(implementing a type hierarchy).
@Entity
@Table(name = "contact_mech_type")
public class ContactMechType implements Serializable {
@Id
@Column(name = "type_id", nullable = false)
private String typeId;
@ManyToOne(fetch = FetchType.EAGER)
@ForeignKey
@JoinColumn(name = "parent_type_id", referencedColumnName =
"type_id", nullable = true)
private ContactMechType parentType;
// This is the inverse of the above
@OneToMany(fetch = FetchType.EAGER, mappedBy = "parentType")
private List<ContactMechType> subTypes;
...
}
Then I have three (3) subclasses of ContactMech which add an extra
few fields, one of
which is a JoinTable mapping:
@Entity
@Table(name = "email")
// Do I need the following if I don't plan to inherit from THIS
class. Obviously I inherit from the MappedSuperclass above,
// but not sure what effect the @Inheritance annotation has on this
class.
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class CMEmail extends ContactMech implements Serializable {
// Simple @Column fields ommitted for brevity
// More on this one in a minute @ManyToMany(fetch =
FetchType.EAGER)
@JoinTable(
name = "email_purpose", joinColumns =
[EMAIL PROTECTED](name = "contact_mech_id", referencedColumnName =
"contact_mech_id")},
inverseJoinColumns = [EMAIL PROTECTED](name = "purpose_type_id",
referencedColumnName = "purpose_id")}
)
private List<ContactMechPurposeType> purposes;
...
}
@Entity
@Table(name = "telecom")
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class CMTelecom extends ContactMech implements Serializable {
// As above, but we use a different JoinTable...
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "telecom_purpose", joinColumns =
[EMAIL PROTECTED](name = "contact_mech_id", referencedColumnName =
"contact_mech_id")},
inverseJoinColumns = [EMAIL PROTECTED](name = "purpose_type_id",
referencedColumnName = "purpose_id")}
)
private List<ContactMechPurposeType> purposes;
...
}
@Entity
@Table(name = "address")
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class CMAddress extends ContactMech implements Serializable {
// Ditto @ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "address_purpose", joinColumns =
[EMAIL PROTECTED](name = "contact_mech_id", referencedColumnName =
"contact_mech_id")},
inverseJoinColumns = [EMAIL PROTECTED](name = "purpose_type_id",
referencedColumnName = "purpose_id")}
)
private List<ContactMechPurposeType> purposes;
...
}
Last but not least...
@Entity
@Table(name = "contact_mech_purpose_type")
public class ContactMechPurposeType implements Serializable {
// Omitted @Id field and various @Column fields for brevity
@ManyToOne
@ForeignKey
@JoinColumn(name = "type_id", referencedColumnName = "type_id",
nullable=false)
private ContactMechType typeId;
...
}
What concerns me about my class network is circular references and
what PCEnhancer is doing to
them (given the comment I pasted from the OpenJPA source, above).
To summarise, here's the reference table for all the classes above
(and which classes they references):
Client: CMEmail, CMTelecom, CMAddress
- these references are all backpointers in bidirectionals
CMEmail: Client, ContactMechType,
ContactMechPurposeType
CMTelecom: Client, ContactMechType,
ContactMechPurposeType
CMAddress: Client, ContactMechType,
ContactMechPurposeType
- In the above 3, Client and ContactMechType references are via
inheritance of ContactMechType
ContactMechType: ContactMechType (self-reference)
ContactMechPurposeType: ContactMechType
NOW FOR THE WIERD BIT...
If I change my Client class (above) to temporarily "disable" any
one of the mappings 'emails',
'addresses', 'telecoms', by tagging the field as @Transient instead
of a backpointer to the
bidirectional mapping, this makes the mapping unidirectional at the
other end, obviously.
When I do this, I don't get the PCEnhancer memory-slurp problem,
and the application runs!?!?!
(except for the bits that rely on the disable field). Note I only
have to disable ONE of these
backpointers for things get past the memory slurp.
Help!!!
And thanks for your patience if got this far...
Cheers, Iain