Skip to main content

Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "EclipseLink/Development/JPA 2.0/metamodel api"

m (DI 58: 20090807: ManagedType Attribute Initialization must differentiate between Collection and List)
m (DI 59: 20090818: PluralAttribute.elementType not set for non-lazy instantiated Collection Attribute)
Line 2,762: Line 2,762:
  
 
====DI 59: 20090818: PluralAttribute.elementType not set for non-lazy instantiated Collection Attribute====
 
====DI 59: 20090818: PluralAttribute.elementType not set for non-lazy instantiated Collection Attribute====
 +
* See also http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_50:_20090727:_Handle_all_mapping_types_in_the_SingularAttribute_constructor
 +
  
 
=====Solution:=====
 
=====Solution:=====

Revision as of 23:17, 19 August 2009

Contents

JPA 2.0: MetaModel API

JPA 2.0 Root | bug 266912

Date Committer(s) Description
Mar 3, 2009 gyorke Initial feature template
Mar 3, 2009 mobrien Start analysis
Mar 25, 2009 mobrien Review MappedSuperclass preliminary Design - off
Mar 31, 2009 mobrien Return to project
Apr 6, 2009 mobrien Rev# 3831, Update metamodel UML diagram, post prototype for @MappedSuperclass, expand on design issues
Apr 21, 2009 mobrien Rev# 4011, javax.persistence build changes - Return to API work
May 25, 2009 mobrien Rev# 4265, Return to API work and update to latest specification changes
May 28, 2009 mobrien Rev# 4353, Partial implementation snapshot 1
May 29, 2009 mobrien Rev# 4357, Partial test suite snapshot 1
Jun 4, 2009 mobrien Rev# 4415, EntityManagerImpl IllegalStateException fix
Jun 4, 2009 mobrien Rev# 4416, MetadataProject OO API fix
Jun 5, 2009 mobrien Rev# 4433, FullRegressionTestSuite launch fix
Jun 17, 2009 mobrien Rev# 4519, Use a Map keyed on MetadataClass instead of overriding equals/hashCode in RelationalDescriptor
Jun 30, 2009 mobrien Rev# 4587, Complete JPA Metadata modifications in support of parameterized generics in Map, Embeddable and ElementCollection types on MappedSuperclass Descriptors in support of the JPA Metamodel API
Jul, 6 2009 mobrien Rev# 4614, 282518 Metamodel superType requires javaClass set on custom descriptor on MappedSuperclassAccessor
Jul, 8 2009 mobrien Rev# 4630, See bug 266912 comment 66 - solved design issues 34, 35 ,38, 40
Jul, 9 2009 mobrien Rev# 4643, See bug 266912 comment 69
Jul, 10 2009 mobrien Rev# 4644, See bug 266912 comment 70 getBindableJavaType for SingularAttribute, adjust BasicType processing to handle non-Entity Java types.
Jul, 14 2009 mobrien Rev# 4661, See bug 266912 comment 74 Implement all ManagedType.getDeclaredX() functionality.
Jul, 17 2009 mobrien Rev# 4683, See bug 266912 Implement EntityTypeImpl.getIdType functionality without full CMP3Policy null testing yet.
Jul, 23 2009 mobrien Rev# 4710, See bug 266912 Design Issue 47 - Implement Map Support for IdClass.
Jul, 29 2009 mobrien Rev# 4754, See bug 266912 Design Issue 52 - Implement recursive ManagedType.getDeclared* algorithm to differentiate by IdentifiableType.
Aug, 3 2009 mobrien Rev# 4777, See bug 266912 Implement design issues 47 ( IdentifiableType.getIdType() for composite keys )

50 (Handle all mapping types in the SingularAttribute constructor - partial) 48 (Map support).

Aug, 4 2009 mobrien Rev# 4779, See bug 266912 Adjust Metamodel.type(Clazz) implementation to handle null(not present) and BasicImple type (IAE) type returns.
Aug, 9 2009 mobrien Rev# 4820 and Rev# 4827, See bug 266912 Design Issue #58: differentiate between Collection and List for lazy IndirectList during plural attribute Type generation for Managed Types.
Aug, 12 2009 mobrien Rev# 4845, See bug 266912

Design Issue #49: @OneToMany on @MappedSuperclass with @JoinTable should not add pseudo PK Field if an @Id already exists on the MappedSuperclass.

Aug, 17 2009 mobrien Rev# 4883 Rev# 4886, See bug 266912 Design Issue 52 - Refactor base case of recursive check for ManagedType.getDeclared* algorithm.
Aug, 0n 2009 mobrien Remaining functions in IdentifiableType get*Id*() and get*Version() in progress.

Note: this document is in progress as of 20090709, therefore the content is in flux.

Summary

In JPA 2.0 (JSR-317), the specification has defined standard APIs for representing the structure of a persistence unit model. This is referred to as the Metamodel APIs. There are two main aspects to providing this functionality. The first is the runtime model accessed from EntityManagerFactory.getMetamodel() and the second is the APT generated metamodel classes. Our first goal is to provide functionality for runtime access.

For details see sections 5.1 and 6.6 of the Proposed Final Draft.

Work Estimate

  • Original March 2009:
    • Investigate EclipseLink Metamodel
      approx 3 days
    • Develop implementations of MetaModel or refactor current metamodel
      approx 10 days
    • APT investigation and prototype - future work
      approx 5 days
    • APT tooling/testing - future work
      approx 10 days
  • Supplemental
    • Learn JPA test model architecture and generation framework
    • Learn 1:1, n:n, 1:n and n:1 mapping functionality (IE: JoinTable and JoinColumn)
    • Add new Metamodel test model with table generation
    • Modify test build scripts for new Metamodel test model
    • Investigate parameterized generics functionality
    • Debug and investigate metadata project generation (what we will wrap)
    • Write up design/issues both before and during development

Work Items

The following work items have been identified. The prefix D signifies done, P means in progress.

  • P - Metamodel Implementation
    • DWrap existing metadata implementation for non-mappedSuperclass code
    • DImplement mappedSuperclass changes during predeploy()
      • DImplement mappedSuperclass accessor changes
      • Implement EmbeddedAccessor mappedSuperclass changes
      • Implement EmbeddedIdAccessor mappedSuperclass changes
      • Implement ManyToManyAccessor mappedSuperclass changes
      • Implement ManyToOneAccessor mappedSuperclass changes
      • DImplement OneToManyAccessor mappedSuperclass changes
      • DImplement OneToOneAccessor mappedSuperclass changes
      • Implement TransientAccessor mappedSuperclass changes - out of spec.
      • Implement VariableOneToOneAccessor mappedSuperclass changes - out of spec.
    • DImplement ManagedTypeImpl
    • DImplement EntityTypeImpl
    • P Prepare for 2-3 code review and code adjustment cycles
  • P Metamodel Test Implementation
    • Create test model
      • add @ManyToMany
      • add @Embeddable
  • D Metamodel Build modifications
  • P Metamodel XML/XSD modifications
  • Metamodel integration with EclipseLink Workbench
  • P Metamodel Design Documentation
  • Metamodel User Documentation

EclipseLink API code Changes

This list of API modifications are detailed as of SVN Rev# 4644 on 24 July 2009

Project.java - Native core

ExceptionLocalizationResource.java - Native core

MetadataProject.java - JPA core

MetadataConstants.java - JPA core

MetadataDescriptor.java - JPA core

MetadataHelper.java - JPA core

MetadataProject.java - JPA core

EmbeddableAccessor.java - JPA core

EntityAccessor.java - JPA core

MappedSuperclassAccessor.java - JPA core

ElementCollectionAccessor.java - JPA core

MappingAccessor.java - JPA core

MetadataAnnotatedElement.java - JPA core

SerializedMetadata.java - JPA core

EntityManagerImpl.java - JPA core

AttributeImpl.java - New

BasicTypeImpl.java - New

CollectionAttributeImpl.java - New

EmbeddableTypeImpl.java - New

EntityTypeImpl.java - New

IdentifiableTypeImpl.java - New

ListAttributeImpl.java - New

ManagedTypeImpl.java - New

MapAttributeImpl.java - New

MappedSuperclassTypeImpl.java - New

MetamodelImpl.java - New

PluralAttributeImpl.java - New

SetAttributeImpl.java - New

SingularAttributeImpl.java - New

TypeImpl.java - New

Unimplemented Functions

IdentifiableTypeImpl.java

public <Y> SingularAttribute<X, Y> getDeclaredId(Class<Y> type)
public <Y> SingularAttribute<X, Y> getDeclaredVersion(Class<Y> type)
public Set<SingularAttribute<? super X, ?>> getIdClassAttributes()
public boolean hasSingleIdAttribute()
public boolean hasVersionAttribute()

MappedSuperclassTypeImpl.java

public Type<?> getIdType() - In progress

EntityTypeImpl.java

public Type<?> getIdType() - In progress

Analysis

  • My understanding is that the metamodel API provides 3 (three) major services to the developer.
    • 1) The Metamodel API exposes the entity annotation API and the XML mapping API as one unified "metadata" type-safe API.
    • 2) The Metamodel creation and validation occurs at predeploy() and is independent of the runtime state of the application.
    • 3) The Metamodel API provides for dynamic Criteria queries without using generated or manually written Metamodel classes (IE: the _underscore prefixed ones).

API Usage

  • There are three ways to query using the Criteria API which can wrap the Metamodel API
    • 1) Static metamodel class model for type safe queries - these are the _Underscore design time classes
    • 2) Dynamic metamodel class model for type safe queries - we use generics and pass in both the return type and the type containing the return type
    • 3) String attribute references for non-type safe queries - see p.262 of the JPA 2.0 specification section 6.7 - there may be type or generic usage compiler warnings that the user will need to workaround when using this non-type-safe query in a type-safe environment.
  • This enhancement deals only with # 2) Dynamic metamodel query generation.

Metamodel

  • In the context of EclipseLink, a metamodel is an abstract view of the managed classes in the persistence unit. We already have an in memory model that we we construct to wrap the class MetadataProcessor - we must transition or refactor this.
  • We use the metamodel to construct a runtime query structure that is "object-based".
  • The metamodel enties and attributes represent static structure that is not changed by runtime behavior (these are not entity instances with object state)

Types Hierarchy

  • The following block diagram illustrates the hierarchical relationship between types in the metamodel. The positioning of the 4 concrete types Entity, MappedSuperclass, Embeddable and Basic can be seen in the 3-level type hierarchy tree Identifiable --> Managed --> Type.

Metamodel types hierarchy block.jpg

Relation to JPQL?

Functional Requirements

  • Todo: 20090715: verify that we cover off what is not being developed like variable OneToOne (unidirectional) mappings (See DI 39) and other mappings that are custom ORM mappings (native API).
  • Todo: 20090715: describe how we will handle extension of existing mappings
  • Requirements and constraints have traceability down to their associated use cases.

Requirements Table

Req# A# C# Use Cases# Description
R1 - - Support runtime Metamodel APIs - See Spec. section 5.2
R1.1 - - Develop, leverage or refactor current Metamodel processor
R1.1.1 - - Implement metamodel.PluralAttribute interface
R1.1.1.1 - - Implement metamodel.MapAttribute interface
R1.1.1.2 - - Implement metamodel.SetAttribute interface (empty)
R1.1.1.3 - - Implement metamodel.ListAttribute interface (empty)
R1.1.1.4 - - Implement metamodel.CollectionAttribute interface
R1.1.5 - - Implement metamodel.SingularAttribute interface
R1.1.6 - - Implement metamodel.BasicType interface (currently empty)
R1.1.7 - - Implement metamodel.MappedSuperclassType interface (currently empty)
R1.1.8 C3 C4 C5 - Implement metamodel.EmbeddableType interface (currently empty)
R1.1.9 - - Implement metamodel.EntityType interface
R1.1.10 - - Implement metamodel.IdentifiableTpe interface
R1.2 - - Dynamic access to the metamodel is provided by the javax.persistence.metamodel.Metamodel interface
R1.2.1 - - Implement public Metamodel EntityManagerFactory.getMetamodel()
R1.2.2 - - Implement public Metamodel EntityManager.getMetamodel()
R1.3 - - - "Metamodel classes are produced for every entity, mapped superclass, and embeddable class in the persistence unit."
R2 A1 - Support APT generation of Canonical Metamodel classes see JPA 2.0 Spec. "Sect. 5.2.1"
R2.1 - - "For each managed class X in package p, a metamodel class X_ in package p is created."

I2: Name collisions with entities that are already named Entity_ in the current package

I3: Package level splitting is incompatible with OSGI

R2.2 - - "The name of the metamodel class is derived from the name of the managed class by appending "_" to the name of the managed class."
R2.3 - - "The metamodel class X_ must be annotated with the javax.annotation.Generated annotation and with the javax.persistence.TypesafeMetamodel annotation."

The example Order_ metamodel class is missing these two annotations in the spec sect 5.2.1.1 p.161

R2.4 - - "If class X extends another class S, where S is the most derived managed class (i.e., entity or mapped superclass) extended by X, then class X_ must extend class S_, where S_ is the metamodel class created for S."
R2.5 - - "For every persistent non-collection-valued attribute y declared by class X, where the type of y is Y, the metamodel class must contain a declaration as follows:"
public static volatile Attribute<X, Y> y;
(and where javax.persistence.Attribute is imported by the class X_ )
R2.6 - - For every persistent collection-valued attribute z declared by class X, where the element type of z is Z, the metamodel class must contain a declaration as follows:
R2.6.1 - - Collection if the collection type of z is java.util.Collection, then
public static volatile Collection<X, Z> z;
(and where javax.persistence.Collection is imported by the class X_ )
R2.6.2 - - Set if the collection type of z is java.util.Set, then
public static volatile Set<X, Z> z;
(and where javax.persistence.Set is imported by the class X_ )
R2.6.3 - - List if the collection type of z is java.util.List, then
public static volatile List<X, Z> z;
(and where javax.persistence.List is imported by the class X_ )
R2.6.4 - - Map if the collection type of z is java.util.Map, then
public static volatile Map<X, K, Z> z;
where K is the type of the key of the map in class X (and and where javax.persistence.Map is imported by the class X_)
R2.7 - - Import statements must be included for all classes X, Y, Z, and K. specification down to the level of import statements vs fully qualified names - is a pending decision
R2.8 - - Sect 5.2.1.2 "When generated metamodel classes are used, they must be specified as part of the persistence unit."
R2.9 - - Sect 5.2.1.2 "When the entity manager factory for the persistence unit is created, it is the responsibility of the persistence provider to initialize the state of its metamodel classes."
R2.10 - - No extra runtime paramenters are required beyond existing -javaagent runtime flag

Assumptions Table

A# Req# Use Cases# Description
A1 - - Java 5 is the minimum compile target we support (unchanged)
A2 - - Runtime environment for metamodel construction is SE and compilation time only

Constraints Table

C# A# Use Cases# Description
C1 - - Dependency on the APT tool specific to the SUN JDK 1.5.0? Yes (tools.jar)

Determine if we are ok running on the IBM J9 JVM and WLS JRockit JVM in SE mode - Yes both have tools.jar.

C2 - - Wrap our existing metamodel (Descriptors on a Project hava a 1-1 correspondence to Mappings)
C3 - - A collection of embedded objects is not supported from entities in JPA 1.0
C4 - - An embedded object referencing other embedded objests is not supported in JPA 1.0
C5 - - An embedded object having a relationship to to an entity or entities is not supported in JPA 1.0
C6 - - Metamodel managedTypes should not be affected by the runtime instantiation or persistence of entities.

Metamodel Interfaces

The criteria API runs on top of the metamodel API and expects that classes of the form X_ exist. It is the responsibility of the metamodel to create and compile these enhanced classes. An ATP tooling library will either need to be developed, extended or imported.

For the primary requirement R1.3 "Metamodel classes are produced for every entity, mapped superclass, and embeddable class in the persistence unit.", we will use the existing metadata API surrounding the Descriptor to crate the Metamodel instance.

ATP Tooling Library

This section discusses requirement #2 in enhacement 267391, it is currently on hold as of 20090306.

Issue 4: Develop, Extend or use an ATP tooling library

We require an ATP tooling library that has implementations of AnnotationProcessorFactory and AnnotationProcessor classes for metamodel types such as List, Collection, Map, Set, Attribute and Basic.

Decision

As of 20090306 we will defer to the provider of the RI for JPA 2.0 to submit a binary ATP tooling implementation jar and/or the source so it can be extended. In the future we may extend or refactor this tool to provide extended support - see enhancement# 267391

Running APT to create AnnotationProcessorFactory classes

APT Static Usage

  • Start with the following APT tutorial by SUN that shows how to list class names
  • Create an SE JPA project - remember to include the provider resource \jpa\org.eclipse.persistence.jpa\resource\META-INF\services\javax.persistence.spi.PersistenceProvider
  • Add the EclipseLink classpath variable TOOLS_LIB = tools.jar so we can import com.sun.mirror
  • Create an AnnotationProcessorFactory class
  • Create a pointer to this class
    • Create a UTF8 no extension text file com.sun.mirror.apt.AnnotationProcessorFactory with the content org.eclipse.persistence.apt.MetamodelAnnotationProcessorFactory
    • Delete the 3 chars upside down ?.. before the same - so we do not get Illegal provider-class name: ?org
    • Place this class into META-INF/services
  • Compile and verify the following inner classes were created
    • MetamodelAnnotationProcessorFactory$ListClassAp$ListClassVisitor.class
    • MetamodelAnnotationProcessorFactory$ListClassAp.class
  • Run APT in this project

APT Dynamic Usage

Use Cases

  • Use cases have traceability back to their requirements and constraints via their id# and have a 1-1 correspondence with test cases.

Use Case Partitioning

  • The following are design points that we will partition the use cases around.
    • Attributes
      • Singular = Basic/Direct
      • Plural
        • List
        • Set
        • Collection (See design issue # 37) Collection in Java and CollectionAttribute in the Metamodel are different)
        • Map
    • Types
      • Basic
      • Entity
      • MappedSuperclass
        • Levels (1 to n)
        • Mapping (Basic,)
      • Embeddable
    • Mappings (assume all bidirectional where appropriate)
      • Singular
        • Basic
        • OneToOne
        • ManyToOne
      • Plural
        • OneToMany
        • ManyToMany
        • ElementCollection

Use Case Maps

  • The following use cases will involve a maximum coverage minimum path through the API.
    • For example using an EntityType that has a oneToMany relationship (ListAttribute) to another entity that inherits its' id from a MappedSuperclassType will exercise 10 interfaces of the 16 interface API.


Use Case Table

Use Case ID# Assumptions# Requirements# Description
UC1 - - Construct object-based query definition object
UC2 - - @Mappings
UC2.1 - - @OneToOne Mappings
UC2.2 - - @OneToMany Mappings
UC2.2.1 - - @OneToMany Unidirectional Mappings
UC2.2.2 - - @OneToMany Bidirectional Mappings
UC2.3 - - @ManyToOne Mappings
UC2.3.1 - - @ManyToOne Unidirectional Mappings
UC2.4 - - @ManyToMany Mappings
UC2.5 - - @Embedded Mappings
UC2.6 - - Basic/Direct Mappings
UC2.7 - - ElementCollection Mappings
UC2.8 - - Unknown/Invalid Mappings
UC3 - - Inheritance Hierarchy Mappings
UC3.1 - - Mapped superclass parent - 1 level
UC3.2 - - Mapped superclass parent - n (2) levels
UC4 - - template

Concrete Use Cases

Details on each use case.

UC5: Metamodel from fully populated entity tree

The tree will contain examples of 1:1, 1:n and n:1 (bidirectional), n:n, variable 1:1 and simulated variable 1:n

  • Preconditions
  • Postconditions
  • Path
  • Exceptions

UC2: Metamodel containing mapped superclass heiarchy

  • Preconditions
  • Postconditions
  • Path
  • Exceptions

UC3: Metamodel containing embeddable

  • Preconditions
  • Postconditions
  • Path
  • Exceptions


......

Variant Use Cases

Details on each variant (negative test) use case.

Design

API

Metamodel and Criteria packages interfaces API - Specification

  • Step 1) determine which interfaces will be extended by concrete classes and which are abstract - this will affect how we deal with multiple inheritance.
    • Basic, Embeddable, Entity, Map, Set, List, Collection are concrete.
    • AbstractCollection, Attribute, ManagedType are all abstract as they implement 2 interfaces.
  • Note that the internal API access path to the EclipseLink mappings is via the entityManager --> AbstractSession --> Project --> Mappings
  • (The criteria package is ommitted here)

Uml class diagram metamodel criteria packages.gif

Metamodel Implementation

The following UML class diagram illustrates the static relationships and hierarchy of the Metamodel implementation classes.

Eclipselink uml class metamodel impl.gif

  • During preprocess() the JPA API will save each MappedSuperclass as an empty RelationalDescriptor (without a table).
  • Later during accessor processing we save the mappings defined on MappedSuperclass objects on the appropriate RelationalDescriptor.
  • The Metamodel retrieves a collection of MappedSuperclassTypeImpl objects from the core Project on the Session on the MetadataProject.

Class Heirarchy Design

How much implementation will be in the abstract classes (TypeImpl and ManagedTypeImpl) and how much will be in the concrete classes (BasicImpl, EmbeddableTypeImpl, EntityTypeImpl and IdentifiableTypeImpl)?

ManagedTypeImpl

  • We will cache the ManagedType on it's core descriptor as a property so we do not have to recreate it.
  • All managedTypeImpl instances are static, so there will only be one object per class. Each static managedTypeImpl is stored on the Metamodel instance on the EM or EMF.

MetamodelImpl

  • Implement handlers and storage for all 4 types of the enumeration PersistenceType (ENTITY, EMBEDDABLE, MAPPED_SUPERCLASS, BASIC)
    • We have the folloing instance fields on MetamodelImpl
metamodel    MetamodelImpl  (id=113) 
    embeddables LinkedHashMap<K,V>  (id=251)    
        size    0   
    entities    LinkedHashMap<K,V>  (id=253)    
        size    10  
    managedTypes    LinkedHashMap<K,V>  (id=254)    
        size    14  
    mappedSuperclasses  HashSet<E>  (id=255)    
        map HashMap<K,V>  (id=278)  
            size    4   
    types   LinkedHashMap<K,V>  (id=259)    
        size    17  
  • In the above list we have the following type containment hierarchy
    • types is the superset of all types including basic types.
    • entities is the subset of entity types (a subset of identifiable types)
    • mappedSuperclasses is the subset of mapped superclasses (a subset of identifiable types)
    • managedTypes is the subset of embeddable, mappedSuperclass and entity types
    • embeddables is the subset of embeddable types (a subset of managed types)


Design Issues

DI 4: QueryBuilder Interface Implementation

  • See section 5 of the spec.
  • The new javax.persistence.criteria.QueryBuilder interface in section 5.4.1 has getter functions in EntityManager* in sect 3.1.1 and 6.4 that return javax.persistence.QueryBuilder.
    • This existing unimplemented Criteria API EntityManager.getQueryBuilder() function that returns an instance of javax.persistence.QueryBuilder for the 2.0 spec that in EntityManagerImpl needs to be either removed or extended.
 /**
 * @see javax.persistence.EntityManager#getQueryBuilder()
 * @since Java Persistence API 2.0
 */
 public QueryBuilder getQueryBuilder() {
 // TODO - May change as Query API is redefined
 throw new PersistenceException("Not Yet Implemented");
 }
EntityManagerImpl and EntityManagerFactoryImpl will require modification
Alternative #1: Remove javax.persistence.QueryBuilder
  • Can we change the signature of getQueryBuilder to return a javax.persistence.criteria.QueryBuilder instance instead?
  • I assume the existing functions in the old javax.persistence.QueryBuilder interface will be discarded.
package javax.persistence;
public interface QueryBuilder {
 QueryDefinition createQueryDefinition();
 DomainObject createQueryDefinition(Class root);
 DomainObject createSubqueryDefinition(PathExpression path);
}
Alternative #2: Extend javax.persistence.QueryBuilder
  • Or, if the 3 functions above are still required can we make javax.persistence.criteria.QueryBuilder inherit from the 1.99/2.0 javax.persistence.QueryBuilder?
public interface QueryBuilder extends javax.persistence.QueryBuilder
  • However, In this situation a client will still need to cast which is not advisable.
QueryBuilder qb = (QueryBuilder)entityManager.getQueryBuilder();
Decision
  • I expect that since the Criteria API is new to JPA 2.0 that we do not have to deprecate the existing javax.persistence.QueryBuilder interface.
  • Alt #2 : The existing javax.persistence.QueryBuilder interface will be removed and replaced by the updated javax.persistence.criteria.QueryBuilder interface.

DI 5: Metamodel package is public or internal API

Decision Criteria

What are the constraints on going public or internal with this new Metamodel API? Normally a public facing API like the Metamodel one would exist in the public package. We are however adding extensions beyond the JPA 2.0 specification that the developer can take advantage of. In order to provide an extended Metamodel API - an intermediate interface will be required. For an existing example of this design pattern see the class hierarchy EntityManagerImpl --> JpaEntityManager(I) --> EntityManager(I).

DI 6: MappedSuperclass does not currently have a Descriptor

  • Mapped Superclass descriptors do not represent entities and therefore do not have a database table behind them - they are usually abstract classes.
  • A mapped superclass can contain mappings such as @Id but these are accessible via child entities - therefore we do not currently store a descriptor for the mapped superclass.
  • In order to support the MappedSuperclass interface we will need to modify core code to break out our metadata as follows
    • Descriptor --> * List<MappedSuperclassMetadata<List<MappedSuperclassType>> --> * List<DatabaseMapping>
    • We need to support multiple levels of mapped superclasses, each with their own mappings
Adding additional MappedSuperclass support
  • Currently any mapped superclasses of entities are processed during predeploy in the 3rd step of PersistenceUnitProcessor.processORMetadata() below as part of entity processing.
	Thread [main] (Suspended)	
		ArrayList<E>.add(E) line: 351	
		EntityAccessor.discoverMappedSuperclassesAndInheritanceParents() line: 245	
		EntityAccessor.process() line: 509	
		MetadataProject.processStage1() line: 720	
		MetadataProcessor.processORMMetadata() line: 450	
		PersistenceUnitProcessor.processORMetadata(MetadataProcessor, boolean) line: 297	
		EntityManagerSetupImpl.predeploy(PersistenceUnitInfo, Map) line: 834	
		JavaSECMPInitializer(JPAInitializer).callPredeploy(PersistenceUnitInfo, Map, PersistenceInitializationHelper) line: 110	
		JavaSECMPInitializer(JPAInitializer).initPersistenceUnits(Archive, Map, PersistenceInitializationHelper) line: 159	
		JavaSECMPInitializer(JPAInitializer).initialize(Map, PersistenceInitializationHelper) line: 144	
		PersistenceProvider.createEntityManagerFactory(String, Map, ClassLoader) line: 107	
		PersistenceProvider.createEntityManagerFactory(String, Map) line: 67	
		Persistence.createEntityManagerFactory(String, Map) line: 158	
		Persistence.createEntityManagerFactory(String) line: 132	
  • This adds an element to the MetdataProject.
m_mappedSuperclasses	ArrayList<E>  (id=171)	
	elementData	Object[10]  (id=196)	
		[0]	MappedSuperclassAccessor  (id=199)	
  • We need to add processing that will persist the mappings on the mapped superclass tree here.
Proposal 1: Add MappedSuperclass (Object) to core Descriptor (Map based) - deprecated

We pass a RelationalDescriptor containing the mappings on the Mapped Superclass parent to the Project as part of a new HashMap (that will disallow duplicates).

Finding Mapped Superclasses during predeploy()
EntityAccessor.discoverMappedSuperclassesAndInheritanceParents()
 this.getProject().getSession().getProject().getMappedSuperclasses().put(parent.getClass(), mappedSuperclassDesc);
this	EntityAccessor  (id=165)	
	m_project	MetadataProject  (id=57)	
		m_session	ServerSession  (id=49)	
			project	Project  (id=182)	
				mappedSuperclasses	HashMap<K,V>  (id=236)	
					entrySet	null	
					keySet	null	
					loadFactor	0.75	
					modCount	1	
					size	1	
					table	HashMap$Entry<K,V>[2]  (id=248)	
						[0]	HashMap$Entry<K,V>  (id=251)	

Retrieving Mapped Superclasses during Metamodel construction
Later, when a request to build a MetamodelImpl instance is requested, we use the stored Map of mapped superclass descriptors on the Project (not the ClassDescriptors themselves).
MetamodelImpl.initialize()
 Map<Class, RelationalDescriptor> mappedSuperclassesSet = project.getMappedSuperclasses(); 
 for(Iterator<RelationalDescriptor> anIterator = mappedSuperclassesSet.values().iterator(); anIterator.hasNext();) {
 RelationalDescriptor mappedSuperclass = anIterator.next();
 .....
 }
  • How to get the mapped superclass fields into our new ClassDescriptor
  • If we add a RelationalDescriptor instance to MappedSuperclassTypeImpl - then how do we handle the fact that this class does not directly inherit from ManagedTypeImpl but does inherit from the ManagedType interface via the IdentifiableType interface?
Proposal 2: Add MappedSuperclass (Object) to core Descriptor (Set based)=
  • This is a variation on proposal 1, where we use a Set<RelationalDescriptor> to avoid duplicates instead of a Map<Class, RelationalDescriptor>.
  • We have the following design changes.
    • Store the Class (Metamodel client class) as the javaClass field on the Descriptor.
Decision:

We introduce a RelationalDescriptor for MappedSuperclasses so that we can process and retain mappings defined on these classes (usually abstract) for Metamodel processing. This custom RelationalDescriptor is only used during metamodel processing and does not actually represent a table on the data store (database).

DI 7: When to bootstrap Metamodel creation

  • We will be creating our Metamodel as a thin wrapper around the existing Metadata model.
  • The MetadataProcessor is invoked during EntityManagerFactory creation on predeploy().

DI 8: Ordering of Metamodel collections

  • Do we need to preserve ordering in the Metamodel?
  • If yes then we need to use LinkedHashMap maps instead of unordered HashMap containers
Solution:

LinkedHashMaps are used on MetamodelImpl

DI 9: Custom Logger for Metamodel

  • Do we need a custom logging wrapper class for the Metamodel API or should we just use the existing AbstractSession.log() functions directly?.
    • Use the current AbstractSession.log()

DI 10: Embeddable Support

  • How are we supporting Embeddables?
    • Via the protected java.util.Map<Class, EmbeddableTypeImpl<?>> embeddables; field on MetamodelImpl.

DI 11: Handle No Duplicates in MappedSuperclass Collection on Metamodel

We will need a collection of MappedSuperclass objects on the metamodel. We need to ensure that this collection does not allow duplicates to be added. Duplicates may be attempted because we make several passes during Metadata construction.

Override hashCode and equals on the key/value objects
  • We must override hashCode() and equals() on the key/value objects in our Collection class used to store mappedSuperclasses.
    • RelationalDescriptor will need these two methods.

DI 12: 20090506: Do we need an implementation of IdentifiableType

The IdentifiableType interface for entities and mappedSuperclasses that subclasses ManagedType is implemented by MappedSuperclassType and EntityType - do we need it?

Solution:

No

DI 16: 20090508: Implement IdentifiableType?

  • IdentifiableType - I have no implementation yet like I do for BasicImpl, Map|List|Set|CollectionImpl, EntityTypeImpl and EmbeddableTypeImpl
    • Just implement the interface in managed types

DI 13: 20090506: EntityManager/EntityManagerFactory.clear() resets MetaModel?

  • Should we null the metaModel field on a close()?
  • I think we should because all the other fields such as the session have been nulled as well - therefore we are in an invalid state.
  • We also by specification throw an ISE on a closed EM or EMF.
  • However, when we reopen the EM/EMF we end up with the same metaModel - because the PU has not changed. So do we really need to clear the metaModel?

DI14: 20090507: EntityTypeImpl support for primitive Id and Version

  • The Entity interface only supports Object versions such as Integer for Id and Version, currently the user will need to wrap their primitive types
    • public <Y> Attribute<? super X, Y> getId(Class<Y> type)
    • public <Y> Attribute<? super X, Y> getVersion(Class<Y> type)

DI 15: 20090508: What is the scope of "Declared"?

  • "Declared" keyword - as in getDeclaredMap() - how is this a subset of getMap()?
    • "Declared" means only in scope of this particular Entity - not via inheritance or mapped superclasses.
  • How are we going to determine whether an attribute is on a particular entity or is inherited via a mapped superclass?
    • We will determine if there exists a mappedSuperclass parent that contains this mapping - then it is non-declared.
Solution: Fixed
  • Issue fixed by Design Issue # 52

DI 17: 20090508: Clear Metamodel on EMF.close()?

  • em|emf.close - do we clear the metamodel?
    • No, keep the instance around
    • Q) How will we know to invalidate the current metamodel if a new persistence unit is loaded?
      • It will be left to the user handle this use case
      • For this variant I will possibly add a clear() function outside of the spec. The user will need to call this and then regenerate the new metamodel via getMetamodel() call.

DI 18: 20090508: Type Checking at metamodel creation or runtime?

  • Type checking exceptions during metamodel creation or in runtime calls? IE: getList("homeAddress") actually returns a single Entity instead of a List.
    • Leave checking to runtime and throw an exception at that time.

DI 24: 20090609: Move accessor processing out of BasicAccessor into MetadataProject

Instead of adding processing code to each Accessor class, we will add a processing step in addition to our current 3 stage processing so that we can reuse the accessor processing code already existing.

Solution
  • We need a fake sequence table name set in MappedSuperclassAccessor so that the processMetamodelDescriptor() call in MetadataProject.processStage2() succeeds.

DI 25: 20090616: Inherited parameterized generics for Element Collections (Basic)

  • An issue arose where the following Collection<X> acclaims attribute on a MappedSuperclass RatedBeerConsumer<X, Y, Z> that inherits the parameterized generic type X from an entity superclass BeerConsumer<T> cannot reference its' type.
  • The solution is to default in this case to void since we are not able to determine the parameterized type.
@MappedSuperclass
@Access(FIELD)
public abstract class RatedBeerConsumer<X, Y, Z> extends BeerConsumer<String> {
 @BasicCollection(valueColumn=@Column(name="ACCLAIM"))
 private Collection<X> acclaims;
 
and...
 
@Entity
@Table(name="CMP3_CONSUMER")
@Inheritance(strategy=JOINED)
@DiscriminatorValue(value="BC")
public class BeerConsumer<T> implements ChangeTracker, Cloneable {
ElementCollectionAccessor Modifications
  • Previously we threw a ValidationException for ElementCollectionAccessor.getReference(), we now return a Void MetadataClass in the specific use case where the classAccessor and metadataDescriptor are both MappedSuperclasses.


DI 26: 20090616: TableGenerator on Id column Accessor is null

  • During testing of the InstanceVariableAttributeAccessor, the following annotations on an @Id field in a @MappedSuperclass fail to get their accessor set.
@MappedSuperclass
public abstract class Person {
 @Id
 @GeneratedValue(strategy=TABLE, generator="PERSON_MM_TABLE_GENERATOR")
 @TableGenerator(
 name="PERSON_MM_TABLE_GENERATOR", 
 table="CMP3_MM_PERSON_SEQ", 
 pkColumnName="SEQ_MM_NAME", 
 valueColumnName="SEQ_MM_COUNT",
 pkColumnValue="CUST_MM_SEQ"
 )
 @Column(name="PERSON_ID") 
 private Integer id;
  • The offending end-of-line source
MetadataProject
 protected void processSequencingAccessors() {
...
 for (MetadataClass entityClass : m_generatedValues.keySet()) {
 ClassAccessor accessor = m_allAccessors.get(entityClass.getName());
-->NPE MetadataDescriptor descriptor = accessor.getDescriptor();
  • The stacktrace
Thread [main] (Suspended (breakpoint at line 944 in MetadataProject))	
	MetadataProject.processSequencingAccessors() line: 944	
	MetadataProject.processStage3() line: 1110	
Solution
  • We need a fake sequence table name set in MappedSuperclassAccessor so that the processMetamodelDescriptor() call in MetadataProject.processStage2() succeeds.

DI 27: 20090616: Embeddable access type conflict exception

  • The stacktrace
Exception Description: The *metadata-less* embeddable class [class org.eclipse.persistence.testing.models.jpa.inherited.Accredidation] is used in entity classes with conflicting access-types.  Its property access flag is set to [true]. It is being embedded in a [class org.eclipse.persistence.testing.models.jpa.inherited.RatedBeerConsumer] with property access flag set to: [false]. This is not allowed as this may result in inconsistent mappings of the embeddable class in different points of use. This problem can be corrected in two ways: 1. Provide metadata on class [class org.eclipse.persistence.testing.models.jpa.inherited.Accredidation] that allows the access type to be determined. 2. Ensure all users of class [class org.eclipse.persistence.testing.models.jpa.inherited.Accredidation] have the same access type.
	at org.eclipse.persistence.exceptions.ValidationException.conflictingAccessTypeForEmbeddable(ValidationException.java:2358)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EmbeddableAccessor.process(EmbeddableAccessor.java:173)
	at org.eclipse.persistence.internal.jpa.metadata.MetadataDescriptor.processAccessors(MetadataDescriptor.java:1158)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.ClassAccessor.processAccessors(ClassAccessor.java:774)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.MappedSuperclassAccessor.processMetamodelDescriptor(MappedSuperclassAccessor.java:1087)
	at org.eclipse.persistence.internal.jpa.metadata.MetadataProject.processStage2(MetadataProject.java:1074)

Solution

Add a MappedSuperclass check in process()

if(!owningDescriptor.getClassAccessor().hasMappedSuperclasses())

DI 28: 20090622: Composite PK support for MappedSuperclasses

  • The following exception is not occurring because of our change to add extra processing for MappedSuperclasses in processStage2, it happens in the use case where a composite key contains a mappedSuperclass and is inherited by a normal Entity.

RelationalDescriptor(org.eclipse.persistence.testing.models.jpa.xml.inherited.BeerConsumer --> [DatabaseTable(__METAMODEL_RESERVED_IN_MEM_ONLY_TABLE_NAME), DatabaseTable(CMP3_XML_CONSUMER)])

We should not be initializing the new mappedSuperclass descriptor in this composite PK case.

entity	EntityAccessor  (id=44)	
  m_className	"org.eclipse.persistence.testing.models.jpa.xml.inherited.ExpertBeerConsumer" (id=93)	
  m_descriptor	MetadataDescriptor  (id=66)	
    m_inheritanceRootDescriptor	MetadataDescriptor  (id=68)	
      m_descriptor	RelationalDescriptor  (id=131)	
        javaClassName	"org.eclipse.persistence.testing.models.jpa.xml.inherited.BeerConsumer" (id=70)	
        primaryKeyFields	ArrayList<E>  (id=177)	
          elementData	Object[2]  (id=256)	
            [0]	DatabaseField  (id=258)	
              name	"__PK_METAMODEL_RESERVED_IN_MEM_ONLY_FIELD_NAME" (id=260)	
              qualifiedName	"__PK_METAMODEL_RESERVED_IN_MEM_ONLY_FIELD_NAME" (id=260)	
 public void processStage2() {
 // 266912: process mappedSuperclasses without going to the database
 for(MappedSuperclassAccessor msAccessor : m_mappedSuperclassAccessors.values()) {
 if(!msAccessor.isProcessed()) { 
 msAccessor.processMetamodelDescriptor(); 
 }
 }
 
 for (EntityAccessor entity : getEntityAccessors()) {
 // If the accessor hasn't been processed yet, then process it. An
 // EntityAccessor may get fast tracked if it is an inheritance
 // parent.
 if (! entity.isProcessed()) {
-->here entity.process();
Exception Description: The multiple table foreign key relationship refers to an unknown table [DatabaseTable(qualified.__METAMODEL_RESERVED_IN_MEM_ONLY_TABLE_NAME)].
Descriptor: RelationalDescriptor(org.eclipse.persistence.testing.models.jpa.xml.inherited.ExpertBeerConsumer --> [DatabaseTable(__METAMODEL_RESERVED_IN_MEM_ONLY_TABLE_NAME), DatabaseTable(CMP3_XML_CONSUMER), DatabaseTable(XML_EXPERT_CONSUMER)])
	at org.eclipse.persistence.exceptions.DescriptorException.illegalTableNameInMultipleTableForeignKeyField(DescriptorException.java:698)
	at org.eclipse.persistence.descriptors.ClassDescriptor.verifyMultipleTablesForeignKeysTables(ClassDescriptor.java:977)
	at org.eclipse.persistence.descriptors.ClassDescriptor.createMultipleTableInsertOrder(ClassDescriptor.java:888)
	at org.eclipse.persistence.descriptors.ClassDescriptor.adjustMultipleTableInsertOrder(ClassDescriptor.java:498)
	at org.eclipse.persistence.descriptors.ClassDescriptor.preInitialize(ClassDescriptor.java:3303)
	at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.initializeDescriptors(DatabaseSessionImpl.java:429)
	at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.initializeDescriptors(DatabaseSessionImpl.java:406)
	at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.postConnectDatasource(DatabaseSessionImpl.java:669)
	at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.login(DatabaseSessionImpl.java:633)
	at org.eclipse.persistence.internal.jpa.EntityManagerFactoryProvider.login(EntityManagerFactoryProvider.java:233)
	at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:260)
Exception Description: Invalid composite primary key specification. The names of the primary key fields or properties in the primary key class [org.eclipse.persistence.testing.models.jpa.xml.merge.inherited.TelephoneNumberPK] and those of the entity bean class [class org.eclipse.persistence.testing.models.jpa.xml.merge.inherited.TelephoneNumber] must correspond and their types must be the same. Also, ensure that you have specified ID elements for the corresponding attributes in XML and/or an @Id on the corresponding fields or properties of the entity class.

at org.eclipse.persistence.exceptions.ValidationException.invalidCompositePKSpecification(ValidationException.java:1124) at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EntityAccessor.validatePrimaryKey(EntityAccessor.java:1433) at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EntityAccessor.processAccessors(EntityAccessor.java:799) at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EntityAccessor.process(EntityAccessor.java:651) at org.eclipse.persistence.internal.jpa.metadata.MetadataProject.processStage2(MetadataProject.java:1129)

Solution:

DI 29: 20090622: Multiple Table PK support for MappedSuperclasses

  • Most of our issues at this point surround class heirarchies involving a MappedSuperclass between entities.
    • as in Entity --> MappedSuperclass --> Entity
  • Our addition of fake PK field names do not handle multiple table PK's.
Exception Description: Multiple table primary key field names must be fully qualified.
Descriptor: RelationalDescriptor(org.eclipse.persistence.testing.models.jpa.xml.inherited.ExpertBeerConsumer --> [DatabaseTable(__RESERVED_IN_MEM_ONLY_TABLE_NAME), DatabaseTable(XML_EXPERT_CONSUMER)])
	at org.eclipse.persistence.exceptions.DescriptorException.multipleTablePrimaryKeyMustBeFullyQualified(DescriptorException.java:967)
	at org.eclipse.persistence.descriptors.ClassDescriptor.addForeignKeyFieldForMultipleTable(ClassDescriptor.java:405)
	at org.eclipse.persistence.internal.jpa.metadata.MetadataDescriptor.addForeignKeyFieldForMultipleTable(MetadataDescriptor.java:288)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EntityAccessor.addMultipleTableKeyFields(EntityAccessor.java:170)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EntityAccessor.processInheritancePrimaryKeyJoinColumns(EntityAccessor.java:981)
	at org.eclipse.persistence.internal.jpa.metadata.inheritance.InheritanceMetadata.process(InheritanceMetadata.java:144)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EntityAccessor.processTableAndInheritance(EntityAccessor.java:1226)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EntityAccessor.process(EntityAccessor.java:633)
	at org.eclipse.persistence.internal.jpa.metadata.MetadataProject.processStage2(MetadataProject.java:1129)
  • Cause: The table.name on targetPrimaryKeyField is not set.
    • sourceForeignKeyField DatabaseField (id=64)
      • table DatabaseTable (id=70)
      • name "XML_EXPERT_CONSUMER" (id=79)
      • qualifiedName "XML_EXPERT_CONSUMER" (id=79)
      • tableQualifier "" (id=69)
    • targetPrimaryKeyField DatabaseField (id=66)
      • table DatabaseTable (id=72)
      • name "" (id=69)
      • qualifiedName "" (id=69)
      • tableQualifier "" (id=69)
  • The primaryKeyTable (eventually sourceTable) on the descriptor (on the pkField) is not set.
addMultipleTableKeyFields(pkJoinColumns, getDescriptor().getPrimaryKeyTable(), getDescriptor().getPrimaryTable(), MetadataLogger.INHERITANCE_PK_COLUMN, MetadataLogger.INHERITANCE_FK_COLUMN);
this	EntityAccessor  (id=31)	
  m_descriptor	MetadataDescriptor  (id=2421)	
    m_descriptor	RelationalDescriptor  (id=2438)	
      javaClassName	"org.eclipse.persistence.testing.models.jpa.xml.inherited.ExpertBeerConsumer" (id=2407)	
	primaryKeyFields	ArrayList<E>  (id=2581)	
		elementData	Object[2]  (id=2594)	
			[0]	DatabaseField  (id=2598)	
				name	"__PK_RESERVED_IN_MEM_ONLY_FIELD_NAME" (id=78)	
				qualifiedName	"__PK_RESERVED_IN_MEM_ONLY_FIELD_NAME" (id=78)	
				table	DatabaseTable  (id=72)	
					name	"" (id=57)	
					qualifiedName	"" (id=57)	
					tableQualifier	"" (id=57)	

and

this	EntityAccessor  (id=30)	
  m_className	"org.eclipse.persistence.testing.models.jpa.xml.merge.inherited.Alpine" (id=120)	
    m_descriptor	MetadataDescriptor  (id=122)	
      m_descriptor	RelationalDescriptor  (id=52)	
	alias	"MergeAlpine" (id=125)	
		mappings	NonSynchronizedVector  (id=272)	
			elementCount	5	
			elementData	Object[10]  (id=281)	
				[0]	DirectToFieldMapping  (id=283)	
					attributeName	"id" (id=309)	
					descriptor	RelationalDescriptor  (id=52)	
						primaryKeyFields	ArrayList<E>  (id=359)
							elementData	Object[2]  (id=368)	
								[0]	DatabaseField  (id=374)	
									name	"__PK_RESERVED_IN_MEM_ONLY_FIELD_NAME" (id=379)	
									qualifiedName	"__PK_RESERVED_IN_MEM_ONLY_FIELD_NAME" (id=379)	
									table	DatabaseTable  (id=382)	
								[1]	DatabaseField  (id=311)	
									name	"ID" (id=380)	
									qualifiedName	"ID" (id=380)	
									table	null	

then

Exception Description: The map key [] on the entity class [class org.eclipse.persistence.testing.models.jpa.inherited.RatedBeerConsumer] could not be found for the mapping [org.eclipse.persistence.mappings.DirectMapMapping[awards]].
	at org.eclipse.persistence.exceptions.ValidationException.couldNotFindMapKey(ValidationException.java:2053)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.MappingAccessor.processMapKey(MappingAccessor.java:1294)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.MappingAccessor.processContainerPolicyAndIndirection(MappingAccessor.java:1116)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.DirectCollectionAccessor.processDirectMapMapping(DirectCollectionAccessor.java:328)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.BasicMapAccessor.process(BasicMapAccessor.java:178)
	at org.eclipse.persistence.internal.jpa.metadata.MetadataProject.processDirectCollectionAccessors(MetadataProject.java:833)
	at org.eclipse.persistence.internal.jpa.metadata.MetadataProject.processStage3(MetadataProject.java:1154)
  • Fixed by checking that we are processing a Metamodel reserved table name.
 if(referenceDescriptor.hasDescriptorReservedForMetamodel())
Solution:
  • We need to add fake names to both tables in ClassDescriptor
if ((!sourceForeignKeyField.hasTableName()) || (!targetPrimaryKeyField.hasTableName())) {


  • The following 2 checks on the table and PK avoid fully processing our custom descriptors.
 public void processDirectCollectionAccessors() {
 for (DirectCollectionAccessor accessor : m_directCollectionAccessors) {
--> if(!accessor.getDescriptor().isPkClassDefinedOnMappedSuperclass()) {
--> if(!accessor.getDescriptor().hasDescriptorReservedForMetamodel()) {
 accessor.process();
 }
 }
 }
 }
  • And where we validate the PKClass, we check first to see if it our fake ID field by cross-referencing the PK field name on the accessor.
  • We are missing annotation processing after skipping accessor.process() above
Exception Description: The @JoinColumns on the annotated element [method getBeerConsumer] from the entity class [class org.eclipse.persistence.testing.models.jpa.xml.inherited.Certification] is incomplete. When the source entity class uses a composite primary key, a @JoinColumn must be specified for each join column using the @JoinColumns. Both the name and the referenceColumnName elements must be specified in each such @JoinColumn.
	at org.eclipse.persistence.exceptions.ValidationException.incompleteJoinColumnsSpecified(ValidationException.java:1789)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.MappingAccessor.getJoinColumnsAndValidate(MappingAccessor.java:461)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.MappingAccessor.getJoinColumns(MappingAccessor.java:418)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.ObjectAccessor.processOneToOneForeignKeyRelationship(ObjectAccessor.java:463)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.ObjectAccessor.processOwningMappingKeys(ObjectAccessor.java:509)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.ManyToOneAccessor.process(ManyToOneAccessor.java:97)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.RelationshipAccessor.processRelationship(RelationshipAccessor.java:443)
	at org.eclipse.persistence.internal.jpa.metadata.MetadataProject.processRelationshipAccessors(MetadataProject.java:879)
	at org.eclipse.persistence.internal.jpa.metadata.MetadataProject.processStage3(MetadataProject.java:1167)


  • In this case the join column size on the parent (MappedSuperclass) descriptor does not match the inheriting entity
 if (joinColumns.size() != descriptor.getPrimaryKeyFields().size()) {
  • child
    • org.eclipse.persistence.testing.models.jpa.xml.inherited.NoviceBeerConsumer
  • parent
    • RelationalDescriptor(org.eclipse.persistence.testing.models.jpa.xml.inherited.BeerConsumer --> [DatabaseTable(__METAMODEL_RESERVED_IN_MEM_ONLY_TABLE_NAME), DatabaseTable(CMP3_XML_CONSUMER)])
Thread [main] (Suspended (breakpoint at line 464 in MappingAccessor))	
	ManyToManyAccessor(MappingAccessor).getJoinColumnsAndValidate(List<JoinColumnMetadata>, MetadataDescriptor) line: 464	
	ManyToManyAccessor(CollectionAccessor).processJoinTable(ManyToManyMapping, JoinTableMetadata) line: 562	
	ManyToManyAccessor.process() line: 105	
	ManyToManyAccessor(RelationshipAccessor).processRelationship() line: 443	
	MetadataDescriptor.getMappingForAttributeName(String, MetadataAccessor) line: 728	
	ManyToManyAccessor(RelationshipAccessor).getOwningMapping(String) line: 273	
	ManyToManyAccessor.process() line: 113	
	ManyToManyAccessor(RelationshipAccessor).processRelationship() line: 443	
	MetadataProject.processRelationshipAccessors() line: 879	
	MetadataProject.processStage3() line: 1167	
  • Fixed by returning a default DatabaseTable when requested of our reserved descriptor.
 public DatabaseTable getPrimaryTable() {
 if (m_primaryTable == null && isInheritanceSubclass()) {
 return getInheritanceRootDescriptor().getPrimaryTable();
 } else {
 if (m_descriptor.isAggregateDescriptor()) {
 // Aggregate descriptors don't have tables, just return a 
 // a default empty table.
 return new DatabaseTable();
 }
 
 // 266912: return a reserved table name for Metamodel processing
--> if(this.hasDescriptorReservedForMetamodel()) {
--> return new DatabaseTable();
  • 20090623:1500 Full JPA test suite preprocessing completes, however we have errors in the regression suite.
    [junit] Exception [EclipseLink-46] (Eclipse Persistence Services - 2.0.0.qualifier): org.eclipse.persistence.exceptions.DescriptorException
    [junit] Exception Description: There should be one non-read-only mapping defined for the primary key field [__METAMODEL_RESERVED_IN_MEM_ONLY_TABLE_NAME.__PK_METAMODEL_RESERVED_IN_MEM_ONLY_FIELD_NAME].
    [junit] Descriptor: RelationalDescriptor(org.eclipse.persistence.testing.models.jpa.xml.cacheable.SubCacheableFalseEntity --> [DatabaseTable(__METAMODEL_RESERVED_IN_MEM_ONLY_TABLE_NAME), DatabaseTable(XML_SUB_CACHEABLE_FALSE)])
    [junit]     at org.eclipse.persistence.exceptions.DescriptorException.noMappingForPrimaryKey(DescriptorException.java:1048)
    [junit]     at org.eclipse.persistence.internal.descriptors.ObjectBuilder.initializePrimaryKey(ObjectBuilder.java:2472)
    [junit]     at org.eclipse.persistence.internal.descriptors.ObjectBuilder.initialize(ObjectBuilder.java:2349)
    [junit]     at org.eclipse.persistence.descriptors.ClassDescriptor.initialize(ClassDescriptor.java:2691)
    [junit]     at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.initializeDescriptors(DatabaseSessionImpl.java:449)
    [junit]     at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.initializeDescriptors(DatabaseSessionImpl.java:406)
    [junit]     at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.postConnectDatasource(DatabaseSessionImpl.java:669)
    [junit]     at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.login(DatabaseSessionImpl.java:633)
    [junit]     at org.eclipse.persistence.internal.jpa.EntityManagerFactoryProvider.login(EntityManagerFactoryProvider.java:233)
    [junit]     at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:260)
    [junit]     at org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.getServerSession(EntityManagerFactoryImpl.java:135)
    [junit]     at org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.createEntityManagerImpl(EntityManagerFactoryImpl.java:187)
    [junit]     at org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:175)
    [junit]     at org.eclipse.persistence.testing.framework.junit.JUnitTestCase.getServerSession(JUnitTestCase.java:299)
    [junit]     at org.eclipse.persistence.testing.framework.junit.JUnitTestCase.getEntityManagerFactory(JUnitTestCase.java:316)
    [junit]     at org.eclipse.persistence.testing.framework.junit.JUnitTestCase.getEntityManagerFactory(JUnitTestCase.java:303)
    [junit]     at org.eclipse.persistence.testing.framework.junit.JUnitTestCase.getServerSession(JUnitTestCase.java:299)
    [junit]     at org.eclipse.persistence.testing.tests.jpa.cacheable.CacheableModelJunitTest.testCachingOnDISABLE_SELECTIVE(CacheableModelJunitTest.java:167)


    [junit] Exception Description: There should be one non-read-only mapping defined for the primary key field
 [__METAMODEL_RESERVED_IN_MEM_ONLY_TABLE_NAME.__PK_METAMODEL_RESERVED_IN_MEM_ONLY_FIELD_NAME].
    [junit] Descriptor: RelationalDescriptor(org.eclipse.persistence.testing.models.jpa.xml.cacheable.SubCache
ableFalseEntity --> [DatabaseTable(__METAMODEL_RESERVED_IN_MEM_ONLY_TABLE_NAME), DatabaseTable(XML_SUB_CACHEAB
LE_FALSE)])
    [junit]
    [junit] Runtime Exceptions:
    [junit] ---------------------------------------------------------
    [junit] )
    [junit] Tests run: 1219, Failures: 5, Errors: 31, Time elapsed: 1,153.016 sec
    [junit] Test org.eclipse.persistence.testing.tests.jpa.FullRegressionTestSuite FAILED
  • see

TEST NAME: testSequencePreallocationUsingCallbackTest(org.eclipse.persistence.testing.tests.jpa.fieldaccess.advanced.EntityManagerJUnitTestSuite)

DI 30: 20090623: BasicMap Parameterized Generic return type cannot be the Void class

MapAccessors cannot use Void because it does not implement Serializable. We will not process this accessor when isMappedSuperclass() is true.

Exception Description: The type [class java.lang.Void] for the attribute [acclaims] on the entity class [class org.eclipse.persistence.testing.models.jpa.inherited.RatedBeerConsumer] is not a valid type for a serialized mapping. The attribute type must implement the Serializable interface.
	at org.eclipse.persistence.exceptions.ValidationException.invalidTypeForSerializedAttribute(ValidationException.java:1078)
	at org.eclipse.persistence.internal.jpa.metadata.converters.SerializedMetadata.process(SerializedMetadata.java:67)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.MappingAccessor.processSerialized(MappingAccessor.java:1496)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.MappingAccessor.processJPAConverters(MappingAccessor.java:1270)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.MappingAccessor.processMappingConverter(MappingAccessor.java:1363)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.MappingAccessor.processMappingValueConverter(MappingAccessor.java:1381)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.DirectCollectionAccessor.processDirectCollectionMapping(DirectCollectionAccessor.java:330)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.BasicCollectionAccessor.process(BasicCollectionAccessor.java:157)
	at org.eclipse.persistence.internal.jpa.metadata.MetadataProject.processDirectCollectionAccessors(MetadataProject.java:833)FATAL ERROR in native method: processing of -javaagent failed

	at org.eclipse.persistence.internal.jpa.metadata.MetadataProject.processStage3(MetadataProject.java:1150)

DI 31: 20090703: EntityTypeImpl inherits from ManagedType but MappedSuperclassTypeImpl does not

We have the following heirarchy.

  • Interfaces
  • Type
    • ManagedType
      • IdentifiableType
        • EntityType
        • MappedSuperclassType
  • Concrete classes
    • EntityTypeImpl extends ManagedTypeImpl implements EntityType (which extends IdentifiableType, Bindable)
    • MappedSuperclassTypeImpl extends Object implements MappedSuperclassType (which extends IdentifiableType)


Solution:
  • IdentifiableType requires an implementation class.
  • Introduce a new IdentifiableTypeImpl concrete class that extends ManagedTypeImpl
    • MappedSuperclassTypeImpl will also inherit from ManagedTypeImpl via IdentifiableTypeImpl
    • EntityTypeImpl now inherits from the IdentifiableTypeImpl subclass instead of from ManagedTypeImpl


DI 32: 20090703: Metamodel superType requires javaClass set on custom descriptor on MappedSuperclassAccessor

See enhancement 282518

Solution:

We are setting the javaClass later during metamodel processing instead of during metadata processing - so we can avoid persistence operations on our custom discriptor.

See FishEye Rev# 4611

DI 33: 20090707: Application Server Container - Verify that we get the correct classLoader from the right session

In MetamodelImpl.initialize() when we are iterating the MappedSuperclass descriptors, we must test and verify that the classLoader is valid in container mode and not just for SE clients.

  • Currently we are using getSession().getActiveSession() just in case we running on an external transaction controller, otherwise getSession() and getActiveSession() return the same session.
    • ClassLoader classLoader = this.getSession().getActiveSession().getClass().getClassLoader();
Solution:

Continue to use the above code until we test the eclipselink.jar on an app server.

DI 34: 20090707: Temporary CNFE when loading Metamodel Impl class

  • Just before resolving an internal metamodel API class we get a ClassNotFoundException on the classLoader when running in Eclipse.
  • I expect that this is normal behavoir and that the catch block in the JRE eventually finds the class on a different branch in the tree - odd since the referencing class is in the same package as the class causing the CNFE.
MappedSuperclassTypeImpl<?> mappedSuperclassType = new MappedSuperclassTypeImpl(this, descriptor);
Thread [Thread-3] (Suspended)	
	ClassNotFoundException(Throwable).<init>(String, Throwable) line: 217	
	ClassNotFoundException(Exception).<init>(String, Throwable) line: not available	
	ClassNotFoundException.<init>(String) line: not available	
	ClassLoader.findBootstrapClass(String) line: not available [native method]	
	Launcher$ExtClassLoader(ClassLoader).findBootstrapClass0(String) line: not available	
	Launcher$ExtClassLoader(ClassLoader).loadClass(String, boolean) line: not available	
	Launcher$AppClassLoader(ClassLoader).loadClass(String, boolean) line: not available	
	Launcher$AppClassLoader.loadClass(String, boolean) line: not available	
	Launcher$AppClassLoader(ClassLoader).loadClass(String) line: not available	
	Launcher$AppClassLoader(ClassLoader).loadClassInternal(String) line: not available	
	MetamodelImpl.initialize() line: 167	
	MetamodelImpl.<init>(DatabaseSession) line: 77	
	MetamodelImpl.<init>(EntityManagerFactory) line: 82	
	EntityManagerFactoryImpl.getMetamodel() line: 416	
	EntityManagerImpl.getMetamodel() line: 1987	
	MetamodelMetamodelTest.testImplementation() line: 449	
  • The class is found in another loader ok
    • org/eclipse/persistence/internal/jpa/metamodel/MappedSuperclassTypeImpl
Thread [Thread-3] (Suspended)	
	MappedSuperclassTypeImpl<X>.<init>(MetamodelImpl, RelationalDescriptor) line: 59	
	MetamodelImpl.initialize() line: 167	
	MetamodelImpl.<init>(DatabaseSession) line: 77	
	MetamodelImpl.<init>(EntityManagerFactory) line: 82	
	EntityManagerFactoryImpl.getMetamodel() line: 416	
	EntityManagerImpl.getMetamodel() line: 1987	
	MetamodelMetamodelTest.testImplementation() line: 449	


Solution:

No code change required

DI 35: 20090707: Where to initialize IdentifiableTypeImpl.supertype

  • The following field on IdentifiableTypeImpl currently has a delayed instantiation after all ManagedTypeImpl objects have been instantiated.
  • MetamodelImpl.initialize() handles setting the supertype field for all MappedSuperclassType and EntityType classes.
    • protected IdentifiableType<? super X> supertype;
Solution:
  • We will continue to initialize the supertype in the second part of initialize() - this will create an IdentifiableType that is in an inconsistent state only within the initialize() function. This is acceptable because the type will be fully instantiated before it is used by any public API.

DI 36: 20090708: The StaticMetamodel requires an implementation class

  • The StaticMetamodel interface does not have a concrete class yet (it used to be TypeSafeMetamodel) - see bug# 282868
  • No work may be required beyond providing this annotation via the javax.persistence interface class.

DI 37: 20090708: CollectionAttribute acts as a peer of Map, Set, List but should be a super interface

In Java the Set and List interfaces inherit from Collection, with Map acting like a Collection but not inheriting from Collection - but in the Metamodel, Collection is at the same level as these collection child interfaces.

We may need special handling for CollectionAttribute so that we can treat the following.

  • PluralAttribute (previously AbstractCollectionAttribute)
    • Collection
    • ListAttribute
    • MapAttribute
    • SetAttribute

Like

  • Collection
    • List
    • Set
  • Map
Analysis:
  • We have 2 scenarios here, 1) we treat CollectionAttribute distinct from List, and Set, or 2) we tread CollectionAttribute as the superclass of List and Set without actually being a super interface.
  • It is evident from the fact that we have only getAttributes(), getCollections() and getSingularAttributes() in ManagedType - that a Collection is a superset of all Set, List and even Map.
Solution: Fixed
  • Treat Collection as a super interface of Set, List and Map in Java but treat is a peer of Set, List and Map in the Metamodel.
  • Solved in SVN Rev# 4820 and 4827 as part of Issue #58

DI 38: 20090708: OneToOne Set defaults to IndirectList

  • The following OneToOne attribute defined as a Collection and instantiated as a HashSet will default to an IndirectList - which will create the wrong ListAttributeImpl member for ManagedType.initialize().
public class Manufacturer extends Corporation implements java.io.Serializable{
 @OneToMany(cascade=ALL, mappedBy="manufacturer")
 private Collection<Computer> computers = new HashSet<Computer>();
  • Causing a CCE later on in
java.lang.ClassCastException: org.eclipse.persistence.internal.jpa.metamodel.ListAttributeImpl cannot be cast to javax.persistence.metamodel.CollectionAttribute
	at org.eclipse.persistence.internal.jpa.metamodel.IdentifiableTypeImpl.getCollectionHelper(IdentifiableTypeImpl.java:120)
	at org.eclipse.persistence.internal.jpa.metamodel.IdentifiableTypeImpl.getCollection(IdentifiableTypeImpl.java:47)
  • However, by changing the declared type to Set - we will default to an IndirectSet - which will create a correct SetAttributeImpl member for ManagedType.initialize().
public class Manufacturer extends Corporation implements java.io.Serializable{
 @OneToMany(cascade=ALL, mappedBy="manufacturer")
 private Set<Computer> computers = new HashSet<Computer>();
Solution: Fixed
  • Add support for Indirect collections by also checking that this type is assignable on the ContainerPolicy.
 colMapping.getContainerPolicy().getContainerClass().isAssignableFrom(IndirectSet.class)

DI 39: 20090708: Handle MappedSuperclass in ManagedTypeImpl.create()

  • We currently create an EmbeddableTypeImpl or an EntityTypeImpl depending on whether the RelationalDescriptor is aggregate or not. We need to also handle MappedSuperclasses.
  • We also need to investigate whether we support descriptor types NORMAL or AGGREGATE_COLLECTION.
  • We need to describe why we do not support descriptor type INTERFACE - we do not support variable OneToOne mappings.
Solution:
  • 20090813: The MappedSuperclass creation code has been moved from Metamodel.initialize() to ManagedTypeImpl.create() and its' subclass
  • The MappedSuperclass part is in SVN rev#.

DI 40: 20090708: Type hierarchy numbers are missing MappedSuperclass types

  • In MetamodelImpl we have the following fields and example number of instances
    • entities = 8
    • mappedSuperclasses = 4
    • embeddable = 2
    • (implied basic types) = 1
  • The combinations of these types should be
    • Types = Basic + Embeddable + MappedSuperclass + Entity = (1 + 2 + 4 + 8)
    • managedTypes = Embeddable + MappedSuperclass + Entity = (2 + 4 + 8)
    • IdentifiableTypes = MappedSuperclass + Entity = (4 + 8)
  • However, I am not seeing MappedSuperclasses in types
Solution: Fixed
  • Move member processing of all Types to a separate iteration step after all types have already been created
  • Add another put to the MappedSuperclass processing loop in MetamodelImpl.initialize()
    • this.types.put(mappedSuperclassType.getJavaType(), mappedSuperclassType);
  • We now get the following valid Metamodel (It contains no EmbeddableTypes or BasicTypes)
metamodel	MetamodelImpl  (id=47)	
	embeddables	LinkedHashMap<K,V>  (id=62)	
		size	0	
		table	HashMap$Entry<K,V>[16]  (id=439)	
	entities	LinkedHashMap<K,V>  (id=70)	
		size	10	
		table	HashMap$Entry<K,V>[16]  (id=435)	
	managedTypes	LinkedHashMap<K,V>  (id=71)	
		size	14	
		table	HashMap$Entry<K,V>[32]  (id=431)	
	mappedSuperclasses	HashSet<E>  (id=72)	
		map	HashMap<K,V>  (id=88)	
			size	4	
			table	HashMap$Entry<K,V>[16]  (id=95)	
	types	LinkedHashMap<K,V>  (id=80)	
		modCount	14	
		size	14	
		table	HashMap$Entry<K,V>[32]  (id=117)	


DI 41: When to throw IAE for missing member or wrong type on get() call

In ManagedType for all the get() calls that also check the type of the member being returned - the javadoc comment for IllegalArgumentException handling may need some expansion to handle the case where the type exists but the type is incorrect.
 * @throws IllegalArgumentException if attribute of the given
 * name and type is not present in the managed type
    • There are 3 valid cases out of a possible 4 for these functions
      • 1) attribute exists, type is correct = normal
      • 2) attribute exists, type is incorrect = throw IAE (assumed only for type) - not presently in the spec.
      • 3) attribute missing, type is X=irrelevant = throw IEA (according to javadoc) - here we have no way of verifying whether the type is correct
    • Based on the 3 cases above I would expect that the javadoc should read
 * @throws IllegalArgumentException if attribute of the given
 * name and type is not present in the managed type
 * @throws IllegalArgumentException if attribute of the given
 * name is present but the type is incorrect in the managed type
  • So if we pass in the wrong type - here EntityType:Memory instead of EntityType:Computer, we should get an IAE even though the attribute "computers" was found.
java.lang.IllegalArgumentException: Expected attribute type 
  [class org.eclipse.persistence.testing.models.jpa.metamodel.Memory] 
  on the existing attribute [computers] 
  on the managed type [ManagedTypeImpl[RelationalDescriptor
  (org.eclipse.persistence.testing.models.jpa.metamodel.Manufacturer -->
   [DatabaseTable(CMP3_MM_MANUF)])]] 
  but found attribute type 
  [org.eclipse.persistence.testing.models.jpa.metamodel.Computer].
Solution:

Pending review but, return two different IAE exceptions based on whether we are in case 2 or 3


DI 42: 20090709: IdentifiableType.supertype - what do top-level types set it to

  • If we have a metamodel type hierarchy like the example in this document where there are several entities or mappedSuperclasses that are at the root of their inheritance tree (Person, Computer, Board, Memory, Location). What do we set the supertype to? null or Object.
  • The API reserves null to mean no supertype, but I think we should differentiate between null==not set and null==no supertype - but we are constrained by the fact that a supertype must be an IdentifiableType - Object is not an IdentifiableType
 /**
 * Return the identifiable type that corresponds to the most
 * specific mapped superclass or entity extended by the entity 
 * or mapped superclass. 
 * @return supertype of identifiable type or null if no such supertype
 */
 public IdentifiableType<? super X> getSupertype() {
Test model hiearchy:
entityManufacturer EntityTypeImpl<X> (id=5854) 
 javaClass Class<T> (org.eclipse.persistence.testing.models.jpa.metamodel.Manufacturer) (id=4201) 
 superType MappedSuperclassTypeImpl<X> (id=5858) 
 javaClass Class<T> (org.eclipse.persistence.testing.models.jpa.metamodel.Corporation) (id=870) 
 superType MappedSuperclassTypeImpl<X> (id=5838) 
 javaClass Class<T> (org.eclipse.persistence.testing.models.jpa.metamodel.Person) (id=1488) 
 superType null
  • Normally for example Manufacturer.getSupertype() would return Corporation since it inherits from it.
  • However, currently however Person.getSupertype() returns null (we don't know if a null means unset or a top-level identifiableType)
Solution:

Return null for any top level managed types and not Object.


DI 43: 20090710: Implement getDeclaredX() methods

  • The following 15 typesafe and non-typesafe functions need to be implemented on ManagedType (for BasicType and EmbeddableType) or in combination with IdentifiableType (for EntityType and MappedSuperclassType).
    • ManagedType
      • getDeclaredAttribute(String)
      • getDeclaredAttributes()
      • getDeclaredAttribute(String)
      • getDeclaredCollection(String)
      • getDeclaredCollection(String, Class)
      • getDeclaredCollections()
      • getDeclaredList(String)
      • getDeclaredList(String, Class)
      • getDeclaredMap(String)
      • getDeclaredMap(String, Class)
      • getDeclaredSet(String)
      • getDeclaredSet(String, Class)
      • getDeclaredSingularAttribute(String)
      • getDeclaredSingularAttribute(String, Class)
      • getDeclaredSingularAttributes()
    • IdentifiableType
      • getDeclaredId(Class)
      • getDeclaredVersion(Class)


Analysis:
  • Constraints:
    • C1: Return the valued attribute of the managed type that corresponds to the specified name and Java element type.
    • C2: throws IllegalArgumentException if attribute of the given name and type is not present in the managed type
  • Q1) We know Basic types can't but can Embeddable types declare attributes/mappings?
    • The answer will determine how high in the hierarcy we implement declared functionality (IdentifiableType, ManagedType or Type)
  • Declared Use Cases:
    • Use Case Partitioning:
      • - attribute positioning(none, current, 1st parent, Nth parent)
      • - attribute type (right, wrong type)
      • - attribute classification for current and parents (Entity, MappedSuperclass, embeddable?, Basic?)
    • UC1) Mapping is not declared on current attribute (or its' superclasses)
      • - Throw IAException
    • UC2) Mapping is declared on current attribute but is of the wrong type
      • - Throw IAException
    • UC3) Attribute is found on on current managedType Entity/MappedSuperclass - (but not found anywhere on the supertype hierarchy - declared above). In this case we do the reverse - keep checking only when attribute is null.
      • - Return attribute
    • UC4) Mapping is declared on immediate superclass
      • - Throw IAException
    • UC5) Mapping is declared on Nth superclass
      • - Throw IAException
Design:
  • We use two functions, one public, one a private recursive function.
    • If the attribute is not found at the current level or above, or is of the wrong type - throw an IAException
    • If the attribute is found then we still need to search to the top of the hierarchy tree to verify it is not declared above - if it is also not found above - return the attribute in this case only
Solution:

DI 44: 20090710: Criteria API FromImpl equality check requires enum instead of BasicTypeImpl

  • Now that i have SingularAttribute.getType() and Bindable.getBindableJavaType() working for SingularAttributeImpl.java - there are a couple adjustments in its' usage in FromImpl that will require changes that we were unable to test previously.
  • Note: this same change should be made to all the other instances of .equals(PeristenceType.
this	BasicTypeImpl<X>  (id=137)	
	javaClass	Class<T> (java.lang.String) (id=149)	
arg0	Type$PersistenceType  (id=184)	
	name	"BASIC" (id=187)	
	ordinal	3	
  • In the following code from FromImpl.java:300
 }else{
 Class clazz = ((SingularAttribute)attribute).getBindableJavaType();
--> if (((SingularAttribute)attribute).getType().equals(PersistenceType.BASIC)){
 return new PathImpl<Y>(this, this.metamodel, clazz, this.currentNode.get(attribute.getName()), (Bindable)attribute);
 }else{
 join = new JoinImpl(this, this.metamodel.type(clazz), this.metamodel, clazz, this.currentNode.get(attribute.getName()), (Bindable)attribute);
 }
Solution:
  • The fix is to to compare enums by changing
 if (((SingularAttribute)attribute).getType().equals(PersistenceType.BASIC)){
  • to
 if (((SingularAttribute)attribute).getType().getPersistenceType().equals(PersistenceType.BASIC)){
  • change
 }else{
 Class clazz = ((SingularAttribute)attribute).getBindableJavaType();
 //if (((SingularAttribute)attribute).getType().equals(PersistenceType.BASIC)){
 if (((SingularAttribute)attribute).getType().getPersistenceType().equals(PersistenceType.BASIC)){ 
-->modified path return new PathImpl<Y>(this, this.metamodel, clazz, this.currentNode.get(attribute.getName()), (Bindable)attribute);
 }else{
-->original path join = new JoinImpl(this, this.metamodel.type(clazz), this.metamodel, clazz, this.currentNode.get(attribute.getName()), (Bindable)attribute);
 }
  • Results with addition of .getPersistenceType() above
results	Vector<E>  (id=205)	
	capacityIncrement	0	
	elementCount	2	
	elementData	Object[2]  (id=212)	
		[0]	Computer  (id=77)	
		[1]	Computer  (id=86)	
	modCount	2	
  • Results with no code changes
results	Vector<E>  (id=205)	
	capacityIncrement	0	
	elementCount	2	
	elementData	Object[2]  (id=209)	
		[0]	Computer  (id=88)	
		[1]	Computer  (id=79)	
	modCount	2	
  • I get the same results for either code path but I think that the [return new PathImpl] positive part of the if will never execute --> .equals() ill always be false without the addition of .getPersistenceType().
  • For another use case - try to cause the following ISE.
 if (((SingularAttribute)attribute).getType().equals(PersistenceType.BASIC)){
 throw new IllegalStateException(ExceptionLocalization.buildMessage("CAN_NOT_JOIN_TO_BASIC"));
 }

DI 45: 20090713: getAttributes() returns a new HashSet - not the actual Metamodel.members.values Collection

  • In ManagedTypeImpl.getAttributes() and especially ManagedTypeImpl.getDeclaredAttributes() - we return a new HashSet instead of directly returning the Collection of values from the Metamodel.members HashMap.
  • See also getCollections()

DI 46: 20090715: Metamodel initialization on EntityManagerFactory creation

  • See bug# 283607
  • In section 6.2.3 "Bootstrapping" of the JPA 2.0 specification - it states that the metamodel must be initialized on EMF creation.
    • "When the entity manager factory for a persistence unit is created, it is the responsibility of the persistence

provider to initialize the state of the metamodel classes of the persistence unit. Any generated metamodel classes must be accessible on the classpath."

  • This design document involves the "dynamic" metamodel and not the static canonical metamodel - however in the introduction for section 6.2, the spec describes the two metamodel access methods dynamic or static to be interchangable.
  • So my question is - do we allow the dynamic metamodel to continue to to be lazy initialized on the EMF when a getMetamodel() call is requested - or do we create the metamodel automatically during EMF construction.
    • This question is best answered when we have the requirements of the canonical metamodel - which will use the dynamic metamodel to introspect the metadata.

DI 47: 20090715: Implement IdentifiableType.getIdType() for composite keys

 this.metamodel.getType(((CMP3Policy) cmpPolicy).getPKClass());
  • However, a test model and more investigation is required for composite keys where the CMP3Policy is null.
  • We need scenarios where this policy is null.
 java.util.List<DatabaseMapping> pkMappings = getDescriptor().getObjectBuilder().getPrimaryKeyMappings(); 
 if (pkMappings.size() == 1) {
 Class aClass = pkMappings.get(0).getAttributeClassification(); // null for OneToOneMapping
 // lookup class in our types map
 Type<?> aType = this.metamodel.type(aClass);
 return aType;
 }

DI 48: 20090721: Implement Map support

  • We are getting a NPE during metamodel creation for the JPQL test suite that includes a Map where the Primary Key is an @IdClass.
    • policy.getKeyType() is null.
 protected MapAttributeImpl(ManagedTypeImpl<X> managedType, CollectionMapping mapping) {
 super(managedType, mapping);
 MapContainerPolicy policy = (MapContainerPolicy) mapping.getContainerPolicy();
---> Type<?> keyType = managedType.getMetamodel().getType(policy.getKeyType().getClass());
Analysis:
  • For the object model where there is a PK on the mappedSuperclass of the owning entity there is no NPE issue but I am getting the class twice in error.
  • The thing to note here is that "key" means PK not the k:key of the Map<K,v>
  • The key is
 private java.lang.Integer
org.eclipse.persistence.testing.models.jpa.metamodel.Person.id
@Entity(name="HardwareDesignerMetamodel")
@Table(name="CMP3_MM_HWDESIGNER")
public class HardwareDesigner extends Designer implements java.io.Serializable{
 // The M:1 side is the owning side
 @ManyToOne(fetch=EAGER)//LAZY)
 @JoinTable(name="CMP3_MM_MANUF_MM_HWDES_MAP", 
 joinColumns = @JoinColumn(name="DESIGNER_MAP_ID"), 
 inverseJoinColumns =@JoinColumn(name="MANUF_ID")) 
 private Manufacturer mappedEmployer;
 
@Entity(name="ManufacturerMetamodel")
@Table(name="CMP3_MM_MANUF")
public class Manufacturer extends Corporation implements java.io.Serializable{
 // If a JoinTable with a JoinColumn is used - then we need a mappedBy on
the inverse side here
 @OneToMany(cascade=ALL, mappedBy="mappedEmployer")
 private Map<String, HardwareDesigner> hardwareDesignersMap = new
HashMap<String, HardwareDesigner>();
  • and associated relational model join table
 public static TableDefinition
buildMANUFACTURER_HARDWAREDESIGNER_MAP_JOINTable() {
 TableDefinition table = new TableDefinition();
 table.setName("CMP3_MM_MANUF_MM_HWDES_MAP");
 
 FieldDefinition field1 = new FieldDefinition();
 field1.setName("MANUF_ID");
 field1.setTypeName("NUMERIC");
 field1.setSize(15);
 field1.setShouldAllowNull(false);
 field1.setIsPrimaryKey(false);
 field1.setUnique(false);
 field1.setIsIdentity(false);
 field1.setForeignKeyFieldName("CMP3_MM_MANUF.PERSON_ID");
 table.addField(field1); 
 
 FieldDefinition field2 = new FieldDefinition();
 field2.setName("DESIGNER_MAP_ID");
 field2.setTypeName("NUMERIC");
 field2.setSize(15);
 field2.setShouldAllowNull(false);
 field2.setIsPrimaryKey(false);
 field2.setUnique(false);
 field2.setIsIdentity(false);
 field2.setForeignKeyFieldName("CMP3_MM_HWDESIGNER.PERSON_ID");
 table.addField(field2); 
 return table;
 }
  • We pass through metamodel type creation without a NPE but the Class for getType() is wrong
Thread [Thread-3] (Suspended) 
 MapContainerPolicy.getKeyType() line: 360 
 MapAttributeImpl<X,K,V>.<init>(ManagedTypeImpl<X>, CollectionMapping)
line: 53 
 EntityTypeImpl<X>(ManagedTypeImpl<X>).initialize() line: 903 
 MetamodelImpl.initialize() line: 229 
 MetamodelImpl.<init>(DatabaseSession) line: 85 
 MetamodelImpl.<init>(EntityManagerFactory) line: 90 
 EntityManagerFactoryImpl.getMetamodel() line: 416 
 EntityManagerImpl.getMetamodel() line: 1991 
 MetamodelMetamodelTest.testImplementation() line: 415 
 NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not
available [native method] 
 NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39 
 DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25 
 Method.invoke(Object, Object...) line: 597 
 MetamodelMetamodelTest(TestCase).runTest() line: 168 
 MetamodelMetamodelTest(TestCase).runBare() line: 134 
 MetamodelMetamodelTest(JUnitTestCase).runBare() line: 379 
 TestResult$1.protect() line: 110 
 TestResult.runProtected(Test, Protectable) line: 128 
 TestResult.run(TestCase) line: 113 
 MetamodelMetamodelTest(TestCase).run(TestResult) line: 124 
 TestExecutor.execute(Test) line: 248 
 TestExecutor.runTest(Test) line: 671 
 SynchronizedTestExecutor.run() line: 61 
 
policy MapContainerPolicy (id=104) 
 cloneMethod null 
 constructor Constructor<T> (id=109) 
 containerClass Class<T>
(org.eclipse.persistence.indirection.IndirectMap) (id=111) 
 containerClassName 
"org.eclipse.persistence.indirection.IndirectMap" (id=112) 
 elementClass Class<T>
(org.eclipse.persistence.testing.models.jpa.metamodel.HardwareDesigner)
(id=115) 
 elementClassName 
"org.eclipse.persistence.testing.models.jpa.metamodel.HardwareDesigner"
(id=117) 
 elementDescriptor RelationalDescriptor (id=118) 
 keyField Field (id=126) 
 annotations byte[60] (id=133) 
 clazz Class<T>
(org.eclipse.persistence.testing.models.jpa.metamodel.Person) (id=135) 
 type Class<T> (java.lang.Integer) (id=143) 
 keyMethod null 
 keyName "id" (id=121)
  • getType(Class) should not be creating a type for java.lang.Class
type BasicTypeImpl<X> (id=150) 
 javaClass Class<T> (java.lang.Class) (id=82)
  • I should be doing something like the following to handle when there is no PK and not gettting the class of a class
  • Note we get a Class in all instances of policy.getKeyType() except for AggregateObjectMappings.
    protected MapAttributeImpl(ManagedTypeImpl<X> managedType, CollectionMapping mapping) {
        super(managedType, mapping);
 
        MapContainerPolicy policy = (MapContainerPolicy) mapping.getContainerPolicy();
        Object policyKeyType = policy.getKeyType(); // returns a Class<?> or descriptor (via AggregateObjectMapping)        
        Type<?> aKeyType = null;
        // Default to Object class for any variant cases that are not handled
        Class<?> javaClass = Object.class;
        if(null == policyKeyType) {
            // No policy key type = IdClass (use CMP3Policy.pkClass)
            if(managedType.isIdentifiableType()) {
                // Use the CMPPolicy on the element not the one on the managedType
                if(policy.getElementDescriptor() != null && policy.getElementDescriptor().getCMPPolicy() != null) {
                    javaClass = policy.getElementDescriptor().getCMPPolicy().getPKClass();
                } else {
                    if(null == policy.getElementDescriptor()) {
                        // check for a keyMapping on the mapping
                        if(policy.isMappedKeyMapPolicy()) {
                            MapKeyMapping mapKeyMapping = ((MappedKeyMapContainerPolicy)policy).getKeyMapping();
                            RelationalDescriptor descriptor = (RelationalDescriptor)((DatabaseMapping)mapKeyMapping).getDescriptor();
                            // If the reference descriptor is null then we are on a direct mapping
                            if(null == descriptor) {
                                throw new IllegalArgumentException("Unsupported operation on " + managedType);
                            } else {
                                if(null == descriptor.getCMPPolicy()) { // for __PK_METAMODEL_RESERVED_IN_MEM_ONLY_FIELD_NAME
                                    //throw new IllegalArgumentException("Unsupported operation on " + managedType);
                                    javaClass = Object.class;
                                } else {
                                    javaClass = descriptor.getCMPPolicy().getPKClass();        
                                }
                            }
                        }
                    } else {                        
                    }
                }
            } else {
                // Handle EmbeddableType
            }
        } else {            
            if(policyKeyType instanceof ClassDescriptor) { // from AggregateObjectMapping
                javaClass = (Class<?>)((ClassDescriptor)policyKeyType).getJavaClass();
            } else {
                javaClass = (Class<?>)policyKeyType;
            }            
        }
        aKeyType = getMetamodel().getType(javaClass);
        this.keyType = (Type<K>) aKeyType;
    }
  • which results in a BasicType<Integer>
keyType BasicTypeImpl<X> (id=88) 
 javaClass Class<T> (java.lang.Integer) (id=82)
Solution:
  • Add a Map attribute to the test model
    • a hardwareDesignersMap on Manufacturer with the mappedBy on HardwareDesigner and a join table across the two entity tables.
  • verify whether we should also use getElementClass()


DI 49: 20090723: OneToMany on MappedSuperclass with JoinTable causes Composite PK in error

  • Fixed in SVN Rev# 4845 using Option 3b (sacrifice space constraints in favor of a no-search performance improvement)
  • 20090724: see also bug# 284147
  • Here, because of the pseudo RelationalDescriptor PK name for MappedSuperclasses added a month ago, there is a small issue arising for @OneToMany mappings on a MappedSuperclass with a JoinTable where the pseudo PK is being added on top of the existing PK in the PK fields list.
  • The result of this is that some of the Metamodel processing changes PK is leaking into the real world Metamodel processing code.
Reproduction:
  • Adding the following unidirectional @OneToMany to a MappedSuperclass that defines an @Id like Person (inherited by the MappedSuperclass Corporation which is inherited by the Entity Manufacturer)
@MappedSuperclass
public abstract class Person {
    @Id
    @GeneratedValue(strategy=TABLE, generator="PERSON_MM_TABLE_GENERATOR")
    @TableGenerator(
        name="PERSON_MM_TABLE_GENERATOR", 
        table="CMP3_MM_PERSON_SEQ", 
        pkColumnName="SEQ_MM_NAME", 
        valueColumnName="SEQ_MM_COUNT",
        pkColumnValue="CUST_MM_SEQ"
    )    
    // InstanceVariableAttributeAccessor testing
    @Column(name="PERSON_ID")    
    private Integer id;
 
 
    // Verify special handling for PK for OneToMany (custom descriptor with fake PK name)
    // If a JoinTable with a JoinColumn is used - then we need a mappedBy on the inverse side here
    // However, bidirectional relationships are not allowed to MappedSuperclasses - as they have no identity
    // This @OneToMany implements internally as a @ManyToMany
    @OneToMany(fetch=EAGER, cascade=ALL)
    @JoinTable(name="CMP3_MM_HIST_EMPLOY", 
                joinColumns = @JoinColumn(name="PERSON_ID", referencedColumnName="PERSON_ID"),
                inverseJoinColumns = @JoinColumn(name="PERSON_ID", referencedColumnName="PERSON_ID"))   
    private Collection<Manufacturer> historicalEmployers = new HashSet<Manufacturer>();
  • You will see the following exception
Caused by: Exception [EclipseLink-7220] (Eclipse Persistence Services - 2.0.0.qualifier): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The @JoinColumns on the annotated element [field historicalEmployers] from the entity class [class org.eclipse.persistence.testing.models.jpa.metamodel.Person] is incomplete. When the source entity class uses a composite primary key, a @JoinColumn must be specified for each join column using the @JoinColumns. Both the name and the referencedColumnName elements must be specified in each such @JoinColumn.
	at org.eclipse.persistence.exceptions.ValidationException.incompleteJoinColumnsSpecified(ValidationException.java:1789)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.MappingAccessor.getJoinColumnsAndValidate(MappingAccessor.java:483)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.CollectionAccessor.processJoinTable(CollectionAccessor.java:578)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.OneToManyAccessor.processManyToManyMapping(OneToManyAccessor.java:153)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.OneToManyAccessor.process(OneToManyAccessor.java:108)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.RelationshipAccessor.processRelationship(RelationshipAccessor.java:459)
	at org.eclipse.persistence.internal.jpa.metadata.MetadataProject.processRelationshipAccessors(MetadataProject.java:1046)
	at org.eclipse.persistence.internal.jpa.metadata.MetadataProject.processStage3(MetadataProject.java:1325)
	at org.eclipse.persistence.internal.jpa.metadata.MetadataProcessor.processORMMetadata(MetadataProcessor.java:460)
	at org.eclipse.persistence.internal.jpa.deployment.PersistenceUnitProcessor.processORMetadata(PersistenceUnitProcessor.java:297)
	at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.predeploy(EntityManagerSetupImpl.java:849)
  • This validation exception is caused by 2 defined primary keys (one defined and the other added for MappedSuperclass processing). Because this particular MappedSuperclass already defines its' own @Id we should not add the fake proceessing Id here in this use case.
descriptor	MetadataDescriptor  (id=75)	
	m_descriptor	RelationalDescriptor  (id=74)	
		primaryKeyFields	ArrayList<E>  (id=182)	
			elementData	Object[2]  (id=195)	
				[0]	DatabaseField  (id=197)	
					name	"__PK_METAMODEL_RESERVED_IN_MEM_ONLY_FIELD_NAME" (id=199)	
					qualifiedName	"__PK_METAMODEL_RESERVED_IN_MEM_ONLY_FIELD_NAME" (id=199)	
					table	DatabaseTable  (id=200)	
					type	null	
					typeName	null	
				[1]	DatabaseField  (id=186)	
					name	"PERSON_ID" (id=202)	
					qualifiedName	"__METAMODEL_RESERVED_IN_MEM_ONLY_TABLE_NAME.PERSON_ID" (id=204)	
					table	DatabaseTable  (id=151)	
					type	null	
					typeName	null	
			modCount	2	
			size	2	
	m_primaryTable	DatabaseTable  (id=151)	
		name	"__METAMODEL_RESERVED_IN_MEM_ONLY_TABLE_NAME" (id=157)	
		qualifiedName	"__METAMODEL_RESERVED_IN_MEM_ONLY_TABLE_NAME" (id=157)	
  • The 1st (pseudo) key is added in stage 1 of metadata processing
    • Note: at the time this is a valid operation becuase the PK List is empty
Thread [main] (Suspended (breakpoint at line 465 in MetadataProject))	
	MetadataProject.addMappedSuperclassAccessor(MetadataClass, MappedSuperclassAccessor) line: 465	
	EntityAccessor.addPotentialMappedSuperclass(MetadataClass) line: 199	
	EntityAccessor.discoverMappedSuperclassesAndInheritanceParents(boolean) line: 298	
	EntityAccessor.preProcess(boolean) line: 597	
	EntityAccessor.preProcess() line: 580	
	MetadataProject.processStage1() line: 1257	
	MetadataProcessor.processORMMetadata() line: 458	
	PersistenceUnitProcessor.processORMetadata(MetadataProcessor, boolean) line: 297	
	EntityManagerSetupImpl.predeploy(PersistenceUnitInfo, Map) line: 849	
  • The 2nd (Real) key is added in stage 2 of metadata processing
Thread [main] (Suspended (breakpoint at line 458 in ClassDescriptor))	
	RelationalDescriptor(ClassDescriptor).addPrimaryKeyField(DatabaseField) line: 458	
	MetadataDescriptor.addPrimaryKeyField(DatabaseField, MappingAccessor) line: 331	
	IdAccessor.process() line: 82	
	MetadataDescriptor.processAccessors(MetadataDescriptor) line: 1242	
	MappedSuperclassAccessor(ClassAccessor).processAccessors() line: 787	
	MappedSuperclassAccessor.processMetamodelDescriptor() line: 883	
	MetadataProject.processStage2() line: 1285	
	MetadataProcessor.processORMMetadata() line: 459	
	PersistenceUnitProcessor.processORMetadata(MetadataProcessor, boolean) line: 297	
	EntityManagerSetupImpl.predeploy(PersistenceUnitInfo, Map) line: 849	
Q1) Do we need the inverseJoinColumns annotation
  • It would seem not, because bidirectional (using mappedBy) is not allowed on a mappedSuperclass
Q2) How do we define the join table columns
  • All the concrete entities must then define the @OneToMany fully with both joinColumns and inverseJoinColums
Analysis:
  • The simple solution would be - when there is an existing key, do not add another key.
  • However, the real key is added after we have customized the descriptor by adding a pseudo key to allow MappedSuperclass descriptors to process.
  • The full solution is to check the PK List for our custom MappedSuperclass pseudo key and remove it before adding the real one.
  • An even better solution would be to not add the "pseudo" key by checking that we "will" be adding a PK in the future - by possibly parsing the mappings in the stage 1 call
    • Note: The following use cases are covered by this operation.
      • UC1: The 2nd key is another pseudo key - we end up with a single one which is ok
      • UC2: The 2nd key is a real key - we delete the pseudo one only
      • UC3: The nth key is part of a composite key - we do not affect any existing composite keys already in the PK List
    • Note: The following use cases may not be covered
      • UCA: Two threads attempt to modify the PK List in an un-Synchronized state
        • This UC may not be valid
Implementation Option 1: Remove pseudo PK field from relational descriptor later in stage 2
  • Here we allow the add of the pseudo pk field to the relational descriptor for our MappedSuperclass when no other PK fields exist in phase 1.
  • Later when we enter phase 2 - we delete the pseudo pk field outside of the PK list and only add it at the end of metadata processing.
    • Issues
      • It seems wastefull to add the Field and then search for and delete it - however during stage 1 we do not know yet whether we will be processing ID accessors yet.
MetadataDescriptor.java
    public void addPrimaryKeyField(DatabaseField field, MappingAccessor accessor) {
// New code start
        /**
         *  266912: For MappedSuperclass processing, if a fake PK already exists,
         *  delete the existing transient key uses solely by non-relational metamodel processing
         *  before adding the real one.
         *  In the case that this key is part of a real composite key, 
         *  the pk name check will still allow multiple keys to be added.
         */
        // Iterate the existing fields and remove the first fake PK used for metamodel processing.
        List<DatabaseField> pkList = m_descriptor.getPrimaryKeyFields();
        // This iteration is self modifying so keep it out of an Iterator
        for(int i=0; i<pkList.size(); i++) {
            if(pkList.get(i).getName().equals(MetadataConstants.MAPPED_SUPERCLASS_RESERVED_PK_NAME)) {
                pkList.remove(i);
                // exit loop
                break;
                //i=pkList.size() + 1;
            }
        }
// New code end
        m_descriptor.addPrimaryKeyField(field);
 
        // Store the primary primary key mappings based on their field name.
        m_primaryKeyAccessors.put(field.getName(), accessor);
    }
Implementation Option 2: Cache pseudo PK field on metadata descriptor until stage 3
  • Here we cache the pseudo descriptor for our MappedSuperclass outside of the PK list and only add it at the end of metadata processing.
    • Issues
      • We will need to track when the descriptor has been fully processed and only add the PK field then
      • We are in a wierd state between stage 1 and 3 - anything that requires at least 1 PK field on the relational descriptor may misbehave.
Implementation Option 3: Determine if there will be 1 or more pk fields before adding pseudo PK field in stage 1
  • Here we before we add the pseudo descriptor for our MappedSuperclass relational descriptor - we first determine (by examining the mappings) whethere there will be one or more (composite PK) on the descriptor - where we do not ever add the pseudo PK field.
    • Issues
      • This would be the cleanest - however we would be duplicating PK processing functionality before it is required in stage 2 - just so that we can predict if there may be another PK field.
MetadataProject.addMapedSuperclassAccessor()
                metadataDescriptor.setPrimaryTable(new DatabaseTable(MetadataConstants.MAPPED_SUPERCLASS_RESERVED_TABLE_NAME));
                // Add PK field to the relationalDescriptor only if there are none yet - or "will be none"
                if(relationalDescriptor.getPrimaryKeyFields().size() == 0) {
                    // Also check that there are no PK Fields to be added later in stage 2
                    // Check accessor collection on the metadataDescriptor (note: getIdAttributeName() and getIdAttributeNames() are not populated yet - so are unavailable
                    // We will check for an IdAttribute instance directly
                    boolean idAccessorFound = false;
                    for(Iterator<MappingAccessor> anIterator = metadataDescriptor.getAccessors().iterator(); anIterator.hasNext();) {
                        MappingAccessor anAccessor = anIterator.next();
                        if(anAccessor.isId()) {
                            idAccessorFound = true;
                            break;
                        }
                    }
                    if(!idAccessorFound) {
-->old code only        relationalDescriptor.addPrimaryKeyFieldName(MetadataConstants.MAPPED_SUPERCLASS_RESERVED_PK_NAME);
                    }
                }
Decision:

I am going with Option 3 (preemptively check for an Id attribute) above until the design review is complete.

  • Fixed in SVN Rev# 4845 using Option 3b (sacrifice space constraints in favor of a no-search performance improvement)
MetadataDescriptor.addAccessor():222
    public void addAccessor(MappingAccessor accessor) {
        m_accessors.put(accessor.getAttributeName(), accessor);
        // Store IdAccessors in a separate map for use by hasIdAccessor()
new-->  if(accessor.isId()) {
new-->      m_idAccessors.put(accessor.getAttributeName(), (IdAccessor)accessor);
new-->  }
    }
MetadataProject.addMappedSuperclassAccessor():469
            metadataDescriptor.setPrimaryTable(new DatabaseTable(MetadataConstants.MAPPED_SUPERCLASS_RESERVED_TABLE_NAME));
new-->      if(!metadataDescriptor.hasIdAccessor()) {
new-->          relationalDescriptor.addPrimaryKeyFieldName(MetadataConstants.MAPPED_SUPERCLASS_RESERVED_PK_NAME);
new-->      }
            m_session.getProject().addMappedSuperclass(metadataClass, relationalDescriptor);

DI 50: 20090727: Handle all mapping types in the SingularAttribute constructor

  • The following code needs to be fully implemented so that the elementType can be set correctly.
  • Currently it is only set for Basic and OneToOne mappings, but requires support for MappedSuperclass variants of these plus the supported ManyToMany, Aggregate and OneToMany mappings.
    • elementType == managedType (not good)
Before
    protected SingularAttributeImpl(ManagedTypeImpl<X> managedType, DatabaseMapping mapping) {
        super(managedType, mapping);
        Class attributeClassification = mapping.getAttributeClassification();
 
        if (null == attributeClassification) {
            // EntityType
            // We support @OneToOne but not EIS, Reference or VariableOneToOne
            if(mapping.isOneToOneMapping()) {
                elementType = (Type<T>)getMetamodel().getType(
                        ((OneToOneMapping)mapping).getReferenceClass());
            } else {
                // TODO: default to containing class
not good--->    elementType = (Type<T>)getMetamodel().getType(managedType.getJavaType()); 
            }
        } else {
            // BasicType
            elementType = (Type<T>)getMetamodel().getType(attributeClassification);
        }
    }
After
    protected SingularAttributeImpl(ManagedTypeImpl<X> managedType, DatabaseMapping mapping, boolean validationEnabled) {
        super(managedType, mapping);
        Class attributeClass = mapping.getAttributeClassification();
 
        // The attribute classification is null for non-collection mappings
        if (null == attributeClass) { // BasicType will != null --> else clause
            // EntityType
            // We support @OneToOne but not EIS, Reference or VariableOneToOne
            if(mapping.isOneToOneMapping()) {
                attributeClass = ((OneToOneMapping)mapping).getReferenceClass();
                if(null == attributeClass && validationEnabled) {
                    AbstractSessionLog.getLog().log(SessionLog.FINEST, "metamodel_attribute_class_type_is_null", this);                    
                }
            } else if (mapping.isOneToManyMapping()) {
                // TODO: verify
                attributeClass = ((OneToManyMapping)mapping).getReferenceClass();
                if(null == attributeClass && validationEnabled) {
                    AbstractSessionLog.getLog().log(SessionLog.FINEST, "metamodel_attribute_class_type_is_null", this);                    
                }
            } else if (mapping.isManyToManyMapping()) {
                // TODO: verify
                attributeClass = ((ManyToManyMapping)mapping).getReferenceClass();
                if(null == attributeClass && validationEnabled) {
                    AbstractSessionLog.getLog().log(SessionLog.FINEST, "metamodel_attribute_class_type_is_null", this);                    
                }
            } else if (mapping.isAggregateObjectMapping()) { // IE: EmbeddedId
                attributeClass = ((AggregateMapping)mapping).getReferenceClass();
                if(null == attributeClass && validationEnabled) {
                    AbstractSessionLog.getLog().log(SessionLog.FINEST, "metamodel_attribute_class_type_is_null", this);            
                }                
            } else if (mapping.isVariableOneToOneMapping()) { // interfaces are unsupported in the JPA 2.0 spec for the Metamodel API
                if(validationEnabled) {
                    AbstractSessionLog.getLog().log(SessionLog.FINEST, "metamodel_mapping_type_is_unsupported", mapping, this);                    
                }
                // see JUnitCriteriaUnitTestSuite.testSelectPhoneNumberAreaCode() line: 246
                // VariableOneToOne mappings are unsupported - default to referenceClass  (Interface) anyway
                attributeClass = ((VariableOneToOneMapping)mapping).getReferenceClass();
                if(null == attributeClass && validationEnabled) {
                    AbstractSessionLog.getLog().log(SessionLog.FINEST, "metamodel_attribute_class_type_is_null", this);            
                }                
 
            } else if (mapping.isEISMapping()) { // unsupported in the JPA 2.0 spec for the Metamodel API
                if(validationEnabled) {
                    AbstractSessionLog.getLog().log(SessionLog.FINEST, "metamodel_mapping_type_is_unsupported", mapping, this);                    
                }
                // TODO: refactor
                // VariableOneToOne mappings are unsupported - default to Object:
                attributeClass = Object.class;
            } else if ( mapping.isReferenceMapping()) { // unsupported in the JPA 2.0 spec for the Metamodel API
                if(validationEnabled) {
                    AbstractSessionLog.getLog().log(SessionLog.FINEST, "metamodel_mapping_type_is_unsupported", mapping, this);                    
                }
                // VariableOneToOne mappings are unsupported - default to referenceClass anyway
                attributeClass = ((ReferenceMapping)mapping).getReferenceClass();
                if(null == attributeClass && validationEnabled) {
                    AbstractSessionLog.getLog().log(SessionLog.FINEST, "metamodel_attribute_class_type_is_null", this);            
                }                
            } else if (mapping.isDirectToFieldMapping()) { // Also handles the keys of an EmbeddedId
                attributeClass = mapping.getField().getType();
                if(null == attributeClass) {
                    // lookup the attribute on the containing class                    
                    Class containingClass = mapping.getDescriptor().getJavaClass();
                    Field aField = null;
                    try {
                        aField = containingClass.getDeclaredField(mapping.getAttributeName());
                        attributeClass = aField.getType();
                    } catch (NoSuchFieldException nsfe) {
                        // This exception will be warned about below
                        //nsfe.printStackTrace();
                    }                    
                }
                // all Direct mappings that don't have a type on their field
                if(null == attributeClass && validationEnabled) {
                    // TODO: refactor
                    attributeClass = Object.class;
                    AbstractSessionLog.getLog().log(SessionLog.FINEST, "metamodel_attribute_class_type_is_null", this);                    
                }
            } else {
                // All unsupported mappings
                if(null == attributeClass && validationEnabled) {
                    // TODO: refactor
                    attributeClass = Object.class;
                    AbstractSessionLog.getLog().log(SessionLog.FINEST, "metamodel_attribute_class_type_is_null", this);                    
                }
            }
        }
        elementType = (Type<T>)getMetamodel().getType(attributeClass);        
    }
  • PluralAttributeImpl also has issues with Direct Mappings where the type on the Field is null
20090731: DirectToField and DirectCollection Mapping Field Type may be null
  • The following null type is preventing us from setting the elementType on this Basic type for this specific use case.
mapping	DirectToFieldMapping  (id=192)	
	attributeAccessor	MethodAttributeAccessor  (id=197)	
	attributeClassification	null	
	attributeClassificationName	null	
	attributeName	"formerCompany" (id=198)	
	attributeObjectClassification	null	
	converter	null	
	converterClassName	null	
	descriptor	RelationalDescriptor  (id=194)	
	field	DatabaseField  (id=199)	
		columnDefinition	"" (id=205)	
		index	-1	
		isInsertable	true	
		isNullable	true	
		isUnique	false	
		isUpdatable	true	
		length	255	
		name	"FORMERCOMPANY" (id=206)	
		precision	0	
		qualifiedName	"FORMERCOMPANY" (id=206)	
		scale	0	
		sqlType	-2147483648	
		table	DatabaseTable  (id=207)	
		type	null	
		typeName	null	
		useDelimiters	false	
Solution:

SingularAttributeImpl constructor changes in progress.

DI 51: 20090728: Implement ManagedTypeImpl.getDeclaredAttributes() for non-collections

  • Currently the function public Set<Attribute<X, ?>> getDeclaredAttributes() is implemented only for collections - it requires a fully expansion.
Solution:

This solution will depend directly on DI 52 - Declared Recursion Algorithm below.

DI 52: 20090728: JPA 2: Implement recursive ManagedType.getDeclared* algorithm to differentiate by IdentifiableType

  • See bug# 284877
  • The get*Declared* functions in ManagedType will require a recursive search on their superType inheritance tree to correctly determine whether an attribute is declared on the current IdentifiableType or higher up the inheritance tree to the root.
    • We need to take into account whether the superType is an Entity or MappedSuperclass - as this will affect whether we continue to search up when a current IdentifiableType is missing the attribute.
    • For example when searching for a declared mapping somewhere in a MappedSuperclass inheritance chain - the inervening MappedSuperclasses will not have their own copy of inherited mappings - only the first Entity in the chain will inherit everything above (without overrides).
      • IE: Entity(Integer id) --> MappedSuperclass --> MappedSuperclass(Integer id).
    • - If superType is entity then inheriting entities will not have copies of the inherited mappings
    • - however, if superType is mappedSuperclass then all inheriting mappedSuperclasses and the first entity will have copies of the inherited mappings
    • - Note: a sub-entity can override a mapping above it - we need to handle this.
Use Cases:
  • UC1 Superclass declares attribute
  • UC1.1: Entity (searched) --> Entity --> Entity (declares attribute)
  • UC1.2: Entity (searched) --> Entity (copy of attribute) --> MappedSuperclass (declares attribute)
  • UC1.3: Entity (searched) --> MappedSuperclass --> Entity (declares attribute)
  • UC1.4: Entity (copy of attribute) (searched) --> MappedSuperclass (NO copy of attribute) (searched) --> MappedSuperclass (declares attribute) (searched)
  • UC1.5: Entity (copy of attribute) (searched) --> MappedSuperclass (declares attribute) (searched) --> MappedSuperclass (searched)
  • UC2 Nobody declares attribute
  • UC2.1: Entity (searched) --> Entity --> MappedSuperclass (declares attribute)
  • UC2.2: Entity (searched) --> Entity --> Entity (declares attribute)
  • UC2.3: Entity (searched) --> MappedSuperclass (searched) --> MappedSuperclass (declares attribute)
  • UC2.4: Entity (searched) --> MappedSuperclass (searched) --> Entity (declares attribute)
  • UC3 Superclass declares attribute but child overrides it
  • UC3.1: Entity (searched) --> Entity --> MappedSuperclass (declares attribute)
  • UC3.2: Entity (searched) --> Entity --> Entity (declares attribute)
  • UC3.3: Entity (searched) --> MappedSuperclass (override attribute) (searched) --> MappedSuperclass (declares attribute)
  • UC3.4: Entity (searched) --> MappedSuperclass (override attribute) (searched) --> Entity (declares attribute) (searched)
  • UC3.5: Entity (override attribute) (searched) --> MappedSuperclass (searched) --> MappedSuperclass (declares attribute) (searched)
  • UC3.6: Entity (override attribute) (searched) --> MappedSuperclass (searched) --> Entity (declares attribute)
Solution:
  • Results Expected for hasDeclaredAttribute()
    • True = attribute declared only on current type
    • False = attribute not found in superType tree or attribute found in more than one(1) level of the superType tree
  • Base Case
    • attribute found && no superType exists = true
    • attribute not found && no superType exists = false
  • Recursive Case
    • Exit(false) as soon as attribute is found in a superType - without continuing to the root
    • continue as long as we find an attribute in the superType (essentially only MappedSuperclass parents)
    private boolean hasDeclaredAttribute(String attributeName) {
        return hasDeclaredAttribute(attributeName, this.getMembers().get(attributeName));
    }
    private boolean hasDeclaredAttribute(String attributeName, Attribute firstLevelAttribute) {
 
        Attribute anAttribute = this.getMembers().get(attributeName);
        ManagedTypeImpl<?> aSuperType = getManagedSuperType();        
 
        // Base Case: If we are at the root, check for the attribute and return results immediately
        if(null == aSuperType) {
            if(null == anAttribute && null != firstLevelAttribute) { 
                return true; 
            } else {
                // UC 1.3 (part of the else condition (anAttribute != null)) is handled by the return false in null != aSuperTypeAttribute
                return false;
            }
        } else {            
           // Recursive Case: check hierarchy only if the immediate superclass is a MappedSuperclassType
           if(aSuperType.isMappedSuperclass()) { 
               Attribute aSuperTypeAttribute = aSuperType.getMembers().get(attributeName);
               // UC1.3 The immediate mappedSuperclass may have the attribute - we check it in the base case of the next recursive call 
               if(null != aSuperTypeAttribute) {
                   // return false immediately if a superType exists above the first level
                   return false;
               } else {
                   // UC1.4 The immediate mappedSuperclass may not have the attribute if another one up the chain of rmappedSuperclasses declares it
                   if(null == aSuperTypeAttribute) {
                       // UC 1.5: keep searching a possible chain of mappedSuperclasses
                       return aSuperType.hasDeclaredAttribute(attributeName, firstLevelAttribute);
                   } else {
                       // superType does not contain the attribute - check that the current attribute and the first differ
                       if(anAttribute != firstLevelAttribute) {
                           return false;
                       } else {
                           return true;
                       }
                   }
               }
           } else {
               // superType (Entity) may declare the attribute higher up - we do not need to check this
               if(null == anAttribute) {
                   return false;
               } else {
                   return true;
               }
           }
        }
    }
DI:52 Refactor: 20090817
  • See line 935 of ManagedTypeImpl.java

ManagedTypeImpl 935 patch

  • The base case for the recursive function managedTypeImpl.hasDeclaredAttribute() does not handle use case 1.4 (root-level managedType) when the caller of the function does not do it's own inheritedType check.
  • This occurs only for managedType.getDeclaredAttributes() - here the returned Set is empty.
  • There are two sets of internal references to hasDeclaredAttribute() - the call getDeclaredAttributes() does not do optimization before calling this recursive function - all the other getDeclaredList()|getDeclaredMap etc - check the immediate hierarchy and only call the recursive hasDeclaredAttribute() if needed.
  • The function is renamed hasNoDeclaredAttributeInSuperType from hasDeclaredAttribute to avoid any developer confusion - since I am really returning the opposite boolean of what my function is named - thank you Chris.
  • Why are we checking the superType tree recursively in the first place? Because a possible MappedSuperclass hierarchy will not declare their own copies of a parent superclass - only the first inheriting entity will get copies of attributes declared at the top of a MappedSuperclass hierarchy tree.
  • A possible time-space optimization would be to store the java members in a separate data structure from the metamodel members.
  • The fix is to change the following code
ManagedTypeImpl.java:897
private boolean hasDeclaredAttribute(String attributeName, Attribute firstLevelAttribute) {
        // Base Case: If we are at the root, check for the attribute and return results immediately
        if(null == aSuperType) {
            if(null == anAttribute && null != firstLevelAttribute) { 
                return true; 
            } else {
                // UC 1.3 (part of the else condition (anAttribute != null)) is handled by the return false in null != aSuperTypeAttribute
--->expand      return false;
            }
        } else {
  • To the expanded version below that handles the case where we are checking the root when the type is declared there.
ManagedTypeImpl.java:897
private boolean hasDeclaredAttribute(String attributeName, Attribute firstLevelAttribute) {
        // Base Case: If we are at the root, check for the attribute and return results immediately
        if(null == aSuperType) {
            if(null == anAttribute && null != firstLevelAttribute) { 
                return true; 
            } else {
                // UC 1.3 (part of the else condition (anAttribute != null)) is handled by the return false in null != aSuperTypeAttribute
                // UC 1.4 (when caller is firstLevel) superType does not contain the attribute - check that the current attribute and the first differ
                if(null != anAttribute && anAttribute == firstLevelAttribute) {
                    return true;
                } else {
                    return false;
                }
            }
        } else {
  • New testing that fails without this fix (no managedTypes were returned for a person.getDeclaredAttributes() call)
                /**
                 * Hierarchy:
                 *   Person : MappedSuperclass
                 *     +
                 *     +- id : Integer
                 *     +- name : String
                 *     +- historicalEmployers : Manufacturer
                 *     
                 *     Corporation : MappedSuperclass extends Person
                 *       +
                 *       +- corporateComputers : Collection 
                 *       
                 *       Manufacturer : Entity extends Corporation
                 *         +
                 *         +- computers : Set
                 *         +- hardwareDesigners : List
                 *         +- hardwareDesignersMap : Map
                 *         +- version : int
                 */
                Set<Attribute<Person, ?>> declaredAttributesSetForPerson = msPerson.getDeclaredAttributes();
                assertNotNull(declaredAttributesSetForPerson);
                // We should see 3 declared out of 3 attributes for Person 
                assertEquals(3, declaredAttributesSetForPerson.size());
                // Id is declared at this level
                assertTrue(declaredAttributesSetForPerson.contains(msPerson.getAttribute("id"))); //
                // name is declared at this level
                assertTrue(declaredAttributesSetForPerson.contains(msPerson.getAttribute("name"))); //
                // historicalEmployers is declared at this level
                assertTrue(declaredAttributesSetForPerson.contains(msPerson.getAttribute("historicalEmployers"))); //

DI 53: 20090729: Verify that inheritied non-JPA class mappings are handled by the Metamodel

  • Talking with Shaun, the following issue came up surrounding the support of declared functionality for ManagedTypes
  • There is a way to map fields that are inherited from non-Entity and non-MappedSuperclass java classes (No @Entity or @MappedSuperclass annotation) that have their fields mapped in XML.
  • These fields are not picked up by default and are warned about in Dali - however the inheriting entities will pick up the fields in the metadata.
  • We therefore need a test model and should decide on how we will handle this case.
  • This is related but is different that the issue of support for interfaces via the VariableOneToOneMapping.
Solution:
  • Support should be provided by default in the Metamodel because the inherited fields should show up in all inheriting entities.

DI 54: 20090803: Metamodel.type(Clazz) should differentiate between null and BasicType

  • The spec states that an IllegalArgumentException should be thrown in the Type is not managed (Embeddable, Entity or MappedSuperclass).
  • However the current code does not differentiate between a null type and a Basic Type
Solution:

DI 55: 20090806: Basic Type processing currently lazy loaded

  • Currently after the metamodel is initialized we still have not processed Basic types - Entity, MappedSuperclass and Embeddable types have been processed.
  • When a runtime Metamodel.getType(Clazz) call is run and the Clazz is not recognized - a Basic Type is created and added to the metamodel.
  • This behaviour violates the metamodel contract - where the entire model must be fully initialized during EMF creation - and not affected by runtime behavior. Two different users will see different numbers of types - depending on whether the Basic types are accessed (lazy load) or not.
Solution:
  • Move delayed Basic type processing back into metamodel.initialize() from metamodel.getType().

DI 56: 20090807: IdentifiableTypeImpl.getId and getVersion should handle null and Object.class for non-strict typing

  • From Gordon:
    • Relax the IAE exception handling for these functions to handle null and Object.class
  • The implementation of these 2 functions is currently still in flux and incomplete.

DI 57: 20090807: Refactor ManagedTypeImpl.create factory method to also build MappedSuperclass Types

  • Currently during metamodel.initialize() we create Entity/Embeddable types separate from MappedSuperclass types using the MappedSuperclassTypeImpl constructor
  • This is not currently an issue because we are only iterating "real" descriptors in this part of initialize() and not the "pseudo" ones on the Metadata Project.
  • The ManagedTypeImpl.create() factory method does not handle MappedSuperclasses
Solution:
  • Refactor the create() method to call the MappedSuperclassTypeImpl constructor as well.

DI 58: 20090807: ManagedType Attribute Initialization must differentiate between Collection and List

  • The collection handling for Collection, Set, List and Map is picking up the wrong type based on the runtime instantiation of the attribute.
    • The issue is we need to handle false positives for the List type because both List and Collection use IndirectList when lazy.
    • This was a known issue on ManagedTypeImpl.initialize()
      • } else if (colMapping.getContainerPolicy().isListPolicy()) { // TODO: isListPolicy() will return true for IndirectList (a lazy Collection)
  • We also need to handle lazy loaded mappings (IE: IndirectList)
  • Field access types will require special handling in the absence of a getMethod on the accessor where we use reflection directly.
Model:
  • See Computer.circuitBoards
    • Both of these should be the same
Computer
    @OneToMany(cascade=ALL, mappedBy="computer")
    // A Collection where the Collection type (Map, Set, List) is not defined at design time
    private Collection<Board> circuitBoards;
Computer
    @OneToMany(cascade=ALL, mappedBy="computer")
    private Collection<Board> circuitBoards = new HashSet<Board>()
  • Also the following List should be treated differently than a Collection - even though both show up as IndirectList on the containerPolicy
Manufacturer
    @OneToMany(cascade=ALL, mappedBy="employer")
    private List<HardwareDesigner> hardwareDesigners = new ArrayList<HardwareDesigner>();
Analysis
Q1: Why do some ManyToMany MethodAttributeAccessors have the get method unpopulated
  • This seems to be due to the fact that the class uses field as opposed to method level access.
  • This fact requires a workaround in ManagedTypeImpl that goes directly to the class
    Method aMethod = this.getJavaType().getDeclaredMethod(getMethodName);
    Class aType = aMethod.getReturnType();
  • Example with no getMethod (Embeddable)
    • class org.eclipse.persistence.testing.models.jpa.inherited.Accredidation
attributeAccessor	MethodAttributeAccessor  (id=187)	
	attributeName	"witnesses" (id=188)	
	getMethod	null	
	getMethodName	"getWitnesses" (id=193)	
	isReadOnly	false	
	isWriteOnly	false	
	setMethod	null	
	setMethodName	"setWitnesses" (id=194)	
  • Example with a getMethod
    • class org.eclipse.persistence.testing.models.jpa.xml.merge.relationships.Item
attributeAccessor	MethodAttributeAccessor  (id=109)	
	attributeName	"partsLists" (id=112)	
	getMethod	Method  (id=139)	
	getMethodName	"getPartsLists" (id=140)	
	isReadOnly	false	
	isWriteOnly	false	
	setMethod	Method  (id=143)	
	setMethodName	"setPartsLists" (id=144)	
Q2: When plural attribute type is not found - default, fail or skip=
  • In the corner cases where we cannot find the type after exercising all the options below - do we default to List, fail on metamodel processing, or skip the attribute - leave it at Object, log a warning and continue.
    • Check container policy
    • Check getMethod attribute class
    • Check getMethodName reflective call
    • Check type on declared field (in the absence of a get method)
Solution: Fixed
  • See reference in
    • MappingAccessor.setIndirectionPolicy(CollectionMapping mapping, String mapKey, boolean usesIndirection)
  • Check CollectionContainerPolicy.isCollection()
  • Get the type (either Collection or List) via the following call
    Class type = ((InstanceVariableAttributeAccessor)colMapping.getAttributeAccessor()).getAttributeField().getType();

DI 59: 20090818: PluralAttribute.elementType not set for non-lazy instantiated Collection Attribute


Solution:
                // Example: Collection with an instantiated Set
                Class attributeClass = ((CollectionMapping)mapping).getReferenceClass();
                // TODO: refactor
                if(null == attributeClass && validationEnabled) {
                    attributeClass = Object.class;
                    AbstractSessionLog.getLog().log(SessionLog.FINEST, "metamodel_attribute_class_type_is_null", this);
                } else {
                    this.elementType = (Type<V>)getMetamodel().getType(attributeClass);
                }

Annotation Processors

We require a design time annotation processing tool to create metamodel class files X_. The APT tool that has shipped since JDK 1.5 will be used to process and create our annotation factories.

APT

APT is the annotation processing tool that has been shipped since Java 5.

Design Criteria

Security
Concurrency
Performance
  • The APT tools needs to perform as fast as normal javac compilation.
Configuration
Dependencies
  • We have a compile time dependency on tools.jar for the com.sun.mirror packages.

Testing Models

Mapped Superclass Test Model

  • In the entity model below, the Person class is abstract representing the root of a @MappedSuperclass hierarchy.
  • The inheriting subclasses of Person is 2 levels wide.
  • We have instances of @OneToOne, @OneToMany, @ManyToOne and @ManyToMany mappings

Eclipselink metamodel model.gif

Missing Test Model Attributes

    • Entity-->Entity hierarchy
      • Done - 20090806 - see Processor.class superclass
      • Via DTYPE discriminator type column field
      • Here we override the default @Inheritance strategy of SINGLE_TABLE and use JOINED instead of TABLE_PER_CLASS
    • Entity-->MappedSuperclass(Abstract)-->Entity hierarchy
    • MappedSuperclass containing a 1:m @OneToMany mapping
      • Done - see Corporation.corporateComputers Collection
    • MappedSuperclass containing an Integer @Version field
      • Done - see Person.class
    • MappedSuperclass containing an @ClassId field
      • Pending
    • MappedSuperclass containing a composite primary key
      • Pending
    • MappedSuperclass containing an @EmbeddedId composite primary key
      • Done - see EmbeddedPK in Location.class
    • Collection attribute without Set/List/Map instantiation (to verify element type)
      • Done - see Collection<Board> circuitBoards in Computer

Implementation

Module Reuse

How much of the existing JPA annotation processor will be used or transitioned? We will wrap most of the JPA metadata processing - however significant changes are required in order to support MappedSuperclass descriptors - since they currently have no RelationalDescriptor because all their state information is persisted by implementing concrete entities.

Modified Modules

The following module changes are implemented as of Rev# 4587.

Foundation

  • org.eclipse.persistence.core
    • org\eclipse\persistence\sessions
      • Project.java - Holds our Map of MappedSuperclass RelationalDescriptors (metadata processing only - these do not goto the database) keyed on MetadataClass. This map is on the core project because Metadata processing is transient.

javax.persistence 2.0

  • MANIFEST.MF

From

Export-Package: javax.persistence;version="1.99.3",
 javax.persistence.spi;version="1.99.3"

To

Export-Package: javax.persistence;version="1.99.3",
 javax.persistence.criteria;version="1.99.3,
 javax.persistence.metamodel;version="1.99.3",
 javax.persistence.spi;version="1.99.3"

org.eclipse.persistence.jpa

  • org\eclipse\persistence\internal\jpa\metadata
    • accessors
    • objects
      • MetadataAnnotatedElement.java - Return Void.class for all parameterized generics in MappedSuperclass descriptors
    • classes
      • EntityAccessor.java - In addPotentialMappedSuperclass() - get MappedSuperclass descriptor stored previously on the core Project and add the MappedSuperclassAccessor based on the mapping
      • EmbeddableAccessor.java - Add workaround for ValidationException when the accessor is on a MappedSuperclass
      • ClassAccessor.java - add @Override function isMappedSuperclass()
      • MappedSuperclassAccessor.java - override processAccessType and add processMetamodelDescriptor() so we can process our custom MappedSuperclassAccessor Map from the core Project
      • accessors
    • mappings
      • MappingAccessor.java - Added support for parameterized generics in MappedSuperclass descriptors
      • ElementCollectionAccessor.java -
    • MetadataProject.java - Add extra processing step for our custom Map of MappedSuperclassAccessor objects where they are processed early as part of processStage2. In addMappedSuperclassAccessor() add to our custom map of accessors and create a new RelationalDescriptor based on this accessor to the core Project.
    • converters
      • SerializedMetadata.java - Relax the ValidationException on serialization if the accessor represents a MappedSuperclass.
    • MetadataConstants.java - Our custom RelationalDescriptor requires a fake PK and Table name to pass metadata processing - even though we don't make it to the database with this descriptor.
    • MetadataDescriptor.java - Add helper function isMappedSuperclass()
    • MetadataHelper.java - Make getClassForName() public so we can use it to test for parameterized generics in MappingAccessor.
EntityManagerImpl
package org.eclipse.persistence.internal.jpa;
public class EntityManagerImpl implements org.eclipse.persistence.jpa.JpaEntityManager {
 
 // 266912: Criteria API and Metamodel API (See Ch 5 of the JPA 2.0 Specification)
 private javax.persistence.metamodel.Metamodel metaModel; 
 
 public javax.persistence.metamodel.Metamodel getMetaModel() {
 return metaModel;
 }

New Modules

Building

Eclipse IDE Required Changes

Ant Build Required Changes

For all these manifest changes we will require the change to be done in build.xml - as the these are generated files at the moment (pre-Orbit).

JPA Project

build.xml
 <echo message=" org.eclipse.persistence.internal.jpa.criteria;version=&quot;${release.version}&quot;,${line.separator}" 
 file="META-INF/MANIFEST.MF" append="true"/>
...
 <echo message=" org.eclipse.persistence.internal.jpa.metamodel;version=&quot;${release.version}&quot;,${line.separator}" 
 file="META-INF/MANIFEST.MF" append="true"/>
...
 <echo message=" javax.persistence.criteria;version=&quot;[1.99.0,2.1.0)&quot;,${line.separator}" 
 file="META-INF/MANIFEST.MF" append="true"/>
 <echo message=" javax.persistence.metamodel;version=&quot;[1.99.0,2.1.0)&quot;,${line.separator}" 
 file="META-INF/MANIFEST.MF" append="true"/>
.MANIFEST.MF
Require-Bundle: org.eclipse.persistence.core;bundle-version="2.0.0";visibility:=reexport,
--> javax.persistence
...
Import-Package: javax.persistence;version="[1.99.0,2.1.0)",
--> javax.persistence.criteria,

Core Project

javax.persistence Project

build.xml
 <echo message=" javax.persistence.criteria;version=&quot;${jpaproto.version}&quot;${line.separator}" 
 file="${jpaproto.resource.dir}/MANIFEST.MF" append="true"/>
 <echo message=" javax.persistence.metamodel;version=&quot;${jpaproto.version}&quot;${line.separator}"
 file="${jpaproto.resource.dir}/MANIFEST.MF" append="true"/>
.MANIFEST.MF
Require-Bundle: org.eclipse.persistence.core;bundle-version="2.0.0";visibility:=reexport,
--> javax.persistence
 org.eclipse.persistence.internal.jpa.criteria;version="2.0.0", 
 org.eclipse.persistence.internal.jpa.metamodel;version="2.0.0", 
 
Import-Package: javax.persistence;version="[1.99.0,2.1.0)",
 javax.persistence.criteria;version="[1.99.0,2.1.0)",
 javax.persistence.metamodel;version="[1.99.0,2.1.0)",

Testing

Client Test Cases

 EntityManagerImpl em = (EntityManagerImpl)getEntityManager();
 Metamodel metamodel = em.getMetamodel();

Example Code

Order Entity

package org.eclipse.persistence.example.jpa.server.business;
 
import java.math.BigDecimal;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
 
@Entity 
 public class Order {
 @Id 
 Integer orderId;
 @ManyToOne 
 Customer customer;
 @OneToMany 
 Set<Item> lineitems;
 Address shippingAddress;
 BigDecimal totalCost;
}

Order_ TypesafeMetamodel

  • The following class has been generated from the Order entity above
    • Note the @Generated and @TypesafeMetamodel annotations
    • Note the static volitile elements of type (Attribute, Set, Collection or List)
package org.eclipse.persistence.example.jpa.server.business;
 
import java.math.BigDecimal;
 
import javax.annotation.Generated;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.Set;
import javax.persistence.metamodel.TypesafeMetamodel;
 
// added annotations from 5.2.1.3
@Generated(value="EclipseLink")
@TypesafeMetamodel(Order.class)
 
// from JPA 2.0 spec section 5 p.162
public class Order_ {
 public static volatile Attribute<Order, Integer> orderId;
 public static volatile Attribute<Order, Customer> customer;
 public static volatile Set<Order, Item> lineitems;
 public static volatile Attribute<Order, Address> shippingAddress;
 public static volatile Attribute<Order, BigDecimal> totalCost;
}

Test Results

Client Test Case

StackTrace
Console
_Metamodel: org.eclipse.persistence.internal.jpa.metamodel.MetamodelImpl@2a122a12
_Metamodel entities: [ManagedType[RelationalDescriptor(org.eclipse.persistence.example.jpa.server.business.Customer --> [DatabaseTable(CUSTOMER)])], ManagedType[RelationalDescriptor(org.eclipse.persistence.example.jpa.server.business.Item --> [DatabaseTable(ITEM)])], ManagedType[RelationalDescriptor(org.eclipse.persistence.example.jpa.server.business.Address --> [DatabaseTable(ADDRESS)])], ManagedType[RelationalDescriptor(org.eclipse.persistence.example.jpa.server.business.Cell --> [DatabaseTable(EL_CELL)])], ManagedType[RelationalDescriptor(org.eclipse.persistence.example.jpa.server.business.Order --> [DatabaseTable(ORDER)])]]
_Metamodel embeddables: []
_Metamodel managedTypes: [ManagedType[RelationalDescriptor(org.eclipse.persistence.example.jpa.server.business.Customer --> [DatabaseTable(CUSTOMER)])], ManagedType[RelationalDescriptor(org.eclipse.persistence.example.jpa.server.business.Item --> [DatabaseTable(ITEM)])], ManagedType[RelationalDescriptor(org.eclipse.persistence.example.jpa.server.business.Address --> [DatabaseTable(ADDRESS)])], ManagedType[RelationalDescriptor(org.eclipse.persistence.example.jpa.server.business.Cell --> [DatabaseTable(EL_CELL)])], ManagedType[RelationalDescriptor(org.eclipse.persistence.example.jpa.server.business.Order --> [DatabaseTable(ORDER)])]]
_Metamodel EntityType for [ManagedType[RelationalDescriptor(org.eclipse.persistence.example.jpa.server.business.Customer --> [DatabaseTable(CUSTOMER)])]]: 

Open Issues

This section lists the open issues that are still pending that must be decided prior to fully implementing this project's requirements.

Issue # Owner Description / Notes
I1 20090304 mobrien compile time dependency on tools.jar for com.sun.mirror packages
I2 20090305 mobrien Name collisions with entities that are already named Entity_ in the current package - see the spec 5.2.1 #1
I3 20090305 mobrien Package level splitting is incompatible with OSGI - see the spec 5.2.1 #1
DI4 20090309 mobrien Alt#1: The existing javax.persistence.QueryBuilder interface will be removed and replaced by the updated javax.persistence.criteria.QueryBuilder interface
DI5 20090316 mobrien Direct public access or indirect intermediate interface access to Metamodel implementation
DI6 20090316 mobrien MappedSuperclass does not currently have a descriptor
DI7 20090327 mobrien When to bootstrap Metamodel creation
DI8 20090327 mobrien Ordering of Metamodel collections
DI9 20090327 mobrien Custom Logger for Metamodel
DI11 20090402 mobrien Handle no duplicates in MappedSuperclass collection on Metamodel

Timeline

20090305 Meeting

  • How to handle broken annotations? skip?
  • How to handle dependent annotations that are broken
  • performance (javac/apt) should be very fast
  • dynamic regeneration on all modifications - should be automatic with -javaagent type ide hook
  • For non-typesafe string based metadata accessors - if we verify type internally we have incremental type safety
  • verify all examples in the spec work with get and not just the static metamodel
  • Is there state for the metadata entity - 5.2.1.2 states it must be initialized
  • Name collisions with entities that are already named Entity_ in the current package see 5.2.1.1
    • package level splitting is incompatible with OSGI
  • multi-level fetch joins(there are 3) (with multiple . operators) must be supported
    • The query api must support paths

20090306 Architecture Meeting

  • We will concentrate on R1 and the Metamodel exposure work
  • The metamodel will be built separately in the JPA codebase
    • The EclipseLink descriptor off the session contains all the mappings we require to build implementations of the new Metamodel API interfaces
      • The MappedSuperclass interface is currently empty in the spec - so it will require no mapping work
      • The mapping of join metadata may be an issue
  • We are not reusing the code from the existing runtime in memory metadata package code in JPA
    • The following issues may have occurred
      • Lazy loading will have a partial metamodel load
      • The entityManager may not be available yet

20090310 Dev Meeting

  • Received initial implementation from Doug
  • slightly modified it and it runs fine with a standard entity model (no embeddables or mappedSuperclass)
  • See updated UML class diagram from 20090311

20090318 Dev Meeting

  • With Guy, Peter
  • How are we going to architect support for mapped superclass?
    • The spec example 5.7.1 only gets entities and embeddables from the Metamodel object - not MappedSuperclass objects - therefore our implementation is open
    • We will provide the following metadata model that is an extension of the current core API
      • Descriptor --> * List<MappedSuperclassMetadata<List<MappedSuperclassType>> --> * List<DatabaseMapping>
      • The type of mappings will include the minimum annotation data that we require in order to complete the model
        • For bidirectional many mappings we need attributes such as the referenceType in the absence of a generic class type reference.

20090406 Review by Guy

  • Presently I clone the mapping for each MappedSuperclass - this clone needs to have some fields not set as they are not relevant for our RelationalDescriptor (that does not actually have a database table associated with it)
  • use reload to get clones for free by using the XML metadata processing that has already happened.
  • Otherwise we will need custom clone() code for each mapping - this is potentially problemmatic because a future mapping may miss its' clone implementation.

20090406 Review by Gordon

  • Make all Entity*Impl fields protected not private - done
  • We need to resolve why MappedSuperclassTypeImpl does not inherit from ManagedTypeImpl but does implement the ManagedType interface
  • Instead of storing just mappedSuperClasses in our Map - store all ManagedTypes to aide lookup later
  • Get the spec interfaces from the pdf and don't wait for an update of the circulating spec jar
  • Change the key in our Map so that duplicates are handled for us - however a Map.put() will overwrite the existing value in our case because our key is the same object but the value is different in pass 2 (we end up clearing our custom RelationalDescriptor)
  • Remove core build changes that reference JPA 2.0 - done - there should be none but we presently require 360 references to JPA 1.0 in core - one of these I added in the past for a JBoss workaround
  • Move metamodel api interfaces to the main jpa 1.99 jar - I was keeping them separate until the spec finalized - in progress
    • Added 2 new exports for (metamodel and criteria) to the manifest - correction, it is generated - we do this in
      • javax.persistence.metamodel;version="1.99.3,
      • javax.persistence.criteria;version="1.99.3,
      • Fixed access restriction "Access rules" for the new new packages in the JPA project
    • Version 2/1 should not be an issue but do we bump the version from 1.99.3 to 1.99.4?
    • We are targetting the 2.0 version that is off of trunk/jpa/plugins not trunk/plugins
    • Add new JPA 2.0 classes to "package-eclipselink-jar" target in trunk/build.xml

20090508 Specification Questions for Gordon

  • 1) "Declared" keyword - as in getDeclaredMap() - how is this a subset of getMap()?
    • "Declared" means only in scope of this particular Entity - not via inheritance or mapped superclasses
  • 2) getVersion and getId do not handle primitives
    • Java SE autoboxing will handle this for us
  • 3) IdentifiableType - I have no implementation yet like I do for BasicImpl, Map|List|Set|CollectionImpl, EntityTypeImpl and EmbeddableTypeImpl
    • Just implement the interface in managed types
  • 4) em|emf.close - do we clear the metamodel?
    • No, keep the instance around
    • Q) How will we know to invalidate the current metamodel if a new persistence unit is loaded?
      • It will be left to the user handle this use case
      • For this variant I will possibly add a clear() function outside of the spec. The user will need to call this and then regenerate the new metamodel via getMetamodel() call.
  • 5) Type checking exceptions during metamodel creation or in runtime calls? IE: getList("homeAddress") actually returns a single Entity instead of a List.
    • Leave checking to runtime and throw exception at that time

20090513 Mapped Superclass Discussion with Gordon/Peter

  • 1) I have switched from using a HashMap<Class, RelationalDescriptor> mappedSuperclasses on the Project to a HashSet<RelationalDescriptor> where the Class key is stored as existing javaClass and javaClassName fields on Project. However while overriding equals() and hashCode() on ClassDescriptor I see different hashCodes for Class objects with the same name?
    • Turns out that I am seeing the Class loaded from two different classLoaders
    • Continue to use javaClassName and not javaClass in the equals() method, but change your code as follows
      • When adding a RelationalDescriptor to the Set, do not store the javaClass - wait until later and call convertClassNamesToClasses() to generate the javaClass at that time - to avoid adding two different Class objects that represent the same metamodel class.
  • 2) When we call get*() do we return separate exceptions for the cases where either an attribute name was not found or it is of the wrong type? Currently the spec. defines a single IAE when both are wrong.
    • Go with the spec for now, but possibly add extra detail and/or a ISE or CCE exception for either runtime exception.

20090525 Implement/Synchronize with lastest Specification API changes in rev 4265

  • Gordon and I started to discuss this late 20090526
  • deleted
    • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/AbstractCollection.java 4265 deleted
      • (replaced by PluralAttribute)
    • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/Member.java 4265 deleted
      • (replaced by Attribute)
  • renamed
    • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/Basic.java 4265 deleted
    • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/BasicType.java 4265 (+34) new
      • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/Collection.java 4265 deleted
    • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/CollectionAttribute.java 4265 (+36) new
    • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/Embeddable.java 4265 deleted
    • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/EmbeddableType.java 4265 (+33) new
      • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/Entity.java 4265 deleted
    • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/EntityType.java 4265 (+41) new
      • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/List.java 4265 deleted
    • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/ListAttribute.java 4265 (+36) new
      • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/Map.java 4265 deleted
    • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/MapAttribute.java 4265 (+50) new
      • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/MappedSuperclass.java 4265 deleted
    • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/MappedSuperclassType.java 4265 (+34) new
      • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/Set.java 4265 deleted
    • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/SetAttribute.java 4265 (+36) new
      • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/TypesafeMetamodel.java 4265 deleted
    • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/StaticMetamodel.java 4265 (+44) new
  • new
    • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/PluralAttribute.java 4265 (+56) new
      • (replaces AbstractCollection)
    • /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/SingularAttribute.java 4265 (+63) new
  • changes
    • Attribute.java
      • Essentially a renamed Member.java with addition of the PersistentAttributeType
      • isId, isVersion, isOptional moved to SingularAttribute
      • added getName, getDeclaringType, getJavaType, getJavaMember, isAssociation, isCollection
      • Attribute.getMultiplicity replaced by getPersistentAttributeType
        • AbstractCollection.Multiplicity moved to Attribute.Multiplicity
        • added MANY_TO_MANY, ONE_TO_MANY, MANY_TO_ONE ELEMENT_COLLECTION mappings
        • Attribute no longer extends Member (deleted) or Bindable
    • New SingularAttribute replaces much of previous Attribute with addition of getType()
    • Bindable.BindableType
      • ATTRIBUTE, COLLECTION, MANAGED_TYPE
      • changed to
      • SINGULAR_ATTRIBUTE, PLURAL_ATTRIBUTE, ENTITY_TYPE
    • IdentifiableType adds hasVersionAttribute, getIdClassAttributes
    • ManagedType no longer extends Bindable - it only continues to extend Type now
      • Added getAttributes, getDeclaredAttributes, the rest are renamed get*Singular* for non-plural methods
    • EntityType now extends Bindable directly as well as IdentifiableType
    • In BasicAccessor.process() the function

Implementation Changes

  • New SingularAttributeImpl takes over part of previous Attribute
  • Attribute becomes abstract
  • Implementation of Member moves to Attribute

Snapshot Checkin for Rev 4350

20090528:1700

This checkin for rev 4353 matches the Metamodel interface model diagram above and is 50% complete.

  • What is working
    • Metamodel interface
    • MappedSuperclassType support in BasicAccessor (for DirectMapping) for @MappedSuperclass annotation (not XML yet)
    • Part of EntityType
  • What is not working
    • Any function that still contains a stubbed TODO and returns null, 0 or false
    • MappedSuperclassType support for all non-Basic types (non-DirectMapping)
      • XML support for MappedSuperclass
    • Fully tested hashcode/equals implementation to disallow duplicates of RelationalDescriptor for MappedSuperclassType support - it needs a more detailed equals()
    • EmbeddableType
    • BasicType
    • XML support for Metamodel annotations for use by WorkBench
    • Completed test suite and Model

The Test configuration patch off of 4350 has been checked in under rev# 4357

20090529: Review Guy

  • 1 - I'd like to see the code you added to EntityAccessor moved to the MetadataProject. That is, instead of seeing getProject().getProject() ... call getProject().addMappedSuperclass(MetadataClass). In their you'll deal with the TopLink project and add the mappedsuperclass descriptor.
  • 2 - classAccessor instanceof check should be changed to classAccessor.isMappedSuperclass()
  • You should have a getMappedSuperclass method on MetadataProject and call it. In there you deal with the actual EclipseLink Project again like comment #1 above.
  • See rev 4415 and 4416

20090609: Review by Guy & Gordon

  • The following changes were done to the way we process mappings on MappedSuperclasses. Instead of creating our own pseudo RelationalDescriptor, we force processing of accessors which creates RelationalDescriptors for all our accessors for us. We no longer need to modify the process() function on every accessor type.
    • The model has been extended with 1:1 and M:1 unidirectional mappings out of a MappedSuperclass - Person.
    • We do not support relationship mappings into mappedSuperclasses - as they have no identity.

20090612: Review by Guy & Gordon

  • See double table exceptions with our new forced accessor processing
    • The exceptions we see where there are 2 tables is because we are adding to the set more than once

(the standard hashCode/equals is missing or we should use a HashMap keyed on className) - using className key for identity

    • Retest on regression and ORM tests
    • Moving on to test Embeddables

Current Results on MappedSuperclasses

_Metamodel mappedSuperclassType:MappedSuperclassTypeImpl@18227730 [descriptor: RelationalDescriptor(org.eclipse.persistence.testing.models.jpa.metamodel.Processor --> [DatabaseTable(__RESERVED_IN_MEM_ONLY_TABLE_NAME)]), mappings: []]
_Metamodel mappedSuperclassType:MappedSuperclassTypeImpl@27916412 [descriptor: RelationalDescriptor(org.eclipse.persistence.testing.models.jpa.metamodel.Corporation --> [DatabaseTable(__RESERVED_IN_MEM_ONLY_TABLE_NAME)]), mappings: []]
_Metamodel mappedSuperclassType:MappedSuperclassTypeImpl@11832081 [descriptor: RelationalDescriptor(org.eclipse.persistence.testing.models.jpa.metamodel.Designer --> [DatabaseTable(__RESERVED_IN_MEM_ONLY_TABLE_NAME)]), mappings: [org.eclipse.persistence.mappings.OneToOneMapping[secondaryEmployer], org.eclipse.persistence.mappings.OneToOneMapping[primaryEmployer]]]
__Mapping: org.eclipse.persistence.mappings.OneToOneMapping[secondaryEmployer]
__Mapping: org.eclipse.persistence.mappings.OneToOneMapping[primaryEmployer]
_Metamodel mappedSuperclassType:MappedSuperclassTypeImpl@1497769 [descriptor: RelationalDescriptor(org.eclipse.persistence.testing.models.jpa.metamodel.Person --> [DatabaseTable(__RESERVED_IN_MEM_ONLY_TABLE_NAME)]), mappings: [org.eclipse.persistence.mappings.DirectToFieldMapping[id-->PERSON_ID], org.eclipse.persistence.mappings.DirectToFieldMapping[name-->NAME]]]
__Mapping: org.eclipse.persistence.mappings.DirectToFieldMapping[id-->PERSON_ID]
__Mapping: org.eclipse.persistence.mappings.DirectToFieldMapping[name-->NAME]

20090617: RelationalDescriptor Review by James

  • See the following rev done in parallel with the accessor/metadataProject refactor that is in progress

4519

  • 266912: Use a Map keyed on MetadataClass instead of overriding equals/hashCode

for MappedSuperclass RelationalDescriptors

  • Suggested by and discussed with James
    • 4519 diff
    • - we may merge Project.mappedSuperclassDescriptors with Project.descriptors in the future to gain query and SessionCustomizer functionality on mappedSuperclasses
    • - The key is Object instead of MetadataClass so that we do not intruduce a Core-->JPA dependency
    • - The value may be ClassDescriptor in the future, for now we use a concrete RelationalDescriptor that matches what the accessors use
    • - Use of a HashMap will enforce identity based on the MetadataClass but we still require a contains(key) check because we do not want a put(key,value) to overwrite a descriptor that already contains mappings as we do 3 passes during metadata processing
    • - Our custom metamodel processing for mappedSuperclasses in the next transaction should be done in the normal initialize() and not treated specially
    • This raises the issue of whether MappedSuperclasses should be treated as real descriptors (minus actually going to the database)

20090618: Synchronize with 20090617 Criteria API specification changes

  • See rev 4525
  • Relevant changes
    • All "Declared" comments are now "Present" - function definition retain "Declared"
    • ManagedType
      • Added Attribute<? super X, ?> getAttribute(String name);
      • Added Attribute<X, ?> getDeclaredAttribute(String name);
    • MappedSuperclassTypeImpl
      • Removed public <Y> Attribute<X, Y> getDeclaredAttribute(String name, Class<Y> type)
    • EntityTypeImpl
      • Removed public boolean hasIdAttribute()
      • Removed public Attribute<X, ?> getDeclaredAttribute(String name)
    • EmbeddableTypeImpl
      • Removed public Attribute<X, ?> getDeclaredAttribute(String name)

20090624: Code Review with Guy, Gordon

  • 20090624:1400
  • This review is for design issues 27-30
  • Code Review prep notes for
  • Prep Before:
    • 1) move constants from MetadataConstants (JPA) to AbstractSessionLog (Core)
    • 2) Javadoc on superclass ClassAccessor.hasMappedSuperclasses()
    • 3) remove/merge three if statements in EmbeddableAccessor:170
    • 4) EntityAccessor:1417
      • verify no validation for
        • if(!getDescriptor().isPkClassDefinedOnMappedSuperclass()) {
      • Javadoc on hasMappedSuperclasses()
    • 5) MappedSuperclassAccessor
      • remove getIdClass() as it is no longer used by MetadataProject.addMappedSuperclassAccessor
    • 6) DirectCollectionAccessor
      • 151: verify workaround for no referenceClass in mappedSuperclass descriptor
      • 311: verify we do not need to processMappingValueConverter() for mappedSuperclass descriptors
        • because currently I do not process in 311 but I do in 344
      • Note: either one must be modified
    • 7) ElementCollectionAccessor
      • 415: verify we are good to override a generic Type definition (IE: T) to Void in getReferenceClass()
    • 8) MappingAccessor
      • 456: verify we can skip compositePrimaryKey JoinColumn Metadata processing when
        • if(!descriptor.hasDescriptorReservedForMetamodelAsParent()) {
      • 519: verify Void parameterized generics override
      • 568: verify getReferenceName override for MappedSuperclass where we return "" empty string - need to refactor this
      • 1271: verify early return with null - avoiding a NPE when getting the name for a mapkey
    • 9) MetadataAnnotatedElement
      • 240: verify Void parameterized generics override
    • 10) InheritanceMetadata
      • 81: no change to accessor.processInheritancePrimaryKeyJoinColumns();
    • 11) MetadataDescriptor
      • 285: TODO: workaround for empty table names may need to be higher than in addForeignKeyFieldForMultipleTable
      • 872: verify early return with empty DatabaseTable is ok for getPrimaryTable() - same as for aggregateDescriptor
        • if(hasDescriptorReservedForMetamodelAsParent()) {
          • return new DatabaseTable();
    • 12) MetadataProject
      • 972: verify secondary check of our custom map of MappedSuperclass descriptors for entities
        • TODO: this is where we may have a problem that causes most of the issue with multiple PK's above
      1. By picking up our MS descriptors here - we end up causing them to be processed by initialize() during login() which we do not want
  • Performance:
    • 1) Verify that any of the if() checks do not affect performance - as in they are in a tight loop
  • During review:

Decisions

This section lists decisions made. These are intended to document the resolution of open issues or constraints added to the project that are important.

Issue # Description / Notes Decision

Appendix A : Specification Details

Version 20090307 and 20090317 PFD Differences

  • Entity interface
    • removed Class<?> getIdJavaType()
  • IdentifiableType interface (super interface of Entity)
    • added boolean hasIdAttribute()
    • added Type<?> getIdType()
  • ManagedType interface (super interface of IdentifiableType)
    • added Attribute<? super X, ?> getDeclaredAttribute(String name)
    • added Attribute<? super X, ?> getCollection(String name)
    • added Attribute<? super X, ?> getSet(String name)
    • added Attribute<? super X, ?> getList(String name)
    • added Attribute<? super X, ?> getMap(String name)
    • added Attribute<? super X, ?> getDeclaredCollection(String name)
    • added Attribute<? super X, ?> getDeclaredSet(String name)
    • added Attribute<? super X, ?> getDeclaredList(String name)
    • added Attribute<? super X, ?> getDeclaredMap(String name)

Appendix B : Deprecated or Resolved Design Notes

Documentation

Wiki and formal documentation links here.

Future Considerations

During the research for this project the following items were identified as out of scope but are captured here as potential future enhancements. If agreed upon during the review process these should be logged in the bug system.

Back to the top