Last month another user posted the following message to the mailing list but
no-one
was able to help:
http://mail-archives.apache.org/mod_mbox/openjpa-users/200802.mbox/ajax/[EMAIL
PROTECTED]
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
--
Internal Virus Database is out-of-date.
Checked by AVG.
Version: 7.5.519 / Virus Database: 269.21.7 - Release Date: 8/03/2008 12:00 AM