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.
EclipseLink/Development/JPA 2.0/metamodel api
Contents
- 1 JPA 2.0: MetaModel API
- 1.1 Status
- 1.2 Code Submission Summary
- 1.3 Summary
- 1.3.1 EclipseLink API code Changes
- 1.3.1.1 Project.java - Native core
- 1.3.1.2 ExceptionLocalizationResource.java - Native core
- 1.3.1.3 MetadataProject.java - JPA core
- 1.3.1.4 MetadataConstants.java - JPA core
- 1.3.1.5 MetadataDescriptor.java - JPA core
- 1.3.1.6 MetadataHelper.java - JPA core
- 1.3.1.7 EmbeddableAccessor.java - JPA core
- 1.3.1.8 EntityAccessor.java - JPA core
- 1.3.1.9 MappedSuperclassAccessor.java - JPA core
- 1.3.1.10 ElementCollectionAccessor.java - JPA core
- 1.3.1.11 MappingAccessor.java - JPA core
- 1.3.1.12 MetadataAnnotatedElement.java - JPA core
- 1.3.1.13 SerializedMetadata.java - JPA core
- 1.3.1.14 EntityManagerImpl.java - JPA core
- 1.3.1.15 AttributeImpl.java - New
- 1.3.1.16 BasicTypeImpl.java - New
- 1.3.1.17 CollectionAttributeImpl.java - New
- 1.3.1.18 EmbeddableTypeImpl.java - New
- 1.3.1.19 EntityTypeImpl.java - New
- 1.3.1.20 IdentifiableTypeImpl.java - New
- 1.3.1.21 ListAttributeImpl.java - New
- 1.3.1.22 ManagedTypeImpl.java - New
- 1.3.1.23 MapAttributeImpl.java - New
- 1.3.1.24 MappedSuperclassTypeImpl.java - New
- 1.3.1.25 MetamodelImpl.java - New
- 1.3.1.26 PluralAttributeImpl.java - New
- 1.3.1.27 SetAttributeImpl.java - New
- 1.3.1.28 SingularAttributeImpl.java - New
- 1.3.1.29 TypeImpl.java - New
- 1.3.1 EclipseLink API code Changes
- 1.4 Analysis
- 1.5 Functional Requirements
- 1.6 Use Cases
- 1.7 Design
- 1.7.1 API
- 1.7.2 Design Issues
- 1.7.2.1 DI 4: QueryBuilder Interface Implementation
- 1.7.2.2 DI 5: Metamodel package is public or internal API
- 1.7.2.3 DI 6: MappedSuperclass does not currently have a Descriptor
- 1.7.2.4 DI 7: When to bootstrap Metamodel creation
- 1.7.2.5 DI 8: Ordering of Metamodel collections
- 1.7.2.6 DI 9: Custom Logger for Metamodel
- 1.7.2.7 DI 10: Embeddable Support
- 1.7.2.8 DI 11: Handle No Duplicates in MappedSuperclass Collection on Metamodel
- 1.7.2.9 DI 12: 20090506: Do we need an implementation of IdentifiableType
- 1.7.2.10 DI 16: 20090508: Implement IdentifiableType?
- 1.7.2.11 DI 13: 20090506: EntityManager/EntityManagerFactory.clear() resets MetaModel?
- 1.7.2.12 DI14: 20090507: EntityTypeImpl support for primitive Id and Version
- 1.7.2.13 DI 15: 20090508: What is the scope of "Declared"?
- 1.7.2.14 DI 17: 20090508: Clear Metamodel on EMF.close()?
- 1.7.2.15 DI 18: 20090508: Type Checking at metamodel creation or runtime?
- 1.7.2.16 DI 24: 20090609: Move accessor processing out of BasicAccessor into MetadataProject
- 1.7.2.17 DI 25: 20090616: Inherited parameterized generics for Element Collections (Basic)
- 1.7.2.18 DI 26: 20090616: TableGenerator on Id column Accessor is null
- 1.7.2.19 DI 27: 20090616: Embeddable access type conflict exception
- 1.7.2.20 DI 28: 20090622: Composite PK support for MappedSuperclasses
- 1.7.2.21 DI 29: 20090622: Multiple Table PK support for MappedSuperclasses
- 1.7.2.22 DI 30: 20090623: BasicMap Parameterized Generic return type cannot be the Void class
- 1.7.2.23 DI 31: 20090703: EntityTypeImpl inherits from ManagedType but MappedSuperclassTypeImpl does not
- 1.7.2.24 DI 32: 20090703: Metamodel superType requires javaClass set on custom descriptor on MappedSuperclassAccessor
- 1.7.2.25 DI 33: 20090707: Application Server Container - Verify that we get the correct classLoader from the right session
- 1.7.2.26 DI 34: 20090707: Temporary CNFE when loading Metamodel Impl class
- 1.7.2.27 DI 35: 20090707: Where to initialize IdentifiableTypeImpl.supertype
- 1.7.2.28 DI 36: 20090708: The StaticMetamodel requires an implementation class
- 1.7.2.29 DI 37: 20090708: CollectionAttribute acts as a peer of Map, Set, List but should be a super interface
- 1.7.2.30 DI 38: 20090708: OneToOne Set defaults to IndirectList
- 1.7.2.31 DI 39: 20090708: Handle MappedSuperclass in ManagedTypeImpl.create()
- 1.7.2.32 DI 40: 20090708: Type hierarchy numbers are missing MappedSuperclass types
- 1.7.2.33 DI 41: When to throw IAE for missing member or wrong type on get() call
- 1.7.2.34 DI 42: 20090709: IdentifiableType.supertype - what do top-level types set it to
- 1.7.2.35 DI 43: 20090710: Implement getDeclaredX() methods
- 1.7.2.36 DI 44: 20090710: Criteria API FromImpl equality check requires enum instead of BasicTypeImpl
- 1.7.2.37 DI 45: 20090713: getAttributes() returns a new HashSet - not the actual Metamodel.members.values Collection
- 1.7.2.38 DI 46: 20090715: Metamodel initialization on EntityManagerFactory creation
- 1.7.2.39 DI 47: 20090715: Implement IdentifiableType.getIdType() for composite keys
- 1.7.2.40 DI 48: 20090721: Implement Map support
- 1.7.2.41 DI 49: 20090723: OneToMany on MappedSuperclass with JoinTable causes Composite PK in error
- 1.7.2.41.1 Reproduction:
- 1.7.2.41.2 Q1) Do we need the inverseJoinColumns annotation
- 1.7.2.41.3 Q2) How do we define the join table columns
- 1.7.2.41.4 Analysis:
- 1.7.2.41.5 Implementation Option 1: Remove pseudo PK field from relational descriptor later in stage 2
- 1.7.2.41.6 Implementation Option 2: Cache pseudo PK field on metadata descriptor until stage 3
- 1.7.2.41.7 Implementation Option 3: Determine if there will be 1 or more pk fields before adding pseudo PK field in stage 1
- 1.7.2.41.8 Decision:
- 1.7.2.42 DI 50: 20090727: Handle all mapping types in the SingularAttribute constructor
- 1.7.2.43 DI 51: 20090728: Implement ManagedTypeImpl.getDeclaredAttributes() for non-collections
- 1.7.2.44 DI 52: 20090728: JPA 2: Implement recursive ManagedType.getDeclared* algorithm to differentiate by IdentifiableType
- 1.7.2.45 DI 53: 20090729: Verify that inheritied non-JPA class mappings are handled by the Metamodel
- 1.7.2.46 DI 54: 20090803: Metamodel.type(Clazz) should differentiate between null and BasicType
- 1.7.2.47 DI 55: 20090806: Basic Type processing currently lazy loaded
- 1.7.2.48 DI 56: 20090807: IdentifiableTypeImpl.getId and getVersion should handle null and Object.class for non-strict typing
- 1.7.2.49 DI 57: 20090807: Refactor ManagedTypeImpl.create factory method to also build MappedSuperclass Types
- 1.7.2.50 DI 58: 20090807: ManagedType Attribute Initialization must differentiate between Collection and List
- 1.7.2.51 DI 59: 20090818: PluralAttribute.elementType not set for non-lazy instantiated Collection Attribute
- 1.7.2.52 DI 60: 20090820: Refactor SingularAttribute and PluralAttribute constructor elementType discovery up into Attribute
- 1.7.2.53 DI 61: 20090820: ManagedType.getDeclaredX() leaks members into entity-entity hierarchy
- 1.7.2.54 DI 62: 20090820: Add support for Embeddable Collections as part of AGGREGATE_COLLECTION support
- 1.7.2.55 DI 63: 20090824: Add Map support for @MapKey to MapAttribute
- 1.7.2.55.1 Use Cases:
- 1.7.2.55.1.1 UC 1a: Generics KV set, no @MapKey present, PK is singular field
- 1.7.2.55.1.2 UC 1b: Generics KV set, no @MapKey present, PK is Multiple fields
- 1.7.2.55.1.3 UC 1c: Generics KV set, no @MapKey present, PK is EmbeddedId
- 1.7.2.55.1.4 UC 1d: Generics KV set, no @MapKey present, PK is ClassId
- 1.7.2.55.1.5 UC 2: Generics KV set, @MapKey is present
- 1.7.2.55.1.6 UC 3a: No Generics KV set, no @MapKey present, PK is singular field
- 1.7.2.55.1.7 UC 3b: No Generics KV set, no @MapKey present, PK is Multiple fields
- 1.7.2.55.1.8 UC 3c: No Generics KV set, no @MapKey present, PK is EmbeddedId
- 1.7.2.55.1.9 UC 3d: No Generics KV set, no @MapKey present, PK is ClassId
- 1.7.2.55.1.10 UC 4: No Generics KV set, @MapKey is present
- 1.7.2.55.1.11 UC 5: Run UC 1-4 on a MappedSuperclass
- 1.7.2.55.1.12 UC 6: No Generics KV set, no targetEntity set, @MapKey is *(set/unset)
- 1.7.2.55.1.13 UC 7: Generics KV set, targetEntity is also set, @MapKey is *(set/unset)
- 1.7.2.55.1.14 UC 8: Generics KV set, Map is instantated to HashMap
- 1.7.2.55.2 Test Model:
- 1.7.2.55.3 Analysis:
- 1.7.2.55.4 Solution: Fixed
- 1.7.2.55.1 Use Cases:
- 1.7.2.56 DI 64: 20090825: ManagedTypeImpl.isAttributeDeclaredOnlyInLeafType() fails to find attributes declared root of mappedSuperclass<--entity<--entity hierarchy
- 1.7.2.57 DI 65: 20090827: Handle DirectCollection elementType retrieval in the absence of a generic type
- 1.7.2.58 DI 66:20090827: EnumSet support - expected/no-fix
- 1.7.2.59 DI 67:20090827: AggregateCollectionMapping support in PluralAttribute
- 1.7.2.60 DI 68: 20090828: Double (auto-boxed) or direct double Attribute.javaType
- 1.7.2.61 DI 69: 20090831: Object.class as a default javaType is not compatible with JPA 1.0
- 1.7.2.62 DI 70: 20090901: Implement JPA 2.0 specification changes in 090828 draft
- 1.7.2.63 DI 71: 20090909: Implement IdentifiableType.getId()
- 1.7.2.64 DI 72: 20090909: Implement IdentifiableType.getDeclaredId()
- 1.7.2.65 DI 73: 20090909: Implement IdentifiableType.getIdClassAttributes()
- 1.7.2.66 DI 74: 20090909: Implement IdentifiableType.hasSingleIdAttribute()
- 1.7.2.67 DI 75: 20090909: Implement IdentifiableType.getVersion()
- 1.7.2.68 DI 76: 20090909: Implement IdentifiableType.getDeclaredVersion()
- 1.7.2.69 DI 77: 20090909: Implement IdentifiableType.hasVersionAttribute()
- 1.7.2.70 DI 78: 20090909: Composite @IdClass on inherited MappedSuperclass chain causes new ValidationException
- 1.7.2.71 DI 79: 20090910: MapAttribute keyType requires @MapKey handler in the template case when CMP3Policy.getPKClass() is null
- 1.7.2.72 DI 80: 20090914: MappedSuperclassTypeImpl.create() does not use ConversionManager to get the EE classLoader
- 1.7.2.73 DI 81: 20090914: Implement @BasicMap DirectMapContainerPolicy support in MapAttributeImpl
- 1.7.2.74 DI 82: 20090914: Verify all is*Policy() calls for MapAttributeImpl instances
- 1.7.2.75 DI 83: 20090914: MapAttributeImpl.elementType incorrectly set when @ObjectTypeConverter is present
- 1.7.2.76 DI 84: 200909015: MapAttribute missing support for UC8: @MapKey with default name attribute
- 1.7.2.77 DI 85: 20090916: Relax IllegalArgumentException checking on autoboxed primitives
- 1.7.2.78 DI 86: 20090921: Handle Embeddable Type keyType in MapAttributeImpl constructor
- 1.7.2.79 DI 87: 20090925: @JoinTable on JPA models is redundant if FK JoinColumn exists
- 1.7.2.80 DI 88: 20091006: Improve Performance via Caching and Minimal Iteration
- 1.7.2.81 DI 89: 20091007: Metamodel Implementation must implement Serializable - required by Criteria API
- 1.7.2.82 DI 90: 20091007: Validate and Remove all remaining 12 TODO comments
- 1.7.2.83 DI 91: 20091008: ManagedType.getDeclaredAttribute() does not throw expected IAE for Entity(target)-MappedSuperclass-MappedSuperclass(attribute) Hierarchy
- 1.7.2.84 DI 92: 20091008: Move metamodel instance field from EntityManagerFactory to EntityManagerSetupImpl
- 1.7.2.85 DI 93: 20091014: IdentifiableTypeImpl.getIdType() assumes all Id mappings are DerivedId mappings
- 1.7.2.86 DI 94: 20091015: Split and Granularize Test Suite
- 1.7.2.87 DI 95: 20091017: Attribute.getJavaMember() returns null for a BasicType on a MappedSuperclass because of an uninitialized accessor
- 1.7.2.88 DI 96: 20091019: Attribute.getPersistentAttributeType() treats ManyToOne the same as OneToOne
- 1.7.2.89 DI 97: 20091102: Add JPA 2.0 @MapKeyClass use cases to test model
- 1.7.2.90 DI 98: 20091109: MapAttribute keyType processing should offload more to MappedKeyContainerPolicy.keyMapping
- 1.7.2.91 DI 99: 20091110: Metamodel.types LinkedHashMap appears to have null=null K,V pairs - expected Java SE behavior
- 1.7.2.92 DI 100: 20100120: EmbeddedId on MappedSuperclass fails metadata validation on reserved temp metamodel PK
- 1.7.2.92.1 DI 100: UML Model Extension
- 1.7.2.92.2 Analysis 100:
- 1.7.2.92.3 Option 100-1: Ignore fake MappedSuperclass IDs
- 1.7.2.92.4 Option 100-2: Do not add fake MappedSuperclass IDs when IdClass or EmbeddedId will exist
- 1.7.2.92.5 Option 100-3: Remove __PK_METAMODEL_RESERVED_IN_MEM_ONLY_FIELD_NAME temp PK when IdClass or EmbeddedId exists
- 1.7.2.92.6 Solution 100:
- 1.7.2.93 DI 101: 20100218: Descriptor.javaClass is null on a container EM for a specific case
- 1.7.2.94 DI 102: 20100421: Fully initialize MappedSuperclass Descriptors and Refactor existing workarounds
- 1.7.2.95 DI 103: 20100601: 315287: Handle BasicType as inheritance root for ManagedTypes
- 1.7.2.96 DI 104: 20100614: 314906: PluralAttribute.getJavaType returns elementType instead of Collection Type
- 1.7.2.97 DI 105: 20100616: 316991: Attribute.getJavaMember() returns null for all Embeddable Attributes
- 1.7.2.98 DI 106: 20100810: 322585:Metamodel initialization via EMF.getMetamodel() before EM deploy results in an invalid Metamodel because of UNINITIALIZED descriptor.initializationStage
- 1.7.2.98.1 Reproduction
- 1.7.2.98.2 References:
- 1.7.2.98.3 Analysis 106:
- 1.7.2.98.4 Alt 106-1: Fail-fast with ISE: emf.getMetamodel() fails with non-spec ISE if in State_predeployed (entity descriptors will be in state 0 not 3)
- 1.7.2.98.5 Alt 106-2: Add State warning and continue to do nothing
- 1.7.2.98.6 Alt 106-3: Force full real deploy
- 1.7.2.98.7 Alt 106-4: Force full internal discardable deploy only for metamodel
- 1.7.2.98.8 Alt 106-5: Partial deploy based on what metamodel needs (entity deployment for embeddables and MappedSuperclasses
- 1.7.2.98.9 Alt 106-6: Full deploy during predeploy only for SE platform
- 1.7.2.98.10 Alt 106-7: Do a login only for early emf.getMetamodel() users
- 1.7.2.98.11 Issues 106:
- 1.7.2.98.12 Regression Testing 106
- 1.7.2.98.13 Fixed 106:
- 1.7.2.99 DI 107: 20110228: Metamodel missing Dynamic JPA metadata requires Regeneration
- 1.7.2.100 DI 108: 20110321: Expose JPA Metamodel with Extensible API relationships via JMX Management API
- 1.8 Testing Models
- 1.9 Implementation
- 1.10 Building
- 1.11 Testing
- 1.12 Open Issues
- 1.13 Timeline
- 1.13.1 20090305 Meeting
- 1.13.2 20090306 Architecture Meeting
- 1.13.3 20090310 Dev Meeting
- 1.13.4 20090318 Dev Meeting
- 1.13.5 20090406 Review by Guy
- 1.13.6 20090406 Review by Gordon
- 1.13.7 20090508 Specification Questions for Gordon
- 1.13.8 20090513 Mapped Superclass Discussion with Gordon/Peter
- 1.13.9 20090525 Implement/Synchronize with lastest Specification API changes in rev 4265
- 1.13.10 20090529: Review Guy
- 1.13.11 20090609: Review by Guy & Gordon
- 1.13.12 20090612: Review by Guy & Gordon
- 1.13.13 20090617: RelationalDescriptor Review by James
- 1.13.14 20090618: Synchronize with 20090617 Criteria API specification changes
- 1.13.15 20090624: Code Review with Guy, Gordon
- 1.14 Decisions
- 1.15 Appendix A : Specification Details
- 1.16 Appendix B: Deprecated Design Documentation Sections
- 1.17 User Use Cases of the Metamodel API
- 1.18 References
JPA 2.0: MetaModel API
- EclipseLink Summit 2010 presentation of the JPA 2.0 Metamodel (Microsoft PPT format)
Status
- The JPA 2.0 Metamodel API as part of EclipseLink 2.0 is complete as of 20091123, all the major design issues below have been addressed via their associated patch and SVN rev# - the last being 5849.
- 20090925: All API interface functions are implemented for EclipseLink 2.0 as of SVN rev# 5303
- - the next step after the 2.0 release is to verify the implementation against the specification, do a full 2nd review, fine tune and possibly refactor the implementation for performance and extensibility (IE: Dyanamic Persistence).
- The JPA 2.0 implementation of the Metamodel API in EclipseLink 1.2 is a preview snapshot of SVN rev# 5189
- - the implementation of the IdentifiableType interface is incomplete in this older view - consequently any interaction with id or version attributes will throw a "Not Yet Implemented" PersistenceException.
Code Submission Summary
Date | Committer(s) | Description, SVN Rev# and Design Issue #'s |
---|---|---|
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, 20 2009 | mobrien | Rev# 4931, See bug 266912 Design Issue 61 - ManagedType.getDeclaredX() leaks members into entity-entity hierarchy. |
Aug, 25 2009 | mobrien | Rev# 4966, See bug 266912 Design Issue 62, 63. |
Sept, 4 2009 | mobrien | Rev# 5050, See bug 266912 Design Issue 67 and 63, 66, 67. |
Sept, 10 2009 | mobrien | Rev# 5104, See bug 266912 Design Issue 79 MapAttribute keyType requires @MapKey handler in the template case when CMP3Policy.getPKClass() is null . |
Sept, 14 2009 | mobrien | Rev# 5124, See bug 266912 Design Issue 80 MappedSuperclassTypeImpl.create() does not use ConversionManager to get the EE classLoader
. |
Sept, 16 2009 | mobrien | Rev# 5148, See bug 266912 Design Issue 81 Enable @BasicMap DirectMapContainerPolicy support in MapAttributeImpl . |
Sept, 17 2009 | mobrien | Rev# 5165, See bug 266912 Design Issue 80 Prepare advanced model metamodel tests for future EE server testing capability via !isJPA10() |
Sept, 18 2009 | mobrien | Rev# 5189, See bug 266912 Design Issue 83 @BasicMap elementType (Map value parameter) support in DirectCollectionMapping.attributeClassification |
Sept, 25 2009 | mobrien | Rev# 5303, See bug 266912 Design Issue 70 - 77 and 65 Implement remaining 7 IdentifiableType functions (Id, IdClass and version). |
Sept, 29 2009 | mobrien | Rev# 5347, See bug 266912 Design Issue 85 SingularAttributeImpl: Relax IllegalArgumentException checking on autoboxed primitives |
Oct, 02 2009 | mobrien | Rev# 5425, See bug 266912 JPA 2.0 Specification adjustments (Metamodel.type() to Metamodel.managedType()) |
Oct, 16 2009 | mobrien | Rev# 0000, See bug 266912 Design Issue 93 - We currently use the new isJPAId() flag in IdentifiableTypeImpl.getIdType() - we need to refactor this custom processing for MappedSuperclass descriptors up into metadata processing by implementing custom initialization in EntityManagerSetupImpl.assignCMPPolicy() which sets the CMPPolicy on our custom MS descriptors and sets the pkClass so we can remove the bulk of the code in getIdType() - this is in the absence of an ObjectBuilder for our MS descriptor. See bug 290567 in rev 5602 |
Nov, 4 2009 | mobrien | Rev# 5729, See bug 266912 DI 96: Add support to JPA to detect a m:1 as 1:1 and a 1:m as m:m mapping for use by Attribute.getPersistentAttributeType() |
Nov, 8 2009 | mobrien | Rev# 5761, See bug 266912 DI 86: MapAttribute support for entity mapkey |
Nov, 10 2009 | mobrien | Rev# 5776, See bug 266912 DI 98: Leverage MappedKeyContainerPolicy.keyMapping for determining keyType:K for Map<K,V>, add @BasicMap support and tune workarounds for attributeClassification (bug# 294765) and CMP3Policy (bug# 294811) workarounds for missing @MapKey name attribute support |
Nov, 13 2009 | mobrien | Rev# 5793, See bug 294765 DI 98: MapKey keyType DirectToField processing should return attributeClassification class in getMapKeyTargetType when accessor.attributeField is null |
Nov, 17 2009 | mobrien | Rev# 5805,Rev# 5808, See bug 266912 DI 80: MappedSuperclassTypeImpl.create() does not use ConversionManager to get the EE classLoader - make test suite reentrant |
Nov, 22 2009 | mobrien | Rev# 5849, See bug 288972 DI 78: Composite @IdClass on inherited MappedSuperclass chain causes new ValidationException - the reproduction model was incorrect. |
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.
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
EmbeddableAccessor.java - JPA core
EntityAccessor.java - JPA core
MappedSuperclassAccessor.java - JPA core
ElementCollectionAccessor.java - JPA core
- See 248293
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
Analysis
- My understanding is that the metamodel API provides 4 (four) 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).
- 4) The Metamodel API is not used internally to initialize the canonical metamodel classes that were generated by the [APT tool API in bug# 267391 - however the two API's must match at runtime.
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.
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; |
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; |
R2.6.2 | - | - | Set if the collection type of z is java.util.Set, then
public static volatile Set<X, Z> z; |
R2.6.3 | - | - | List if the collection type of z is java.util.List, then
public static volatile List<X, Z> z; |
R2.6.4 | - | - | Map if the collection type of z is java.util.Map, then
public static volatile Map<X, K, Z> z; |
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 objest 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.
Implementors of the Metamodel API
- The following projects or APIs require the Metamodel API in order to function.
- See section 6.2.1 of the specification
267391: Canonical Metamodel Static Class Generation Intialization
- The initialization of the canonical "_" underscore classes can be done by reflectively setting attributes by leveraging the runtime metamodel.
: Criteria API Typesafe Queries
- The criteria API queries that use the "_" underscore classes utilizes the runtime metamodel API.
: Criteria API String based Queries
- The criteria API queries that use dynamic string based get calls utilize the runtime metamodel API.
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
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
- Singular
- Attributes
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)
Metamodel Implementation
The following UML class diagram illustrates the static relationships and hierarchy of the Metamodel implementation classes.
- 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
- Legend: Anything with an italics-green header is resolved.
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(RelationalDescriptor mappedSuperclass : mappedSuperclassesSet.values()) { ..... }
- 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)
- Update: see bug 322166 fix.
- 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)
- sourceForeignKeyField DatabaseField (id=64)
- 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
- IdentifiableType
- ManagedType
- 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
Solution:
We are setting the javaClass later during metamodel processing instead of during metadata processing - so we can avoid persistence operations on our custom Relational Descriptor.
- 20100217: see bug# 303063 for an issue where RelationalDescriptor.getJavaClass() is null (out of spec) causing a possible NPE for SingularAttributes.
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)
- See Fix for Design Issue # 58
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
- There are 3 valid cases out of a possible 4 for these functions
* @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 (Entity or MappedSuperclass because it has no @Entity or @MappedSuperclass annotation)
- Java vs Metamodel object spaces discussion:
- The superclass for top-level types is Object - however we set [null] as the supertype for root types.
- The reasoning behind this is to separate the Java and Metamodel object spaces.
- In Java all type inherit from Object, however in the JPA Metamodel all types DO NOT inherit from a common type.
- Therefore in the metamodel top-level root types have a superType of null.
/** * 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() {
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)
- ManagedType
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
- Use Case Partitioning:
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
- Fixed 20090731 in rev# 4765
- 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")); }
- Fixed 20090731 in rev# 4765
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
- 20091214 - This issue is deprecated by DI 92 and the fix for enhancement# 297748
- See ELUG documentation on Canonical Metamodel Classes Generation http://wiki.eclipse.org/UserGuide/JPA/Using_the_Canonical_Model_Generator_%28ELUG%29
- 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.
- The current EntityManagerFactoryImpl metamodel functions.
/** * Return an instance of Metamodel interface for access to the * metamodel of the persistence unit. * @return Metamodel instance * @throws IllegalStateException if the entity manager factory has * been closed. * @see javax.persistence.EntityManagerFactory#getMetamodel() * @since Java Persistence 2.0 */ public Metamodel getMetamodel() { if(!this.isOpen()) { throw new IllegalStateException(ExceptionLocalization.buildMessage("operation_on_closed_entity_manager_factory")); } // perform lazy initialization if(null == metaModel) { metaModel = new org.eclipse.persistence.internal.jpa.metamodel.MetamodelImpl(this); //If the canonical metamodel classes exist, initialize them initializeCanonicalMetamodel(metaModel); } return metaModel; } /** * INTERNAL: * Convenience function to allow us to reset the Metamodel * in the possible case that we want to regenerate it. * This function is outside of the JPA 2.0 specification. * @param aMetamodel */ public void setMetamodel(Metamodel aMetamodel) { if(!this.isOpen()) { throw new IllegalStateException(ExceptionLocalization.buildMessage("operation_on_closed_entity_manager_factory")); } metaModel = aMetamodel; }
Solution 46: - Fixed
- 20091006 status: This issue is reopened - we are moving the initialization higher up to EMF creation
- We will need a baseline with 100's of persistence units holding xx entities/mappedSuperclasses/embeddables which we can use to measure any performance increase
- After a discussion with Gordon it would be best to move the metamodel to EntityManagerSetupImpl from EntityManagerFactoryImpl where it will get initialized during the predeploy - after stage 3 is complete.
EntityAccessor.preProcess(boolean) line: 636 EntityAccessor.preProcess() line: 589 MetadataProject.processStage1() line: 1281 MetadataProcessor.processORMMetadata() line: 461 PersistenceUnitProcessor.processORMetadata(MetadataProcessor, boolean) line: 337 EntityManagerSetupImpl.predeploy(PersistenceUnitInfo, Map) line: 857
- 20091214 status: Issue reopened again - we will not let any exception during metamodel processing to hold up the entityManager deploy - see bug# 297555 and enhancement# 297nnn
DI 47: 20090715: Implement IdentifiableType.getIdType() for composite keys
- The following code works fine for single keys such as Integer and @EmbeddedId classes
- Q) What is the differences/advantages of using @IdClass and @EmbeddedId?
- See http://forums.java.net/jive/thread.jspa?threadID=17026
- See section 2.4.1.2 in the JPA 2.0 specification.
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
- 20090824: See continuation part 2 in DI 63 of this fix that adds support for the @MapKey annotation in addition to this default behavior of using the Primary Key of the target entity when the @MapKey annotation is not present.
- 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
- UCA: Two threads attempt to modify the PK List in an un-Synchronized state
- Note: The following use cases are covered by this operation.
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.
- Issues
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(); for(Iterator<DatabaseField> it = pkList.iterator(); it.hasNext(); ) { if(it.next().getName().equals(MetadataConstants.MAPPED_SUPERCLASS_RESERVED_PK_NAME)) { it.remove(); // exit loop break; } } // 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.
- Issues
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.
- Issues
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().isEmpty()) { // 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(MappingAccessor anAccessor : metadataDescriptor.getAccessors()) { 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.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
- 20100217: see bug# 303063 for an issue where RelationalDescriptor.getJavaClass() is null (out of spec) causing a possible NPE for SingularAttributes.
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
- 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 isAttributeDeclaredOnlyInLeafTypee 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 isAttributeDeclaredOnlyInLeafType(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 isAttributeDeclaredOnlyInLeafType(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"))); //
Solution 52: - Fixed
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 54: - Fixed after Reopen
- 20090804 - Fix is part of the diff for enhancement 285512
- 20091014 - reopened bug
- During a pass through the code - I have decided that the specification is clear - all non-ManagedType clazz parameters should throw an IAE - even null.
The function needs to be reverted back to it's original state - not that it has been renamed .managedType() and its' usage in the Criteria tests should be modified to getType().
- I discussed this with Chris
- There is a secondary issue to the fix. In the Metamodel interface there is no specification function that will return a Type - the implementation classes will need to be used in this case.
CriteriaQueryImpl.java:118 public CriteriaQuery<T> select(Selection<? extends T> selection) { // From: ManagedType type = this.metamodel.managedType(this.queryType); // To: TypeImpl type = ((MetamodelImpl)this.metamodel).getType(this.queryType);
- Test results
- With Metamodel change and no Criteria change
TEST MODEL NAME: (JUnit test): Criteria Errors: (failures): 0 Fatal Errors: (errors): 15 Passed: 104 Total Tests: 119
- With Metamodel change and one of the Criteria changes (fixes 12 of the 15 issues)
TEST MODEL NAME: (JUnit test): Criteria Errors: (failures): 0 Fatal Errors: (errors): 3 Passed: 116 Total Tests: 119
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
Solution: - Fixed
- 20090925 - See solution for design Issue 74
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 57: - Partially fixed
- Refactor the create() method to call the MappedSuperclassTypeImpl constructor as well - by creating a version of the create() static method on MappedSuperclassTypeImpl and possibly EntityTypeImpl - bypassing the abstrace IdentifiableTypeImpl.
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
- The implementation of the PluralAttribute constructor is missing the case for a non-lazy instantiated collection. In this case we are unable to use the normal methods of mapping.getField().getType() or mapping.getReferenceType() or policy.getElementClass() to obtain the elementType Class - we must use mapping.getReferenceClass()
- See also http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_50:_20090727:_Handle_all_mapping_types_in_the_SingularAttribute_constructor
- Previously working example in Corporation.java - a MappedSuperclass - therefore the mapping is unidirectional
@OneToMany(cascade=ALL) private Collection<Computer> corporateComputers;
- Previously broken example in Corporation.java - a MappedSuperclass - therefore the mapping is unidirectional
@OneToMany(cascade=ALL) private Collection<Computer> corporateComputers = new HashSet<Computer>();
Solution: Fixed
protected PluralAttributeImpl(ManagedTypeImpl<X> managedType, CollectionMapping mapping, boolean validationEnabled) { .. if (elementDesc != null) { .. } else { if(mapping.isDirectCollectionMapping() || mapping.isAbstractCompositeDirectCollectionMapping()) {// || mapping.isAbstractDirectMapping() ) { .. } else { // 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); }
- See SVN Rev# 4916
DI 60: 20090820: Refactor SingularAttribute and PluralAttribute constructor elementType discovery up into Attribute
- There is duplicated code in both Attribute subclasses that can be merged into the superclass constructor
- See also
- http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_50:_20090727:_Handle_all_mapping_types_in_the_SingularAttribute_constructor
- http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_59:_20090818:_PluralAttribute.elementType_not_set_for_non-lazy_instantiated_Collection_Attribute
Solution:
DI 61: 20090820: ManagedType.getDeclaredX() leaks members into entity-entity hierarchy
- It turns out that members are being leaked into inheriting entities in a entity-entity hierarchy because the expected "gap" or no-copy behavior expected that occurs for MappedSuperclass hierarchy Descriptors is not happening.
- Currently the code does not handle the fact that there are copies of inherited declared mappings in an entity subclass.
private boolean isAttributeDeclaredOnlyInLeafType(String attributeName, Attribute firstLevelAttribute) { if(null == aSuperType) { ... } else { if(aSuperType.isMappedSuperclass()) { ... } else { // superType (Entity) may declare the attribute higher up - we do not need to check this // TODO: verify handling of XML mapped non-Entity (plain Java Class) inherited mappings // http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_53:_20090729:_Verify_that_inheritied_non-JPA_class_mappings_are_handled_by_the_Metamodel if(null == anAttribute) { return false; } else { --->in error return true; } } } }
Example
@Entity(name="ProcessorMetamodel") @Table(name="CMP3_MM_PROC") @Inheritance(strategy=JOINED) public class Processor { // The M:1 side is the owning side @ManyToOne(fetch=EAGER)//LAZY) @JoinTable(name="CMP3_MM_BOARD_MM_PROC", joinColumns = @JoinColumn(name="PROC_ID"), inverseJoinColumns =@JoinColumn(name="BOARD_ID")) private Board board; } @Entity(name="VectorProcessorMetamodel") @Table(name="CMP3_MM_PROC") public class VectorProcessor extends Processor {}
Solution: 61 - Fixed
- Check the Entity superclasses as well as the already checked MappedSuperclasses.
- see SVN Rev# 4931
DI 62: 20090820: Add support for Embeddable Collections as part of AGGREGATE_COLLECTION support
- The ManagedTypeImpl.create() function requires enhancement to support collections of Embeddables.
- We also need a way to differentiate between Embeddables and Entities here.
public static ManagedTypeImpl<?> create(MetamodelImpl metamodel, RelationalDescriptor descriptor) { // AGGREGATE:2 or AGGREGATE_COLLECTION:3 if (descriptor.isAggregateDescriptor()) { // AGGREGATE:2 == EMBEDDABLE managedType = new EmbeddableTypeImpl(metamodel, descriptor); } else if (descriptor.isAggregateCollectionDescriptor()) { --->functionality required both for Embeddables and Entities
Analysis:
Solution:
- In progress as of 20090820
DI 63: 20090824: Add Map support for @MapKey to MapAttribute
- See work done 1 month ago to add Map support in DI 48 that adds support for the default primary key as map key (when the @MapKey is absent). It turns out that this is only half the implementation - I still need to add support for obtaining the map key class when set using the @MapKey annotation.
- For the following MapAttribute
@Entity public class Manufacturer extends Corporation implements java.io.Serializable{ @OneToMany(cascade=ALL, mappedBy="mappedEmployer") private Map<String, HardwareDesigner> hardwareDesignersMap;// = new HashMap<String, HardwareDesigner>(); } @Entity 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; }
- We are getting the Integer PK field for the ManagedType instead of the String Map Key - because the @MapKey annotation is missing and we default to the target entity primary key.
anAttribute MapAttributeImpl<X,K,V> (id=190) elementType EntityTypeImpl<X> (id=156) descriptor RelationalDescriptor (id=202) javaClass Class<T> (org.eclipse.persistence.testing.models.jpa.metamodel.HardwareDesigner) (id=131) members HashMap<K,V> (id=208) metamodel MetamodelImpl (id=109) superType MappedSuperclassTypeImpl<X> (id=209) keyType BasicTypeImpl<X> (id=196) javaClass Class<T> (java.lang.Integer) (id=211) managedType EntityTypeImpl<X> (id=154) descriptor RelationalDescriptor (id=213) javaClass Class<T> (org.eclipse.persistence.testing.models.jpa.metamodel.Manufacturer) (id=125) members HashMap<K,V> (id=214) metamodel MetamodelImpl (id=109) superType MappedSuperclassTypeImpl<X> (id=94) mapping OneToManyMapping (id=199)
Use Cases:
- The use cases are split into 2 sections of 2 based on whether the attribute is using generics or not to define the K, V pair, and whether the @MapKey annotation is set.
- Prerequisites
- Map attribute is on an Entity (it may inherit from a MappedSuperclass and may also be the superclass of a subclass Entity).
UC 1a: Generics KV set, no @MapKey present, PK is singular field
@Id private Integer id; @OneToMany(cascade=ALL, mappedBy="mappedEmployer") private Map<String, HardwareDesigner> hardwareDesignersMap;
- Expected results:
- keyType is the primary key (PK) of the target entity - in this case HardwareDesigner which inherits its @Id from the Person @MappedSuperclass as Integer.
UC 1b: Generics KV set, no @MapKey present, PK is Multiple fields
@OneToMany(cascade=ALL, mappedBy="mappedEmployer") private Map<String, HardwareDesigner> hardwareDesignersMap;
- Expected results:
- keyType is the primary key (PK) of the target entity.
UC 1c: Generics KV set, no @MapKey present, PK is EmbeddedId
@EmbeddedId protected EmbeddedPK primaryKey; @OneToMany(cascade=ALL, mappedBy="mappedEmployer") private Map<String, HardwareDesigner> hardwareDesignersMap;
- Expected results:
- keyType is the target entity primary key class.
UC 1d: Generics KV set, no @MapKey present, PK is ClassId
@OneToMany(cascade=ALL, mappedBy="mappedEmployer") private Map<String, HardwareDesigner> hardwareDesignersMap;
- Expected results:
- keyType is the target entity primary key class.
UC 2: Generics KV set, @MapKey is present
@OneToMany(cascade=ALL, mappedBy="mappedEmployer") @MapKey(name="name") private Map<String, HardwareDesigner> hardwareDesignersMap;
- Expected results:
UC 3a: No Generics KV set, no @MapKey present, PK is singular field
@Id private Integer id; @OneToMany(targetEntity=HardwareDesigner.class, cascade=ALL, mappedBy="mappedEmployer") private Map hardwareDesignersMap;
- Expected results:
UC 3b: No Generics KV set, no @MapKey present, PK is Multiple fields
@OneToMany(targetEntity=HardwareDesigner.class, cascade=ALL, mappedBy="mappedEmployer") private Map hardwareDesignersMap;
- Expected results:
UC 3c: No Generics KV set, no @MapKey present, PK is EmbeddedId
@EmbeddedId protected EmbeddedPK primaryKey; @OneToMany(targetEntity=HardwareDesigner.class, cascade=ALL, mappedBy="mappedEmployer") private Map hardwareDesignersMap;
- Expected results:
UC 3d: No Generics KV set, no @MapKey present, PK is ClassId
@OneToMany(targetEntity=HardwareDesigner.class, cascade=ALL, mappedBy="mappedEmployer") private Map hardwareDesignersMap;
- Expected results:
UC 4: No Generics KV set, @MapKey is present
@OneToMany(targetEntity=HardwareDesigner.class, cascade=ALL, mappedBy="mappedEmployer") @MapKey(name="name") private Map hardwareDesignersMap;
- Expected results:
- Variant Use cases
- Expected results:
UC 5: Run UC 1-4 on a MappedSuperclass
@OneToMany(cascade=ALL, mappedBy="mappedEmployer") private Map<String, HardwareDesigner> hardwareDesignersMap;
- Expected results:
UC 6: No Generics KV set, no targetEntity set, @MapKey is *(set/unset)
@OneToMany(cascade=ALL, mappedBy="mappedEmployer") private Map hardwareDesignersMap;
- Expected results:
- Invalid
- Exception Description: The target entity of the relationship attribute [hardwareDesignersMapUC6] on the class [class org.eclipse.persistence.testing.models.jpa.metamodel.Manufacturer] cannot be determined. When not using generics, ensure the target entity is defined on the relationship mapping.
UC 7: Generics KV set, targetEntity is also set, @MapKey is *(set/unset)
@OneToMany(targetEntity=HardwareDesigner.class, cascade=ALL, mappedBy="mappedEmployer") private Map<String, HardwareDesigner> hardwareDesignersMap;
- Expected results:
UC 8: Generics KV set, Map is instantated to HashMap
@OneToMany(cascade=ALL, mappedBy="mappedEmployer") private Map<String, HardwareDesigner> hardwareDesignersMap = new HashMap<String, HardwareDesigner>;
- Expected results:
Test Model:
@Entity public class Manufacturer extends Corporation implements java.io.Serializable{ // The following MapAttribute use cases are referenced in Design Issue 63 // http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_63:_20090824:_Add_Map_support_for_.40MapKey_to_MapAttribute // UC 1a: Generics KV set, no @MapKey present, PK is singular field @OneToMany(cascade=ALL, mappedBy="mappedEmployerUC1a") private Map<String, HardwareDesigner> hardwareDesignersMapUC1a; // UC 2: Generics KV set, @MapKey is present @OneToMany(cascade=ALL, mappedBy="mappedEmployerUC2") @MapKey(name="name") private Map<String, HardwareDesigner> hardwareDesignersMapUC2; // UC 4: No Generics KV set, @MapKey is present @OneToMany(targetEntity=HardwareDesigner.class, cascade=ALL, mappedBy="mappedEmployerUC4") @MapKey(name="name") private Map hardwareDesignersMapUC4; // UC 6: No Generics KV set, no targetEntity set, @MapKey is *(set/unset) @OneToMany(cascade=ALL, mappedBy="mappedEmployerUC6") private Map hardwareDesignersMapUC6; // UC 7: Generics KV set, targetEntity is also set, @MapKey is *(set/unset) @OneToMany(targetEntity=HardwareDesigner.class, cascade=ALL, mappedBy="mappedEmployerUC7") private Map<String, HardwareDesigner> hardwareDesignersMapUC7; }
Analysis:
- We have 3 options for getting the generic type key on the Map mapping.
- 1) We can get if directly from the Java class
- 2) We can get it from the MetadataClass
- 2a) Add a JPA metadata processing step that stores a property on the mapping for the Map key
- 2b) Add a field or property on the mapping
- For this approach we will need to add a map of non-MappedSuperclass descriptors to Project for use by the Metamodel
- Debugging the creation of the MapAttribute during keyType creation.
Thread [Thread-3] (Suspended) MapAttributeImpl<X,K,V>.<init>(ManagedTypeImpl<X>, CollectionMapping, boolean) line: 76 EntityTypeImpl<X>(ManagedTypeImpl<X>).initialize() line: 1064 MetamodelImpl.initialize() line: 349 MetamodelImpl.<init>(DatabaseSession) line: 86 MetamodelImpl.<init>(EntityManagerFactory) line: 96 EntityManagerFactoryImpl.getMetamodel() line: 433 EntityManagerImpl.getMetamodel() line: 2054
- Using the Java class - we don't get the key that we need
managedType EntityTypeImpl<X> (id=96) descriptor RelationalDescriptor (id=139) javaClass Class<T> (org.eclipse.persistence.testing.models.jpa.metamodel.Manufacturer) (id=262) declaredFields SoftReference<T> (id=270) referent Field[5] (id=279) [3] Field (id=6201) clazz Class<T> (org.eclipse.persistence.testing.models.jpa.metamodel.Manufacturer) (id=262) name "hardwareDesignersMap" (id=132) signature "Ljava/util/Map<Ljava/lang/String;Lorg/eclipse/persistence/testing/models/jpa/metamodel/HardwareDesigner;>;" (id=6216) type Class<T> (java.util.Map) (id=70) genericInfo ClassRepository (id=6260) typeParams TypeVariable<D>[2] (id=6266) [0] TypeVariableImpl<D> (id=6278) boundASTs FieldTypeSignature[1] (id=6285) [0] ClassTypeSignature (id=6304) path ArrayList<E> (id=6306) elementData Object[5] (id=6309) [0] SimpleClassTypeSignature (id=6311)
- We will use the MetadataClass JPA wrapper and obtain the keyMapping from modifications done in MappingAccessor.
managedType EntityTypeImpl<X> (id=133) javaClass Class<T> (org.eclipse.persistence.testing.models.jpa.metamodel.Manufacturer) (id=151) metamodel MetamodelImpl (id=149) session ServerSession (id=192) project Project (id=31) mappedSuperclassDescriptors HashMap<K,V> (id=256) table HashMap$Entry<K,V>[8] (id=350) [4] HashMap$Entry<K,V> (id=353) key MetadataClass (id=356) m_fields HashMap<K,V> (id=367) table HashMap$Entry<K,V>[16] (id=381) [14] HashMap$Entry<K,V> (id=385) key "corporateComputers" (id=387) value MetadataField (id=388) m_annotations HashMap<K,V> (id=391) m_attributeName "corporateComputers" (id=387) m_factory MetadataAsmFactory (id=364) m_genericType ArrayList<E> (id=392) elementData Object[10] (id=400) [0] "java.util.Collection" (id=402) [1] "org.eclipse.persistence.testing.models.jpa.metamodel.Computer" (id=403) m_name "corporateComputers" (id=387) m_type "java.util.Collection" (id=394)
Solution: Fixed
- 20090901: from initial testing it looks like the metamodel layer supplies everything correctly to the metamodel layer - no work required so far.
- 20090903: see SVN rev# 5050
- 20110315: see key change from MetadataClass to String in https://bugs.eclipse.org/bugs/show_bug.cgi?id=338140
DI 64: 20090825: ManagedTypeImpl.isAttributeDeclaredOnlyInLeafType() fails to find attributes declared root of mappedSuperclass<--entity<--entity hierarchy
- The last recursive return flag in the following line is reversed for entity-->entity hierarchies - it is working for MappedSuperclass chains.
- It also has no problem with single table inheritance - only shared table inheritance has an issue.
return aSuperType.isAttributeDeclaredOnlyInLeafType(attributeName, firstLevelAttribute);
- We also need to simplify the recursion by extracting out the dual functionality into 2 separate functions
- We can eliminate recursion by using reflective getDeclaredField() calls and maintaining a separate list
- We can skip the null check on a null attributeName
- We can pass in an Attribute directly instead of using the wrapper function to get the attributeName
- More testing is required for Entity-->Entity inheritance chains where attributes are declared on the leaf.
Solution:
- Turns out that for one issue was a SVN sync issue.
- Add an attribute declared on the top of a mappedSuperclass<--entity<--entity hierarcy
DI 65: 20090827: Handle DirectCollection elementType retrieval in the absence of a generic type
High Priority
- If the designer omits generics in the attribute definition - we need a way to determine the type of the collection.
Example:
- Notice there is no Collection<String> that the method level MethodAttributeAccessor can key off of - there may be one on the field declaration.
package org.eclipse.persistence.testing.models.jpa.advanced; public class Employee implements Serializable, Cloneable { private Collection<String> responsibilities; ... @BasicCollection(valueColumn=@Column(name="DESCRIPTION")) @CollectionTable(name="CMP3_RESPONS") // generics left off the Collection on purpose ... @Property(name="attributeName", value="responsibilities") public Collection getResponsibilities() { return responsibilities; }
Analysis:
- We will need to key off the declared field's generic declaration on the clazz - if it exists.
- However, we should not have to parse the Ljava/util/Collection<Ljava/lang/String;>; signature field - it should be handed to us by the metadata processing layer.
mapping DirectCollectionMapping (id=145) attributeAccessor MethodAttributeAccessor (id=150) attributeName "responsibilities" (id=151) getMethod Method (id=170) clazz Class<T> (org.eclipse.persistence.testing.models.jpa.advanced.Employee) (id=99) declaredFields SoftReference<T> (id=6724) referent Field[31] (id=6728) [18] Field (id=6763) annotations null clazz Class<T> (org.eclipse.persistence.testing.models.jpa.advanced.Employee) (id=99) declaredAnnotations null fieldAccessor null genericInfo null modifiers 2 name "responsibilities" (id=6777) override false overrideFieldAccessor null root null securityCheckCache null securityCheckTargetClassCache null signature "Ljava/util/Collection<Ljava/lang/String;>;" (id=6779) slot 126 type Class<T> (java.util.Collection) (id=120)
Solution:
- We are currently defaulting to Object.class - which is not good in
protected PluralAttributeImpl(ManagedTypeImpl<X> managedType, CollectionMapping mapping, boolean validationEnabled) {
DI 66:20090827: EnumSet support - expected/no-fix
- 20090828: This issue is deprecated - the EnumSet should resolve to a SingularAttribute
- We currently define an EnumSet as a SingularAttribute in error - it should be a SetAttribute of BasicType elements.
- Here is the example from the advanced jpa model - part of the SRG.
package org.eclipse.persistence.testing.models.jpa.advanced; @Entity @Table( name="CMP3_BUYER", uniqueConstraints = { @UniqueConstraint(columnNames={"BUYER_ID", "BUYER_NAME"}), @UniqueConstraint(columnNames={"BUYER_ID", "DESCRIP"}) } ) @Inheritance(strategy=JOINED) public class Buyer implements Serializable { public enum Weekdays { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY } private EnumSet<Weekdays> buyingDays; @Column(name="BUY_DAYS") public EnumSet<Weekdays> getBuyingDays() { return buyingDays; }
- In the following stacktrace
Thread [Thread-4] (Suspended (breakpoint at line 98 in SingularAttributeImpl)) SingularAttributeImpl<X,T>.<init>(ManagedTypeImpl<X>, DatabaseMapping, boolean) line: 98 EntityTypeImpl<X>(ManagedTypeImpl<X>).initialize() line: 1215 MetamodelImpl.initialize() line: 351 MetamodelImpl.<init>(DatabaseSession) line: 88 MetamodelImpl.<init>(EntityManagerFactory) line: 98 EntityManagerFactoryImpl.getMetamodel() line: 433 EntityManagerImpl.getMetamodel() line: 2054 AdvancedJPAJunitTest.testMetamodel() line: 184
- Here is what we instantiate the buyingDays member to.
entityBuyer EntityTypeImpl<X> (id=224) descriptor RelationalDescriptor (id=230) javaClass Class<T> (org.eclipse.persistence.testing.models.jpa.advanced.Buyer) (id=223) members HashMap<K,V> (id=231) size 8 table HashMap$Entry<K,V>[16] (id=233) [10] HashMap$Entry<K,V> (id=243) key "buyingDays" (id=264) value SingularAttributeImpl<X,T> (id=265) elementType BasicTypeImpl<X> (id=267) managedType EntityTypeImpl<X> (id=224) mapping DirectToFieldMapping (id=270)
Analysis:
- The EnumSet mapping is not recognized as a collectionMapping (Set) below because it is actually a DirectToFieldMapping (Basic) - so the existing code is correct in general and will not be modified.
public abstract class ManagedTypeImpl<X> extends TypeImpl<X> implements ManagedType<X> { protected void initialize() { // TODO: Check all is*Policy() calls for (DatabaseMapping mapping : getDescriptor().getMappings()) { if (mapping.isCollectionMapping()) { } else { // Handle 1:1 single object and direct mappings --> member = new SingularAttributeImpl(this, mapping, true);
Solution: Fixed
- no-fix - code is working as expected
DI 67:20090827: AggregateCollectionMapping support in PluralAttribute
- We are not currently handling AggregateCollections in PluralAttribute.
- See org.eclipse.persistence.mappings.AggregateCollectionMapping[records]
Solution:
protected PluralAttributeImpl(ManagedTypeImpl<X> managedType, CollectionMapping mapping, boolean validationEnabled) { } else if(mapping.isAggregateCollectionMapping()) { // get reference class and check if managedType is a MappedSuperclass attributeClass = ((AggregateCollectionMapping)mapping).getReferenceClass(); }
DI 68: 20090828: Double (auto-boxed) or direct double Attribute.javaType
- This is an issue for all primitive Basic types if we expect the Object version of primitives.
- Do we return an auto-boxed java.lang.Double for an attribute declared as the primitive double?
- Currently I just return the class defined by the metadata - without transaltion (which can be double)
package org.eclipse.persistence.testing.models.jpa.advanced; @Entity public class LargeProject extends Project { private double m_budget; public double getBudget() {
Example:
- See the advanced model
EntityType<LargeProject> entityLargeProject = metamodel.entity(LargeProject.class); Set<Attribute<LargeProject, ?>> declaredAttributes = entityLargeProject.getDeclaredAttributes(); assertTrue(declaredAttributes.size() > 0); // instead of a assertEquals(1, size) for future compatibility with changes to Buyer // check that getDeclaredAttribute and getDeclaredAttributes return the same attribute Attribute<LargeProject, ?> budgetAttribute = entityLargeProject.getDeclaredAttribute("budget"); assertNotNull(budgetAttribute); Attribute<LargeProject, ?> budgetSingularAttribute = entityLargeProject.getDeclaredSingularAttribute("budget"); assertNotNull(budgetSingularAttribute); assertEquals(budgetSingularAttribute, budgetAttribute); assertTrue(declaredAttributes.contains(budgetSingularAttribute)); // check the type Class budgetClass = budgetSingularAttribute.getJavaType(); // Verify whether we expect a boxed class or not assertEquals(double.class, budgetClass); //assertEquals(Double.class, budgetClass);
Solution 68: - Fixed
- It was decided in the 20090831 scrum that we do not translate primitive types to their boxed Object versions.
- We set the TypeImpl.javaType directly from the type class returned from metadata processing.
DI 69: 20090831: Object.class as a default javaType is not compatible with JPA 1.0
- Use of Object.class as the default javaType is not acceptable as this is not a compatible type for a JPA mapping.
- See all
attributeClass = MetamodelImpl.DEFAULT_ELEMENT_TYPE_FOR_UNSUPPORTED_MAPPINGS; protected MapAttributeImpl(ManagedTypeImpl<X> managedType, CollectionMapping mapping, boolean validationEnabled) { public Type<?> getIdType() { protected PluralAttributeImpl(ManagedTypeImpl<X> managedType, CollectionMapping mapping, boolean validationEnabled) { protected Class getTypeClassFromAttributeOrMethodLevelAccessor(DatabaseMapping mapping) { public Class<T> getJavaType() { protected SingularAttributeImpl(ManagedTypeImpl<X> managedType, DatabaseMapping mapping, boolean validationEnabled) {
Solution:
- Use String.class as we do in other parts of the JPA API.
- or, fail fast and halt metamodel processing in these undefined or unsupported cases.
DI 70: 20090901: Implement JPA 2.0 specification changes in 090828 draft
- The following changes will affect the signature of the Metamodel API.
- getCollections is now getPluralAttributes
- getDeclaredCollections is now getDeclaredPluralAttributes
- See enhancement# 288267.
DI 71: 20090909: Implement IdentifiableType.getId()
- We need to answer the open questions in DI 47
- We need to model the MappedById annotation - see section 2.4.1.1 of the JPA 2.0 Specification.
Solution: 71 - Fixed
- See SVN Rev# 5303
References: 71
- For all of the IdentifiableType implementation changes here and below refer the the main bug# 266912 as well as the following secondary enhancement bugs 289817
DI 72: 20090909: Implement IdentifiableType.getDeclaredId()
Solution: 72 - Fixed
- See SVN Rev# 5303
DI 73: 20090909: Implement IdentifiableType.getIdClassAttributes()
Solution: 73 - Fixed
- See SVN Rev# 5303
DI 74: 20090909: Implement IdentifiableType.hasSingleIdAttribute()
- See SVN Rev# 5303
- See related secondary bug 289817
- see preliminary 266912 patch 20090925
- For all of the IdentifiableType issues 70 to 77 we can implement this functionality in 2 ways
- 1) Store flags on each attribute whether it is a version or id attribute - since the schema will not change at runtime until we implement dynamic persistence.
- 2) Dynamically figure out which attribute is a version or id attribute by consulting the project metadata
- In all cases we can either use the existing functionality in the CMP3Policy or figure out whether an attribute is an Id by consulting the Mapping and Descriptor
- For example if the mapping on the attribute is also in the attribute.mapping.descriptor().getObjectBuilder().getPrimaryKeyMappings List.
- Or we can call DatabaseMapping.isPrimaryKeyMapping.
- The following text is the JPA2 specification of this function - it essentially states that the function returns false when the IdentifiableType has an IdClass.
/** * Whether the identifiable type has a single id attribute. * Returns true for a simple id or embedded id; returns false * for an idclass. * @return boolean indicating whether the identifiable * type has a single id attribute */ boolean hasSingleIdAttribute();
- We have the following requirements based on the specification of getSingleIdAttribute.
- R1: A way to differentiate between IdClass and EmbeddedId id attributes.
Analysis: 74
- There are a couple ways that we can get the Id for this IdentifiableType. It may involve extending the current core API to preserve some of the metadata processing in the JPA project preprocessing.
R1:I1: Query the descriptor directly
- Code like the following can only go so far in returning our Id class - it will not differentiate between IdClass and EmbeddedId instances.
List<DatabaseField> pkFields = this.getDescriptor().getPrimaryKeyFields(); Class pkClass = ((CMP3Policy)this.getDescriptor().getCMPPolicy()).getPKClass();
R1:I2: Consult stored Id information on the session
- We can store the IdClass and EmbeddedId names in separate Maps of Lists on the core Project during predeploy metamodel processing and consult this list later during runtime
- The MetadataProject.addIdClass() function handles both EmbeddedId and IdClass attributes - we will key in on the code that only adds IdClass ones.
- We have the following hierarchy
- ClassAccessor (EmbeddedId processed but ignored in this case)
- EmbeddableAccessor (nested EmbeddedId processed but ignored in this case)
- MappedSuperclassAccessor (IdClass processed here)
- ClassAccessor (EmbeddedId processed but ignored in this case)
- 1) EmbeddableId attributes added in ClassAccessor.addAccessor() - not used
- We have the following hierarchy
public abstract class ClassAccessor extends MetadataAccessor { protected void addAccessor(MappingAccessor accessor) { if (accessor != null) { ... // Add any embeddedid references to the list of id 'used' classes. // That is @IdClass and @EmbeddedId reference classes. if (accessor.isEmbeddedId()) { getProject().addIdClass(accessor.getReferenceClassName()); // 266912: Save Ids on the core project for Metamodel processing not required --> getCoreProject().addMetamodelEmbeddedId(this.getAccessibleObject().getName(), accessor.getAccessibleObject().getName()); }
- 2) Nested Embeddeable of an EmbeddableId - not used
public class EmbeddableAccessor extends ClassAccessor { protected void addPotentialEmbeddableAccessor(MetadataClass potentialEmbeddableClass) { if (potentialEmbeddableClass != null) { // Get embeddable accessor will add the embeddable to the // project if it is a valid embeddable. That is, if one the class // has an Embeddable annotation of the class is used as an IdClass // for another entity within the persistence unit. EmbeddableAccessor embeddableAccessor = getProject().getEmbeddableAccessor(potentialEmbeddableClass, true); if (embeddableAccessor != null && ! embeddableAccessor.isPreProcessed()) { // 266912: Save Ids on the core project for Metamodel processing not required --> getCoreProject().addMetamodelEmbeddedId(this.getAccessibleObject().getName(), embeddableAccessor.getAccessibleObject().getName());
- 3) IdClass attributes added in EntityAccessor(MappedSuperclassAccessor).initIdClass() - we will use this one.
public class MappedSuperclassAccessor extends ClassAccessor { protected void initIdClass() { ... // Add the id class to the known list of id classes for this project. if (m_idClass != null && ! m_idClass.equals(void.class)) { getProject().addIdClass(m_idClass.getName()); --> getCoreProject().addMetamodelIdClass(this.getAccessibleObject().getName(), m_idClass.getName());
/** * Store the IdClass Id attributes for exclusive use by the Metamodel API * Keyed on the fully qualified accessible object owner class name. * Value is a List of ithe fully qualified id class name or id attribute name. * @since EclipseLink 1.2 for the JPA 2.0 Reference Implementation */ protected Map<String, List<String>> metamodelIdClassMap;
- For example the following EmbeddedId is present in our model.
this EntityTypeImpl<X> (id=304) javaClass Class<T> (org.eclipse.persistence.testing.models.jpa.metamodel.Manufacturer) (id=287) metamodel MetamodelImpl (id=317) session ServerSession (id=328) project Project (id=361) metamodelEmbeddedIdMap HashMap<K,V> (id=381) table HashMap$Entry<K,V>[16] (id=406) [10] HashMap$Entry<K,V> (id=408) key "org.eclipse.persistence.testing.models.jpa.metamodel.GalacticPosition" (id=414) value ArrayList<E> (id=415) elementData Object[10] (id=417) [0] "primaryKey" (id=420)
- The following IdClass has been added to the model
@Entity(name="EnclosureMetamodel") @Table(name="CMP3_MM_ENCLOSURE") @IdClass(org.eclipse.persistence.testing.models.jpa.metamodel.EnclosureIdClassPK.class) public class Enclosure implements java.io.Serializable{ @Id @Column(name="TYPE") public String type; @Id @Column(name="LENGTH") protected String length; @Id @Column(name="WIDTH") private String width; } public class EnclosureIdClassPK { public String type; protected String length; private String width; }
- Duplicates or composite keys are handles by adding to a current List
- From
{ org.eclipse.persistence.testing.models.jpa.xml.complexaggregate.CountryDweller=[name], org.eclipse.persistence.testing.models.jpa.xml.complexaggregate.Vitals=[ org.eclipse.persistence.testing.models.jpa.xml.complexaggregate.PersonalVitals] }
- To
{ org.eclipse.persistence.testing.models.jpa.xml.complexaggregate.CountryDweller=[name], org.eclipse.persistence.testing.models.jpa.xml.complexaggregate.Vitals=[ org.eclipse.persistence.testing.models.jpa.xml.complexaggregate.PersonalVitals, org.eclipse.persistence.testing.models.jpa.xml.complexaggregate.TeamVitals] }
R1:I3: Check mapping.isPrimaryKeyMapping()
- Here during AttributeImpl construction we lazy load this boolean directly onto the attribute and bypass checking the mapping or descriptor in the future (until the metamodel is optionally refreshed).
R1:I4: Check for more than one CMP3Policy.keyClassField on the descriptor
this SingularAttributeImpl<X,T> (id=185) descriptor RelationalDescriptor (id=139) alias "EnclosureMetamodel" (id=207) cmpPolicy CMP3Policy (id=216) keyClassFields CMPPolicy$KeyElementAccessor[3] (id=225) [0] CMP3Policy$FieldAccessor (id=6049) [1] CMP3Policy$FieldAccessor (id=6050) [2] CMP3Policy$FieldAccessor (id=6051) pessimisticLockingPolicy null pkClass Class<T> (org.eclipse.persistence.testing.models.jpa.metamodel.EnclosureIdClassPK) id=229)
Issues: 74
- 1) The specification of the hasSingleIdAttribute() is vague on what to do if there is no @Id.
- This should not be an issue because we are only dealing with IdentifiableTypes which are Entities and MappedSuperclasses - not Basic, Embeddable and transient types - therefore we will always see an @Id of some type.
- 2) An IdentifiableType with an @IdClass can be have a single @Id - discussed this with Chris.
- The issue is that in my example I use an @IdClass that defines 3 @Id attributes - but we can have one - therefore this function may "return an ambiguous false-positive result" - in the words of Chris
Solution 74: - Fixed
- See SVN Rev# 5303
DI 75: 20090909: Implement IdentifiableType.getVersion()
Solution 75: - Fixed
- See SVN Rev# 5303
- See solution for design Issue 74
DI 76: 20090909: Implement IdentifiableType.getDeclaredVersion()
Solution 76: - Fixed
- See SVN Rev# 5303
DI 77: 20090909: Implement IdentifiableType.hasVersionAttribute()
- See SVN Rev# 5303
Solution 77: - Fixed
DI 78: 20090909: Composite @IdClass on inherited MappedSuperclass chain causes new ValidationException
- See 288972
- We are now throwing a validation exception after the metadata changes to preprocess the metamodel descriptors for MappedSuperclasses in the case where there is an @IdClass in a MappedSuperclass above an @Id - this is invalid and used to be ignored.
- We are performing more validation as a result of our expansion of descriptor creation for MappedSuperclass - for Metamodel consumption.
- This will manifest without a em.getMetamodel() because the metamodel pre-processing always occurs in step 1 and 3 of metadata processing - just in case we require it later - as we discard the transient metadata.
- See the following SVN Rev#'s to start
- http://fisheye2.atlassian.com/changelog/~author=mobrien/eclipselink/?cs=4587
- http://fisheye2.atlassian.com/browse/eclipselink/trunk/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metadata/accessors/classes/MappedSuperclassAccessor.java?r1=4500&r2=4587
- http://fisheye2.atlassian.com/browse/eclipselink/trunk/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metadata/MetadataProject.java?r1=4553&r2=4587
- http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_6:_MappedSuperclass_does_not_currently_have_a_Descriptor
- Section 5 of http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#20090624:_Code_Review_with_Guy.2C_Gordon
- Also, if using maps refer to the following but note that no metadata changes were made.
- Some of the changes were required in order to handle the non-relational aspects of creating a RelationalDescriptor for a MappedSuperclass.
- We should do a second review of the use of Void as the default parameterized type (a copy of an existing workaround already in the code).
- See change to handle composite primary keys
Model:
@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) // Note: DI We do not check the values of the join column names - they can be anything @JoinTable(name="CMP3_MM_HIST_EMPLOY", joinColumns = {@JoinColumn(name="PERSON_ID", referencedColumnName="PERSON_ID"), @JoinColumn(name="DESIGNER_ID", referencedColumnName="DESIGNER_ID")}, inverseJoinColumns = {@JoinColumn(name="PERSON_ID", referencedColumnName="PERSON_ID"), @JoinColumn(name="DESIGNER_ID", referencedColumnName="DESIGNER_ID")}) private Collection<Manufacturer> historicalEmployers = new ArrayList<Manufacturer>(); } @MappedSuperclass public abstract class Designer extends Person { @Id @Column(name="DESIGNER_ID") private Integer ident; } @Entity(name="HardwareDesignerMetamodel") @Table(name="CMP3_MM_HWDESIGNER") public class HardwareDesigner extends Designer implements java.io.Serializable {}
Analysis:
General Model: MappedSuperclassRoot (3 @Id)
- MappedSuperclassCenter (1 @Id) - EntityRoot (0 @Id - by spec)
Q1) Where do we place the @IdClass annotation when an @IdClass has its @Id mappings distributed among multiple @MappedSuperclass abstract classes? - on both mappedSuperclasses
Q2) If in Q1) we the MSCenter class has 1 @Id defined as part of an @IdClass - is it safe to say that it has a single Id mapping?
This is more of an internal API question since we don't have 2+ id fields we currently return false for metamodel.IdentifiableType.hasSingleIdAttribute. - we may need to also look at all inherited parents in the hierarchy chain.
Solution 78: - fixed
- Originally we discussed this and determined that this extra validation may not be an issue - we may allow this behavior change to proceed.
- However at the end of Oct 2009 we decided to remove the validation for the case where an @Id is declared above and Entity on an inherited @MappedSuperclass
20091120:2200 the model is wrong
- We must ensure that we have access to the IdClass attribute where we define @IdClass.
In the case of the model above - the 4th @Id on the middle subclass mappedSuperclass is not accessible from the top root.
- Model
@MappedSuperclass @IdClass(org.eclipse.persistence.testing.models.jpa.metamodel.MSIdClassPK.class) // invalid - should be one level down public abstract class MS_MS_Entity_Root implements java.io.Serializable { @Id protected String type; @Id protected String length; @Id protected String width; } @MappedSuperclass public abstract class MS_MS_Entity_Center extends MS_MS_Entity_Root { > @Id protected Integer ident; } @Entity(name="MS_MS_EntityLeafMetamodel") public class MS_MS_Entity_Leaf extends MS_MS_Entity_Center {}
- should be
@MappedSuperclass public abstract class MS_MS_Entity_Root implements java.io.Serializable { @Id protected String type; @Id protected String length; @Id protected String width; } @MappedSuperclass @IdClass(org.eclipse.persistence.testing.models.jpa.metamodel.MSIdClassPK.class) // or defined on the entity public abstract class MS_MS_Entity_Center extends MS_MS_Entity_Root { > @Id protected Integer ident; } @Entity(name="MS_MS_EntityLeafMetamodel") public class MS_MS_Entity_Leaf extends MS_MS_Entity_Center {}
DI 79: 20090910: MapAttribute keyType requires @MapKey handler in the template case when CMP3Policy.getPKClass() is null
- Ran into this on the advanced model when I ran into bug# 289108.
- The solution is to get the @MapKey also in this case via the attributeClassification and default to the owning Type's PK in the absence of a @MapKey.
// 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();
member MapAttributeImpl<X,K,V> (id=6560) elementType EmbeddableTypeImpl<X> (id=6575) keyType BasicTypeImpl<X> (id=6569) javaClass null managedType EntityTypeImpl<X> (id=6535) mapping AggregateCollectionMapping (id=6519)
- on the Attribute and Type
MapAttributeImpl[org.eclipse.persistence.mappings.AggregateCollectionMapping[redStripes]]
EntityTypeImpl@30704835:BeerConsumer [ javaType: class org.eclipse.persistence.testing.models.jpa.inherited.BeerConsumer descriptor: RelationalDescriptor(org.eclipse.persistence.testing.models.jpa.inherited.BeerConsumer --> [DatabaseTable(CMP3_CONSUMER)]), mappings: [org.eclipse.persistence.mappings.DirectToFieldMapping[id-->CMP3_CONSUMER.ID], org.eclipse.persistence.mappings.DirectToFieldMapping[version-->CMP3_CONSUMER.VERSION], org.eclipse.persistence.mappings.DirectToFieldMapping[name-->CMP3_CONSUMER.NAME], org.eclipse.persistence.mappings.OneToManyMapping[coronaBeersToConsume], org.eclipse.persistence.mappings.OneToManyMapping[blueBeersToConsume], org.eclipse.persistence.mappings.OneToManyMapping[telephoneNumbers], org.eclipse.persistence.mappings.OneToManyMapping[certifications], org.eclipse.persistence.mappings.OneToManyMapping[heinekenBeersToConsume], org.eclipse.persistence.mappings.OneToManyMapping[blueLightBeersToConsume], org.eclipse.persistence.mappings.OneToManyMapping[alpineBeersToConsume], org.eclipse.persistence.mappings.OneToManyMapping[becksBeersToConsume], org.eclipse.persistence.mappings.OneToManyMapping[canadianBeersToConsume], org.eclipse.persistence.mappings.AggregateCollectionMapping[redStripes]]]
Reproduction:
- A Map where the mapKey is templated is declared on a Entity root and is being processed 2 levels down on another inheriting Entity where a MappedSuperclass is between them in the following E-->MS-->E inheritance chain.
- See the following model
BeerConsumer(id) : Entity -->private Map<T, RedStripe> redStripeBeersToConsume; @ElementCollection // TODO: Correct resolving the T type without specifying the map key class // Map key class will get figured out through generic types. @MapKeyClass(String.class) @MapKeyColumn(name="RS_KEY") @CollectionTable(name="CONSUMER_REDSTRIPES", joinColumns=@JoinColumn(name="C_ID", referencedColumnName="ID")) public Map<T, RedStripe> getRedStripes() { return redStripeBeersToConsume; } RatedBeerConsumer : MappedSuperclass NoviceBeerConsumer : Entity
Solution:
- When the pkClass is null on the elementDescriptor CMPPolicy - use the attributeClassification to get the type from the @MapKey (and default to the owning entity PK when there is no @MapKey).
protected MapAttributeImpl(ManagedTypeImpl<X> managedType, CollectionMapping mapping, boolean validationEnabled) { 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(); --> new block if(null == javaClass) { // check for a @MapKeyClass annotation 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) { if(null != descriptor.getCMPPolicy()) { javaClass = ((DatabaseMapping)mapKeyMapping).getAttributeClassification(); if(null == javaClass) { // Default to the PK of the owning descriptor when no MapKey is specified javaClass = descriptor.getCMPPolicy().getPKClass();
DI 80: 20090914: MappedSuperclassTypeImpl.create() does not use ConversionManager to get the EE classLoader
- See Bug 266912 patch in comment 114
- See Bug 266912 patch in comment 116 for EE testing capability
- In MappedSuperclassTypeImpl.create() we are not using a classLoader that is shared library friendly.
relationalDescriptor.convertClassNamesToClasses(metamodel.getSession().getActiveSession().getClass().getClassLoader());
- Should be
relationalDescriptor.convertClassNamesToClasses(metamodel.getSession().getDatasourcePlatform().getConversionManager().getLoader());
Reproduction:
- Reproduction with old code - not EE friendly
- I added a @MappedSuperclass and a em.getMetamodel() call to the @Local @Stateless session bean in the WebLogic tutorial and we fail using the existing classLoader call.
- Before using
relationalDescriptor.convertClassNamesToClasses(metamodel.getSession().getActiveSession().getClass().getClassLoader());
Exception [EclipseLink-7198] (Eclipse Persistence Services - 2.0.0.qualifier): org.eclipse.persistence.exceptions.ValidationException Exception Description: Class: [org.eclipse.persistence.example.jpa.server.business.CellMS] was not found while converting from class names to classes. Internal Exception: java.lang.ClassNotFoundException: org.eclipse.persistence.example.jpa.server.business.CellMS Daemon Thread [[ACTIVE] ExecuteThread: '1' for queue: 'weblogic.kernel.Default (self-tuning)'] (Suspended) RelationalDescriptor(ClassDescriptor).convertClassNamesToClasses(ClassLoader) line: 1209 MappedSuperclassTypeImpl<X>.create(MetamodelImpl, RelationalDescriptor) line: 88 ManagedTypeImpl<X>.create(MetamodelImpl, RelationalDescriptor) line: 414 MetamodelImpl.initialize() line: 294 MetamodelImpl.<init>(DatabaseSession) line: 88 MetamodelImpl.<init>(EntityManagerFactory) line: 98 EntityManagerFactoryImpl.getMetamodel() line: 496 EntityManagerImpl.getMetamodel() line: 2054 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 TransactionalEntityManagerProxyImpl(BasePersistenceContextProxyImpl).invoke(Object, Method, Object[], Transaction) line: 90 TransactionalEntityManagerProxyImpl.invoke(Object, Method, Object[], Transaction) line: 89 TransactionalEntityManagerProxyImpl(BasePersistenceContextProxyImpl).invoke(Object, Method, Object[]) line: 80 TransactionalEntityManagerProxyImpl.invoke(Object, Method, Object[]) line: 24 $Proxy71.getMetamodel() line: not available ApplicationService_5ptwty_Impl(ApplicationService).verifyMetamodel() line: 51 ApplicationService_5ptwty_Impl(ApplicationService).insertObjects(List<Cell>) line: 65 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 AopUtils.invokeJoinpointUsingReflection(Object, Method, Object[]) line: 310 ReflectiveMethodInvocation.invokeJoinpoint() line: 182 ReflectiveMethodInvocation.proceed() line: 149 DelegatingIntroductionInterceptor.doProceed(MethodInvocation) line: 131 DelegatingIntroductionInterceptor.invoke(MethodInvocation) line: 119 ReflectiveMethodInvocation.proceed() line: 171 MethodInvocationVisitorImpl.visit() line: 37 EnvironmentInterceptorCallbackImpl.callback(MethodInvocationVisitor) line: 54 EnvironmentInterceptor.invoke(MethodInvocation) line: 50 ReflectiveMethodInvocation.proceed() line: 171 ExposeInvocationInterceptor.invoke(MethodInvocation) line: 89 ReflectiveMethodInvocation.proceed() line: 171 DelegatingIntroductionInterceptor.doProceed(MethodInvocation) line: 131 DelegatingIntroductionInterceptor.invoke(MethodInvocation) line: 119 ReflectiveMethodInvocation.proceed() line: 171 JdkDynamicAopProxy.invoke(Object, Method, Object[]) line: 204 $Proxy72.insertObjects(List) line: not available ApplicationService_5ptwty_ApplicationServiceLocalImpl.insertObjects(List<Cell>) line: 306 FrontController.generateGlider(PrintWriter) line: 237 FrontController.processGliderCommand(HttpServletRequest, HttpServletResponse, PrintWriter) line: 290 FrontController.processDemoCommand(HttpServletRequest, HttpServletResponse, PrintWriter) line: 359 FrontController.processAction(HttpServletRequest, HttpServletResponse) line: 388 FrontController.doGet(HttpServletRequest, HttpServletResponse) line: 459 FrontController(HttpServlet).service(HttpServletRequest, HttpServletResponse) line: 707 FrontController(HttpServlet).service(ServletRequest, ServletResponse) line: 820 StubSecurityHelper$ServletServiceAction.run() line: 227 StubSecurityHelper.invokeServlet(ServletRequest, HttpServletRequest, ServletRequestImpl, ServletResponse, HttpServletResponse, Servlet) line: 125 ServletStubImpl.execute(ServletRequest, ServletResponse, FilterChainImpl) line: 292 ServletStubImpl.execute(ServletRequest, ServletResponse) line: 175 WebAppServletContext$ServletInvocationAction.run() line: 3586 AuthenticatedSubject.doAs(AbstractSubject, PrivilegedAction) line: 321 SecurityManager.runAs(AuthenticatedSubject, AuthenticatedSubject, PrivilegedAction) line: 121 WebAppServletContext.securedExecute(HttpServletRequest, HttpServletResponse, boolean) line: 2196 WebAppServletContext.execute(ServletRequestImpl, ServletResponseImpl) line: 2102 ServletRequestImpl.run() line: 1428 ExecuteThread.execute(Runnable) line: 201 ExecuteThread.run() line: 173
Solution:
- With the ConversionManager fix - we create the MappedSuperclass ok now
- After using
relationalDescriptor.convertClassNamesToClasses(metamodel.getSession().getDatasourcePlatform().getConversionManager().getLoader());
- Note: I used to use getActiveSession() instead of getSession() as this will handle any JTS exteranal transaction if it exists - however a simple getSession() is all that is required.
/** * PUBLIC: * Return the active session for the current active external (JTS) transaction. * This should only be used with JTS and will return the session if no external transaction exists. */ public org.eclipse.persistence.sessions.Session getActiveSession() {
####<Sep 14, 2009 11:24:24 AM EDT> <Notice> <Stdout> <mfobrien-q4> <AdminServer> <[ACTIVE] ExecuteThread: '1' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1252941864996> <BEA-000000> <[EL Example]: enterprise: Metamodel: MetamodelImpl@29414608 [ 4 Types: , 2 ManagedTypes: , 1 EntityTypes: , 1 MappedSuperclassTypes: , 0 EmbeddableTypes: ]>
this MetamodelImpl (id=10853) mappedSuperclasses LinkedHashSet<E> (id=10871) map LinkedHashMap<K,V> (id=10874) size 1 table HashMap$Entry<K,V>[16] (id=10876) [8] LinkedHashMap$Entry<K,V> (id=10883) key MappedSuperclassTypeImpl<X> (id=10877) descriptor RelationalDescriptor (id=10864) inheritingIdentifiableTypes HashMap<K,V> (id=10878) size 1 table HashMap$Entry<K,V>[16] (id=10887) key Class<T> (org.eclipse.persistence.example.jpa.server.business.Cell) (id=10882) value EntityTypeImpl<X> (id=10858) javaClass Class<T> (org.eclipse.persistence.example.jpa.server.business.CellMS) (id=10879)
DI 81: 20090914: Implement @BasicMap DirectMapContainerPolicy support in MapAttributeImpl
- Refer to ManagedTypeImpl.initialize()
- I currently support all Map policies where isMapPolicy() returns true.
- There currently is no support for @BasicMap in because I was assuming that a call to CollectionContainerPolicy.isMapPolicy() returned true for DirectMapContainerPolicy.
Analysis:
- Change
public abstract class ManagedTypeImpl<X> extends TypeImpl<X> implements ManagedType<X> { protected void initialize() { for (DatabaseMapping mapping : getDescriptor().getMappings()) { if (mapping.isCollectionMapping()) { ContainerPolicy collectionContainerPolicy = colMapping.getContainerPolicy(); ---> if (collectionContainerPolicy.isMapPolicy()) {
- To
public abstract class ManagedTypeImpl<X> extends TypeImpl<X> implements ManagedType<X> { protected void initialize() { for (DatabaseMapping mapping : getDescriptor().getMappings()) { if (mapping.isCollectionMapping()) { ContainerPolicy collectionContainerPolicy = colMapping.getContainerPolicy(); ---> if (collectionContainerPolicy.isMapPolicy() || collectionContainerPolicy.isDirectMapPolicy()) {
- As well as
protected MapAttributeImpl(ManagedTypeImpl<X> managedType, CollectionMapping mapping, boolean validationEnabled) { //MapContainerPolicy policy = (MapContainerPolicy) mapping.getContainerPolicy(); ContainerPolicy policy = mapping.getContainerPolicy();
Solution 81 - Fixed:
- See SVN rev# 5148
DI 82: 20090914: Verify all is*Policy() calls for MapAttributeImpl instances
In ManagedTypeImpl.initialize() we need to verify handlers for all possible is*Policy() calls.
Analysis
Solution:
DI 83: 20090914: MapAttributeImpl.elementType incorrectly set when @ObjectTypeConverter is present
- see bug# 289487
- In the case where we have a @BasicMap with an @ObjectTypeConverter we end up with a valueField.type set to the database field (in this case String) and the valueField.typeName set to the object type of Long.
Analysis:
- I currently key off the valueField.type when setting the elementType - I should be using the attributeClassification but none exists for DirectCollectionMapping.
Solution: 83 - Fixed
- After discussing this with Guy, Gordon and Peter - We will add attributeClassification to DirectCollectionMapping
- Note: For XML processing and the Mapping Workbench - this introduction of a new field on DirectCollectionMapping may need to be picked up.
- See SVN rev# 5189
DI 84: 200909015: MapAttribute missing support for UC8: @MapKey with default name attribute
Analysis:
// UC8: no targetEntity, MapKey uses name default @OneToMany(cascade=ALL, mappedBy="mappedEmployerUC8") @MapKey // name attribute will default to "id" private Map<Integer, HardwareDesigner> hardwareDesignersMapUC8;
Solution:
DI 85: 20090916: Relax IllegalArgumentException checking on autoboxed primitives
- This one found by Chris. Our current IAE check on functions like getSingularAttribute will throw an IAE on primitive attributes that have their type passed in as the boxed object wrapper class.
- For example, if i have int id and I execute the function aManagedType.getSingularAttribute("name", Integer.class) - then an IAE will be thrown.
Analysis 85:
/** * Return the single-valued attribute of the managed * type that corresponds to the specified name and Java type * in the represented type. * @param name the name of the represented attribute * @param type the type of the represented attribute * @return single-valued attribute with given name and type * @throws IllegalArgumentException if attribute of the given * name and type is not present in the managed type */ public <Y> SingularAttribute<? super X, Y> getSingularAttribute(String name, Class<Y> type) { SingularAttribute<? super X, Y> anAttribute = (SingularAttribute<? super X, Y>)getSingularAttribute(name); Class<Y> aClass = anAttribute.getType().getJavaType(); -->relax if(type != aClass) { throw new IllegalArgumentException(ExceptionLocalization.buildMessage( "metamodel_managed_type_attribute_type_incorrect", new Object[] { name, this, type, aClass })); } return anAttribute; }
Solution 85: - Fixed
- We may want to relax primitive checking to not throw an IAE if the type is either the primitive class or its boxed type - int.class or Integer.class
- This work is slated for review and completion next week after the remaining IdentifiableTypeImpl functions are finished.
- 20090918: We have decided that we will relax the IAE rules and treat boxed classes the same as their primitive analogues - and not throw and IAE.
- 20090929: see SVN Rev# 5347
DI 86: 20090921: Handle Embeddable Type keyType in MapAttributeImpl constructor
- See SVN rev# 5761
- See changes to keyType handling in DI 98
- In the MapAttributeImpl we determine the keyType for the Map - support for embeddables may be missing here.
- We also need to verity that elementType processing for maps is sufficient in the super PluralAttributeImpl constructor.
Analysis 86:
- 20091105: starting investigation after discussion with Mitesh.
- It looks like support for an Entity keyType needs to be modified.
Model Extensions: 86
- We need to add the following extensions to our model in order to verify functionality
- Enclosure (existing) already uses EnclusureIdClassPK as its IdClass
@Entity(name="ManufacturerMetamodel") public class Manufacturer extends Corporation implements java.io.Serializable{ ... // UC9: no targetEntity, no MapKey, but generics are set (MapKey has an IdClass with an Embeddable) @OneToMany(cascade=CascadeType.ALL, mappedBy="mappedManufacturerUC9") ---> new private Map<Board, Enclosure> enclosureByBoardMapUC9; } @Entity(name="EnclosureMetamodel") @IdClass(org.eclipse.persistence.testing.models.jpa.metamodel.EnclosureIdClassPK.class) public class Enclosure implements java.io.Serializable { ... @Id @Column(name="TYPE") public String type; @Id @Column(name="LENGTH") protected String length; @Id @Column(name="WIDTH") private String width; // The M:1 side is the owning side @Id @ManyToOne @JoinColumn(name="PERSON_ID") ---> new private Manufacturer mappedManufacturerUC9; } public class EnclosureIdClassPK { ... public String type; protected String length; private String width; ---> new private Integer mappedManufacturerUC9; }
Testing before change
The following test passes with the change submitted in the DI86 patch below (without this fix we get the following test failure with the new model change above)
TEST SUITE NAME: org.eclipse.persistence.testing.tests.jpa.metamodel.MetamodelMetamodelTest TEST NAME: testMapAttribute_getKeyJavaType_UC9_DI86_Embeddable_IdClass_keyType_Method(org.eclipse.persistence.testing.tests.jpa.metamodel.MetamodelMetamodelTest) ##FAILURE## RESULT: Error (failure) junit.framework.AssertionFailedError: expected:<class org.eclipse.persistence.testing.models.jpa.metamodel.Board> but was:<class java.lang.Integer> at junit.framework.Assert.fail(Assert.java:47) at junit.framework.Assert.failNotEquals(Assert.java:277) at junit.framework.Assert.assertEquals(Assert.java:64) at junit.framework.Assert.assertEquals(Assert.java:71) at org.eclipse.persistence.testing.tests.jpa.metamodel.MetamodelMetamodelTest.testMapAttribute_getKeyJavaType_UC9_DI86_Embeddable_IdClass_keyType_Method(MetamodelMetamodelTest.java:4502)
Solution 86: - fixed
- See DI 86 patch in SVN rev# 5761
- The changes essentially involves adding support for an Entity @MapKey by remove the 2nd call to getOwningPKTypeWhenMapKeyAnnotationMissingOrDefaulted when policyKeyType is not null - previously I defaulted in this specific case to the PK.
- Remove the 2nd call to getOwningPKTypeWhenMapKeyAnnotationMissingOrDefaulted when policyKeyType is not null.
- Checking code coverage I see 18 hits on 20091023 on line 110 that is slated for removal - but running FullRegressionTestSuite gets no hits on my debugger with the 20091105 view.
- However, later in 2 separate bugs this code was further enhanced to not use the policy.getElementDescriptor() directly and instead use policy is*Policy() calls like isMapPolicy/isDirectMapPolicy/isMappedKeyMapPolicy - reviewed by Guy.
- See later fix in DI 98 in SVN rev# 5776 and 5793
- Regression test case
- MetamodelMetamodelTest.testMapAttribute_getKeyJavaType_UC9_DI86_Embeddable_IdClass_keyType_Method()
DI 87: 20090925: @JoinTable on JPA models is redundant if FK JoinColumn exists
- Currently the join tables I am defining are empty because I am defining both a FK on each table as well as join column to model a bidirectional 1:m relationship.
Solution 87: - Fixed
- Comment out all @JoinColumn annotations on my model.
- In the future after Guy's next feature is complete - we should transition one of the FK join columns back to using a Join table.
DI 88: 20091006: Improve Performance via Caching and Minimal Iteration
- On some legacy machines the metamodel initialization is taking up to 90 seconds. We will identify areas of the Metamodel that can be cached and iterative loops that can be minimized to start
Analysis 88:
- After a talk with Gordon it would be better to move any requirements up back into metadata processing instead of keeping caching it only for the metamodel.
Solution 88:
- In progress 20091005
DI 89: 20091007: Metamodel Implementation must implement Serializable - required by Criteria API
- The Criteria API requires that all Metamodel Impl classes implement Serializable.
Analysis 89:
- See Item 54 of Effective Java by Joshua Bloch p.223.
- "declare an explicit serial version UID in every serializable class you write...If no serial version UID is provided, an expensive computation is required to generate one at run time."
- See the section about Serialization in the SUN JDK Documentation
- "It is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected serialVersionUID conflicts during deserialization, causing deserialization to fail."
- Generate a unique serialVersionUID using the Eclipse 3.5 IDE using Source | Clean Up | Custom profile | Configure | Missing Code | Potential programming problems | Add serial version ID | Generated
- No custom serialization handling with overrides of readObject and writeObject is required at this time.
- How to handle deserialization
- Do we rely on the serialization mechanism or do we implement an ExternalizableDelegator to regenerate the metamodel after readObject completes.
Solution 89: - Fixed
- It is not entirely clear whether inherited abstract classes in the chain must have serialVersionUID - however a change is in that has all root classes in the hierachy implement Serializable and all concrete classes provide their non-default distinct serialVersionUID.
- Fixed in SVN rev# 5506
DI 90: 20091007: Validate and Remove all remaining 12 TODO comments
- Description Resource Path Location Type
- TODO : Check all is*Policy() calls ManagedTypeImpl.java org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel line 1096 Java Task
- TODO : Embeddable Collection handling is in progress ManagedTypeImpl.java org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel line 410 Java Task - finished remove.
- TODO : expand on variant use case IdentifiableTypeImpl.java org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel line 231 Java Task - 0% code coverage - remove case handling
- TODO : handle AggregateCollectionMapping PluralAttributeImpl.java org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel line 77 Java Task
- TODO : Handle EmbeddableType MapAttributeImpl.java org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel line 93 Java Task - 0% code coverage - remove
- TODO : refactor SingularAttributeImpl.java org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel line 119 Java Task - 12 runs in code coverage - change required.
- TODO : REFACTOR PluralAttributeImpl.java org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel line 87 Java Task - 6 runs in code coverage - same change as above required.
- TODO : refactor exception handling PluralAttributeImpl.java org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel line 122 Java Task - 6 runs - same fix as above
- TODO : refactor: default to the managedType PluralAttributeImpl.java org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel line 111 Java Task - 0% code coverage - invalid - remove case code line.
- TODO : System.out.println("_Warning: defaulting to non-Set specific Collection type on " + colMapping); ManagedTypeImpl.java org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel line 1272 Java Task - implement a finer log.
- TODO : System.out.println("_Warning: type is null on " + colMapping); ManagedTypeImpl.java org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel line 1084 Java Task - same as above - implement a real log message
- TODO : verify handling of XML mapped non-Entity (plain Java Class) inherited mappings ManagedTypeImpl.java org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel line 1069 Java Task - remove - this is a future design enhancement in DI 53
- TODO : verify that all entities or'd with embeddables matches the number of types MetamodelImpl.java org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel line 311 Java Task - simple to implemement - or just remove
- TODO : verify use case ManagedTypeImpl.java org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel line 1045 Java Task - has 57 hits in code-coverage - identify test case - and remove todo
DI 91: 20091008: ManagedType.getDeclaredAttribute() does not throw expected IAE for Entity(target)-MappedSuperclass-MappedSuperclass(attribute) Hierarchy
- The logic of the recursive call in getDeclaredAttribute works fine but it must be extended to be able to be used by getDeclaredId and getDeclaredVersion - in this case we already know that there is an attribute on the current target leaf element that we start the recursive search up to the root.
- I found this when I did not return an expected IAE when testing the variant use case for IdentifiableType.getDeclaredId() where the id is declared 2 levels above on the Entity-MappedSuperclass-MappedSuperclass inheritance chain.
Analysis 91:
Solution 91: - Fixed
- The fix is simple and is along the lines that Chris discussed for a similar issue with the recursive handler in ManagedTypeImpl during a review a couple months ago.
- Reverse the if check against the recursive call return in getDeclaredAttribute()
ManagedTypeImpl public Attribute<X, ?> getDeclaredAttribute(String name){ // get the attribute parameterized by <Owning type, return Type> - throw an IAE if not found (no need to check hierarchy) // Handles UC1 and UC2 Attribute<X, ?> anAttribute = getAttribute(name); // If an Attribute is found then check the hierarchy for a declaration in the superclass(s) // Keep moving up only when the attribute is not found ManagedTypeImpl aManagedSuperType = getManagedSuperType(); if(null == aManagedSuperType) { return anAttribute; } else { // keep checking the hierarchy but skip this level --add ! if(!aManagedSuperType.isAttributeDeclaredOnlyInLeafType(name)) { // Handles UC4 and UC5 - throw an IAE if the class is declared above throw new IllegalArgumentException(ExceptionLocalization.buildMessage( "metamodel_managed_type_declared_attribute_not_present_but_is_on_superclass", new Object[] { name, this })); } else { // Handles UC3 (normal case - attribute is not declared on a superclass) return anAttribute; } } }
- However there is more to this - we also need to pass in the original attribute found on the leaf - if found - for comparison.
- Fixed in SVN rev# 5506 and 5509
DI 92: 20091008: Move metamodel instance field from EntityManagerFactory to EntityManagerSetupImpl
- This will solve any GC issues that arise
- Currently the metamodel field is part of the EntityManagerFactoryImpl - it would be better to place this at the end of the deploy in EntityManagerSetupImpl - Gordon.
Analysis 92:
- Validation or not - metamodel is initialized
- The EntityManagerSetupImpl does not have an accessor on EntityManagerFactoryImpl - therefore I will leave the non-spec. setMetamodel() function used to reset the metamodel for testing as an internal public function on EntityManagerFactoryImpl and not move it to EntityManagerSetupImpl as well.
- We have the following new chain of execution that will initialize the metamodel just after deploy()
Thread [Thread-3] (Suspended (breakpoint at line 1930 in EntityManagerSetupImpl)) EntityManagerSetupImpl.getMetamodel() line: 1930 EntityManagerSetupImpl.deploy(ClassLoader, Map) line: 301 EntityManagerFactoryImpl.getServerSession() line: 160 EntityManagerFactoryImpl.createEntityManagerImpl(Map) line: 216 EntityManagerFactoryImpl.createEntityManager() line: 204 JUnitTestCase.getServerSession(String) line: 340 JUnitTestCase.getEntityManagerFactory(String, Map) line: 365 JUnitTestCase.getEntityManagerFactory(String) line: 344 JUnitTestCase.getServerSession(String) line: 340 MetamodelMetamodelTest(MetamodelTest).setUp() line: 46 MetamodelMetamodelTest.setUp() line: 134
Solution 92: - Fixed
- Reviewed and discussed with Chris - See SVN rev# 5541.
- 20091214 - part of the solution around initialization has been modified as part of bug# 297555 which will be further refined by enhancement# 297748 - a try/catch wrapper around any CCE or spec defined IAE that will allow all deployment scenarios to proceed to completion - regardless of whether the metamodel is correct or not.
DI 93: 20091014: IdentifiableTypeImpl.getIdType() assumes all Id mappings are DerivedId mappings
- The IdentifiableType.getIdType() function assumes that all Entity types have a CMPPolicy set on their descriptor. This occurs because currently even for BasicType Id's (which are not DerivedId's) have the following line.
BasicType.java public void process() { ... // Derived ID: set if this mapping has been marked as an ID. mapping.setIsDerivedIdMapping(isId());
- The getIdType() function has special handling for our pseudo descriptor MappedSuperclasses - where the CMPPolicy is null - otherwise they just check the CMPPolicy for entities.
- The problem is that I am getting a NPE if there is no CMPPolicy on the mapping - as a possible refactor of the metadata code uncovered.
- The second part of the code for single key support is outside of a null check on the CMPPolicy - this will cause a NPE in some cases - and has for upcoming changes to Derived Id work.
public Type<?> getIdType() { CMPPolicy cmpPolicy = getDescriptor().getCMPPolicy(); if (null == cmpPolicy) { // Composite key support (IE: @EmbeddedId) ... } // Single Key support using any Java class - built in or user defined // There already is an instance of the PKclass on the policy NPE--> if (cmpPolicy.isCMP3Policy()) { // BasicType, EntityType or IdentifiableType are handled here, lookup the class in the types map and create a wrapper if it does not exist yet return this.metamodel.getType(((CMP3Policy) cmpPolicy).getPKClass()); } }
Solution 93: - Fixed
- For now we will not force all Id mappings to have a CMPPolicy - this is a future todo.
- We will however introduce a new isJPAId() flag for basic type id's to replace using isDerivedId() in all cases - Guy is checking this in as of 20091015.
- For the future we need to implement custom initialization code for our MappedSuperclass Decriptors that sets the CMPPolicy on the descriptor and initializes the pkClass - see the comment in EntityManagerSetupImpl.assignCMPPolicy().
DI 94: 20091015: Split and Granularize Test Suite
- Historically, the test model runs as a second priority to getting the implementation stable - I don't agree with this, and now that the implementation is complete and relatively stable - we can fix up the test suite.
- The test suite currently consists of 8 small tests and 1 massive 3000 line test case that excercises a majority of all the interface functions.
- The biggest issue is if one part of the massive test case fails - the remaining part does not run. Also it is difficult to track the failure down - the stack trace must be used instead of a more traditional report format.
- This suite needs to be split up into 100-200 test cases so we have more
Solution 94:
- In progress in parallel with other issues
DI 95: 20091017: Attribute.getJavaMember() returns null for a BasicType on a MappedSuperclass because of an uninitialized accessor
- Fixed in bug# 322585 in SVN rev # 8141 (2.2) and 8141 (2.1.2)in Design Issue 106, Design Issue 101and Design Issue 95.
- Found this one while testing code coverage for Attribute.getJavaMember.
- It looks like we will need to add to the initialization of the descriptor for MappedSuperclasses that is pending for DI 93 - pkClass initialization in CMP3Policy.
/** * Return the <code>java.lang.reflect.Member</code> for the represented * attribute. * @return corresponding <code>java.lang.reflect.Member</code> */ java.lang.reflect.Member getJavaMember();
- The AttributeAccessor on the DirectToField" Mapping is missing the attributeField required by getJavaMember().
this SingularAttributeImpl<X,T> (id=70) elementType BasicTypeImpl<X> (id=81) managedType MappedSuperclassTypeImpl<X> (id=54) mapping DirectToFieldMapping (id=84) attributeAccessor InstanceVariableAttributeAccessor (id=92) attributeField null attributeName "id" (id=95) isReadOnly false isWriteOnly false attributeClassification null attributeClassificationName null attributeName "id" (id=95) attributeObjectClassification null
- The workaround is to use our own java reflection code on the class or get the member from an inheriting subclass - in the case of a MappedSuperclass managingType.
Solution 95:
Good 95:
- Get the attribute Field from the inheriting subclass.
public Member getJavaMember() { AttributeAccessor accessor = getMapping().getAttributeAccessor(); if (accessor.isMethodAttributeAccessor()) { return ((MethodAttributeAccessor) accessor).getGetMethod(); } Member aMember = ((InstanceVariableAttributeAccessor) accessor).getAttributeField(); // For primitive and basic types - we should not return null - the attributeAccessor on the MappedSuperclass is not initialized // MappedSuperclasses need special handling to get their type from an inheriting subclass if(null == aMember) { if(this.getManagedTypeImpl().isMappedSuperclass()) { // get inheriting subtype member (without handling @override annotations) AttributeImpl inheritingTypeMember = ((MappedSuperclassTypeImpl)this.getManagedTypeImpl()) .getMemberFromInheritingType(mapping.getAttributeName()); // Verify we have an attributeAccessor aMember = ((InstanceVariableAttributeAccessor)inheritingTypeMember.getMapping() .getAttributeAccessor()).getAttributeField(); } } return aMember; }
aMember Field (id=145) annotations (id=151) clazz Class<T> (org.eclipse.persistence.testing.models.jpa.metamodel.Person) (id=91) declaredAnnotations null fieldAccessor null genericInfo null modifiers 2 name "id" (id=136) override true overrideFieldAccessor null root Field (id=155) securityCheckCache null securityCheckTargetClassCache null signature null slot 0 type Class<T> (java.lang.Integer) (id=108)
- The problem with this code is it does not account for Attribute Overrides on the inheriting Entity class or class subtree.
Better 95:
- Initialize the MappedSuperclass descriptor.
DI 96: 20091019: Attribute.getPersistentAttributeType() treats ManyToOne the same as OneToOne
- This one occurred because we treat ManyToOne the same as OneToOne internally by design
- We solve this by either checking for Join columns or by storing a map of JPA accessors for later use by AttributeImpl.getPersistentAttributeType().
- However we need to work on the MANY_TO_MANY and ELEMENT_COLLECTION case - as they do not have coverage yet.
- This issue actually has 2 parts
- We need a way to differentiate when a OneToOne was defined as a ManyToOne and also when a ManyToMany was defined as a OneToMany.
Analysis 96:
- Search for the join table on the ManyToOne mapping.
- For example we have the following relationTable:DatabaseTable on the OneToOneMapping that flags this mapping as an actual ManyToOneMapping
mapping OneToOneMapping (id=84) mechanism RelationTableMechanism (id=116) deleteQuery DataModifyQuery (id=184) hasCustomDeleteQuery false hasCustomInsertQuery false insertQuery DataModifyQuery (id=187) lockRelationTableQuery null relationTable DatabaseTable (id=188) name "CMP3_MM_COMPUTER_MM_BOARD" (id=195) qualifiedName "CMP3_MM_COMPUTER_MM_BOARD" (id=195) tableQualifier "" (id=196) uniqueConstraints Vector<E> (id=197) useDelimiters false
@Entity(name="BoardMetamodel") @Table(name="CMP3_MM_BOARD") public class Board implements java.io.Serializable{ // The M:1 side is the owning side for "circuitBoards" @ManyToOne(fetch=EAGER) @JoinTable(name="CMP3_MM_COMPUTER_MM_BOARD", joinColumns = @JoinColumn(name="BOARD_ID"), inverseJoinColumns = @JoinColumn(name="COMPUTER_ID")) private Computer computer; } @Entity(name="ComputerMetamodel") @Table(name="CMP3_MM_COMPUTER") public class Computer implements java.io.Serializable { // Inverse side @OneToMany(cascade=ALL, mappedBy="computer") private Collection<Board> circuitBoards; }
Solution 96:
- I will expand and post a patch shortly but here is the main point
- Store a Map on the (core) Project of Key:DatabaseMapping and target:MappingAccessor
- Use the map to lookup the Accessor based on the mapping on the attribute
- Use this accessor to ask isManyToOne() or isOneToOne()
- 20100419: There is a new ManyToOneMapping in ..
DI 97: 20091102: Add JPA 2.0 @MapKeyClass use cases to test model
- In the same way that we have @MapKey testing on Manufacturer we need to verify MapAttribute processing for @MapKeyClass annotated elements.
Solution 97:
- We extend the existing model where HardwareDesigner and Enclosure are mapped from Manufacturer and Computer.
DI 98: 20091109: MapAttribute keyType processing should offload more to MappedKeyContainerPolicy.keyMapping
- After a discussion about the fix for DI 86 in SVN Rev# 5761 it looks like my fix is doing more than it requires when the key column is not specified and should not be using the CMP3Policy in the case that the keyType is null on the MappedKeyMapContainerPolicy.
- Use cases 1a and 7 have been fixed by deferring to the MappedKeyMapContainerPolicy - for these 2 cases where the mapKey was actually on the attributeClassification
- However, after debugging in detail though the current use cases which cover all scenarios except for one. It looks like we still need special processing for 2 cases in bug # 294765 and bug# 294811 that involve the superclass MapContainerPolicy - therefore some of the existing special handling code for determining the PK class when required will remain.
- Case 1: No keyField set on MapContainerPolicy
- Case 1a: Element class uses an @Id field - we require the mapPolicy.elementDescriptor.cmppolicy.pkClass
- Case 1b: Element class uses an @Id function - we require reflection code
- The following code should be done for me in the MKMCPolicy - but at this time still must exist lower in the Metamodel layer.
- Case 1: No keyField set on MapContainerPolicy
javaClass = policy.getElementDescriptor().getCMPPolicy().getPKClass();
- Normally, I should just be using the key mapping on the MKMCPolicy and let this policy handle corner use cases in the specification for me. But, the way the MapContainerPolicy currently works requires some special handling in MapAttribute.
Analysis 98:
- See section 10.1.26 (@MapKey) and 10.1.27 (@MapKeyClass) of the JSR-317 spec. for details on the two JPA annotations for specifying the Map key in addition to using generics.
- A summary is that either the name of the map key attribute is specified or we use the K in the Map<K,V> declaration to determine the Map key - otherwise we default to the PK.
- Use Case segmentation for keyType
- MapContainerPolicy
- keyField set (lazy loaded)
- UC2 - name attribute defines mapKey, generics are not required and are secondary
- @OneToMany(cascade=ALL, mappedBy="mappedEmployerUC2")
- @MapKey(name="name")
- private Map<String, HardwareDesigner> hardwareDesignersMapUC2;
- UC4 - name attribute defines mapKey, generics are not required
- @OneToMany(targetEntity=HardwareDesigner.class, cascade=ALL, mappedBy="mappedEmployerUC4")
- @MapKey(name="name")
- private Map hardwareDesignersMapUC4;
- UC8 - mapKey defined via generics
- @OneToMany(cascade=ALL, mappedBy="mappedEmployerUC8")
- @MapKey // name attribute will default to "id"
- private Map<Integer, HardwareDesigner> hardwareDesignersMapUC8;
- UC2 - name attribute defines mapKey, generics are not required and are secondary
- Use mapPolicy.elementDescriptor.cmppolicy.pkClass (since KeyField == null)
- UC10 - mapKey defined via generics and is a java class defined as an IdClass on the element(value) class - here Enclosure
- @OneToMany(mappedBy="computer", cascade=ALL, fetch=EAGER)
- @MapKey // key defaults to an instance of the composite pk class
- private Map<EnclosureIdClassPK, Enclosure> enclosures;
- UC10 - mapKey defined via generics and is a java class defined as an IdClass on the element(value) class - here Enclosure
- or (get keyClass from mapping if the Id is a get() function)
- TBD
- keyField set (lazy loaded)
- MappedKeyMapContainerPolicy
- mapKeyTargetType set on the keyMapping - normal processing
- UC9 - mapKey defined by generics in the absence of a MapKey annotation
- @OneToMany(cascade=CascadeType.ALL, mappedBy="mappedManufacturerUC9")
- private Map<Board, Enclosure> enclosureByBoardMapUC9;
- UC9 - mapKey defined by generics in the absence of a MapKey annotation
- Secondary processing for Basic (DirectToField) mappings - use AttributeClassification (since keyMapping.attributeAccessor.attributeClass == null)
- UC1a - used to return the Integer PK
- @OneToMany(cascade=ALL, mappedBy="mappedEmployerUC1a")
- private Map<String, HardwareDesigner> hardwareDesignersMapUC1a;
- UC7 - used to return the Integer PK
- @OneToMany(targetEntity=HardwareDesigner.class, cascade=ALL, mappedBy="mappedEmployerUC7")
- private Map<String, HardwareDesigner> hardwareDesignersMapUC7;
- UC1a - used to return the Integer PK
- mapKeyTargetType set on the keyMapping - normal processing
- MapContainerPolicy
- Note: we will not qualify validation for the following invalid scenarios.
- The name attribute of the MapKey annotation defines a key class different from the one defined in the Map<K,V> generics field.
- When both the name attribute and the Map<K,V> generics fields are specified - internal order of validation is not enforced.
Solution 98:
- See SNV Rev # Rev # 5793 in bug# 294765 for part of the fix for this issue.
- See SVN Rev # 5776 in DI 98: leverage MappedKeyMapContainerPolicy, add basicMap Support, keep 294811 and 294765 woirkarounds for keyType when MapKey not specified
- The following bugs have been entered against foundation
- (fixed) 294765:MapKey keyType DirectToField processing should return attributeClassification class in getMapKeyTargetType when accessor.attributeField is null
- (fixed) 294811:Map keyType Embeddable, Entity or Transient processing requires CMP3Policy workaround when working with a MapContainerPolicy superclass of MappedKeyMapContainerPolicy
- This fix also completes DI 82 above.
- See change to DI 86 below
- before
protected MapAttributeImpl(ManagedTypeImpl<X> managedType, CollectionMapping mapping, boolean validationEnabled) { // Set the managedType (X or owning Type) super(managedType, mapping, validationEnabled); // Override the elementType (V or Map value) // We need to set the keyType Type that represents the type of the Map key for this mapping ContainerPolicy policy = mapping.getContainerPolicy(); ClassDescriptor policyElementDescriptor = policy.getElementDescriptor(); 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 = null; 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(policyElementDescriptor != null && policyElementDescriptor.getCMPPolicy() != null) { javaClass = policy.getElementDescriptor().getCMPPolicy().getPKClass(); } if(null == javaClass) { // check for a @MapKeyClass annotation if(policy.isMappedKeyMapPolicy()) { javaClass = getOwningPKTypeWhenMapKeyAnnotationMissingOrDefaulted( (MappedKeyMapContainerPolicy)policy); } } } } else { if(policyKeyType instanceof ClassDescriptor) { // from AggregateObjectMapping javaClass = ((ClassDescriptor)policyKeyType).getJavaClass(); } else { javaClass = (Class<?>)policyKeyType; } } aKeyType = getMetamodel().getType(javaClass); this.keyType = (Type<K>) aKeyType; }
- after
protected MapAttributeImpl(ManagedTypeImpl<X> managedType, CollectionMapping mapping, boolean validationEnabled) { // Set the managedType (X or owning Type) super(managedType, mapping, validationEnabled); // We need to set the keyType Type that represents the type of the Map key for this mapping ContainerPolicy policy = mapping.getContainerPolicy(); Type<?> aKeyType = null; Class<?> javaClass = null; MapKeyMapping keyMapping = null; ClassDescriptor policyElementDescriptor = policy.getElementDescriptor(); Object policyKeyType = null; // Step 1: We check the Map policy for the keyMapping if(policy.isMapPolicy()) { // There is always a MapPolicy // check for Either a generic Map (MapContainerPolicy) or specific MappedKeyMapContainerPolicy subclass if(policy.isMappedKeyMapPolicy()) { keyMapping = ((MappedKeyMapContainerPolicy)policy).getKeyMapping(); policyKeyType = keyMapping.getMapKeyTargetType(); // DirectToFieldMapping implements MapKeyMapping - here we get the key from the attributeClassification if(null == policyKeyType) { policyKeyType = ((DatabaseMapping)keyMapping).getAttributeClassification(); } } else { // Assume we have a MapContainerPolicy general superclass with a lazy-loaded keyType policyKeyType = policy.getKeyType(); // returns a Class<?> or descriptor (via AggregateObjectMapping) // The keyType will be null on the MapContainerPolicy when the keyField is null } } // Step 2: We determine the java class from the policyKeyType (class or ClassDecriptor) - and perform alternate keyType lookup if(null == policyKeyType) { // The keyType will be null on a MapContainerPolicy when the keyField is null - usually a composite key if(managedType.isIdentifiableType()) { // Use the PK of the element not the one on the managedType // Case: @MapKey private Map<K,V> // no name column specified if(policyElementDescriptor != null && policyElementDescriptor.getCMPPolicy() != null) { javaClass = policy.getElementDescriptor().getCMPPolicy().getPKClass(); } // Case: @MapKey private Map<K,V> // no name column specified and the PK is defined by a method if(null == javaClass) { if(policy.isMappedKeyMapPolicy()) { javaClass = getOwningPKTypeWhenMapKeyAnnotationMissingOrDefaulted( (MappedKeyMapContainerPolicy)policy); } } } } else { // Process the policyKeyType if(policyKeyType instanceof ClassDescriptor) { // from AggregateObjectMapping javaClass = ((ClassDescriptor)policyKeyType).getJavaClass(); } else { javaClass = (Class<?>)policyKeyType; } } // Step 3: We wrap the java type in a Metamodel Type instance or retrieve an existing Type aKeyType = getMetamodel().getType(javaClass); this.keyType = (Type<K>) aKeyType; }
- See 266912 patch 20091109
DI 99: 20091110: Metamodel.types LinkedHashMap appears to have null=null K,V pairs - expected Java SE behavior
- During forensic analysis of the workarounds in DI 98 surrounding bug# 294765 I ran across what looked to be null KV pairs int the Metamodel.types LinkedHashMap - these turned out to be normal after adding debugging code.
MetamodelImpl@7674523 [ 40 Types: , 20 ManagedTypes: , 13 EntityTypes: , 4 MappedSuperclassTypes: , 3 EmbeddableTypes: ] null=null on metamodel MetamodelImpl (id=192) embeddables LinkedHashMap<K,V> (id=315) entities LinkedHashMap<K,V> (id=316) managedTypes LinkedHashMap<K,V> (id=317) mappedSuperclasses LinkedHashSet<E> (id=318) session ServerSession (id=319) types LinkedHashMap<K,V> (id=263) accessOrder false entrySet HashMap$EntrySet (id=352) header LinkedHashMap$Entry<K,V> (id=270) keySet null loadFactor 0.75 modCount 40 size 40 table HashMap$Entry<K,V>[64] (id=274) [0] null [1] LinkedHashMap$Entry<K,V> (id=282) after LinkedHashMap$Entry<K,V> (id=287) after LinkedHashMap$Entry<K,V> (id=295) after LinkedHashMap$Entry<K,V> (id=300) after LinkedHashMap$Entry<K,V> (id=270) after LinkedHashMap$Entry<K,V> (id=347) before LinkedHashMap$Entry<K,V> (id=300) hash -1 > key null next null > value null before LinkedHashMap$Entry<K,V> (id=295) hash 9542041 key Class<T> (java.lang.Short) (id=304) next null value BasicTypeImpl<X> (id=305) before LinkedHashMap$Entry<K,V> (id=287) hash 20782847 key Class<T> (double) (id=301) next LinkedHashMap$Entry<K,V> (id=302) value BasicTypeImpl<X> (id=303) before LinkedHashMap$Entry<K,V> (id=282) hash 23367130 key Class<T> (long) (id=298) next null value BasicTypeImpl<X> (id=299) before LinkedHashMap$Entry<K,V> (id=294) hash 9273729 key Class<T> (java.math.BigDecimal) (id=296) next null value BasicTypeImpl<X> (id=297) [2] LinkedHashMap$Entry<K,V> (id=283) [62] LinkedHashMap$Entry<K,V> (id=370) [63] LinkedHashMap$Entry<K,V> (id=295) threshold 48 values HashMap$Values (id=242)
- A non-issue solved
- 20091216: reopening - we are running into an issue where an emf.getCriteriaBuilder() call before we create an EntityManager runs into the same invalid NPE on a null ClassDescriptor.javaClass - check secondarily in bug# 297928
MetamodelImpl.java private boolean putType(Class javaClassKey, TypeImpl typeValue) { boolean isValid = true; // DI99: Check for an invalid key without reporting it (a non-Fail-Fast pattern) if(null == javaClassKey) { ---> isValid = false; } this.types.put(javaClassKey, typeValue); return isValid; }
DI 100: 20100120: EmbeddedId on MappedSuperclass fails metadata validation on reserved temp metamodel PK
- See EmbeddedIdAccessor:154 - The fake table name - inserted so that we can process metamodel types is leaking into metadata processing.
- Either skip validation for this reserved temporary PK or delay/truncate insertion in metadata processing until after this step if not required for metamodel pre-processing.
- 20100308: See related bug# 300051 fixed in SVN rev # 6784 for EclipseLink 2.1
- 20100924: See related GlassFish bug# 13557 in EclipseLink 2.0.1 - fixed by 300051
- See also enhancement # 326317 to name the attribute of this field to aide in triage of validation exceptions involving the Metadata or Metamodel API when using temporary internal processing fields.
DI 100: UML Model Extension
- CPU (Abstract @MappedSuperclass) fields: @EmbeddedId CPUEmbeddableId
Analysis 100:
- In the following code
public class EmbeddedIdAccessor extends EmbeddedAccessor { public void process() { // Check if we already processed an Id or IdClass. if (owningDescriptor.hasPrimaryKeyFields()) { throw ValidationException.embeddedIdAndIdAnnotationFound(getJavaClass(), getAttributeName(), owningDescriptor.getIdAttributeName()); }
- The following exception will occur if we move @EmbeddedId down from E:GalacticPosition to MS:CoordinateMS (It does not need to be composite)
Exception Description: Entity class [class org.eclipse.persistence.testing.models.jpa.metamodel.CoordinateMS] has both an @EmbdeddedId (on attribute [primaryKey]) and an @Id (on attribute []. Both ID types cannot be specified on the same entity. at org.eclipse.persistence.exceptions.ValidationException.embeddedIdAndIdAnnotationFound(ValidationException.java:845) at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.EmbeddedIdAccessor.process(EmbeddedIdAccessor.java:154) at org.eclipse.persistence.internal.jpa.metadata.MetadataDescriptor.processAccessors(MetadataDescriptor.java:1271) at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.ClassAccessor.processAccessors(ClassAccessor.java:825) at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.MappedSuperclassAccessor.processMetamodelDescriptor(MappedSuperclassAccessor.java:1088) at org.eclipse.persistence.internal.jpa.metadata.MetadataProject.processStage2(MetadataProject.java:1331) at org.eclipse.persistence.internal.jpa.metadata.MetadataProcessor.processORMMetadata(MetadataProcessor.java:461) at org.eclipse.persistence.internal.jpa.deployment.PersistenceUnitProcessor.processORMetadata(PersistenceUnitProcessor.java:390) at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.predeploy(EntityManagerSetupImpl.java:947)
- This validation exception is occurring because metadata processing does not expect the fake table names
(previously we did not have descriptors for MappedSuperclasses - before we introduced the Metadata JPA 2.0 API)
- The fix will be to "not count" the fake ID's or either not add them in the presence of an IdClass or EmbeddedID
- RelationalDescriptor(org.eclipse.persistence.testing.models.jpa.metamodel.CoordinateMS --> [DatabaseTable(__METAMODEL_RESERVED_IN_MEM_ONLY_TABLE_NAME)])
primaryKeyFields ArrayList<E> (id=107) elementData Object[2] (id=115) [0] DatabaseField (id=117) name "__PK_METAMODEL_RESERVED_IN_MEM_ONLY_FIELD_NAME" (id=118) qualifiedName "__PK_METAMODEL_RESERVED_IN_MEM_ONLY_FIELD_NAME" (id=118)
Option 100-1: Ignore fake MappedSuperclass IDs
- If we check the id for our fake MappedSuperclass id as follows
if (owningDescriptor.hasPrimaryKeyFields() && !owningDescriptor.getPrimaryKeyField().getQualifiedName().equalsIgnoreCase( MetadataConstants.MAPPED_SUPERCLASS_RESERVED_PK_NAME)) { throw ValidationException.embeddedIdAndIdAnnotationFound(getJavaClass(), getAttributeName(), owningDescriptor.getIdAttributeName()); }
- We get the following instead of the validation exception
cmpPolicy CMP3Policy (id=154) descriptor RelationalDescriptor (id=124) fieldToAccessorMap null forceUpdate null keyClassFields null mappedClass null modificationDeferralLevel 2 nonDeferredCreateTime 0 pessimisticLockingPolicy null pkClass null pkClassName "org.eclipse.persistence.testing.models.jpa.metamodel.EmbeddedPK" (id=160) updateAllFields null
Option 100-2: Do not add fake MappedSuperclass IDs when IdClass or EmbeddedId will exist
- Previously, I thought we do not have the Id accessors populated yet - to be able to check.
- It turns out that the call accessor.addAccessors(); actually does present me with the IdClass or EmbeddedId accessors
- The issue is that the call if (!metadataDescriptor.hasIdAccessor()) { is not doing enough - it should be also calling hasEmbeddedId() - however this field is currently not set yet and is null.
this MappedSuperclassAccessor (id=115) m_descriptor MetadataDescriptor (id=117) m_accessors HashMap<K,V> (id=119) size 2 table HashMap$Entry<K,V>[16] (id=212) [7] HashMap$Entry<K,V> (id=219) key "primaryKey" (id=223) value EmbeddedIdAccessor (id=141) m_defaultAccess "FIELD" (id=202) m_embeddedIdAccessor null m_pkClass null m_pkClassIDs HashMap<K,V> (id=242) m_idClass null
if (!metadataDescriptor.hasIdAccessor()) { relationalDescriptor.addPrimaryKeyFieldName(MetadataConstants.MAPPED_SUPERCLASS_RESERVED_PK_NAME); } m_idAccessors HashMap<K,V> (id=91) entrySet HashMap$EntrySet (id=128) size 0 Thread [main] (Suspended (breakpoint at line 467 in MetadataProject)) MetadataProject.addMetamodelMappedSuperclass(MetadataClass, MappedSuperclassAccessor) line: 467 EntityAccessor.addPotentialMappedSuperclass(MetadataClass, boolean) line: 188 EntityAccessor.discoverMappedSuperclassesAndInheritanceParents(boolean) line: 300 EntityAccessor.preProcess(boolean) line: 539 EntityAccessor.preProcess() line: 522 MetadataProject.processStage1() line: 1303 MetadataProcessor.processORMMetadata() line: 460 PersistenceUnitProcessor.processORMetadata(MetadataProcessor, boolean) line: 390 EntityManagerSetupImpl.predeploy(PersistenceUnitInfo, Map) line: 947 JavaSECMPInitializer(JPAInitializer).callPredeploy(SEPersistenceUnitInfo, Map, PersistenceInitializationHelper, String, String) line: 88 JavaSECMPInitializer.initPersistenceUnits(Archive, Map, PersistenceInitializationHelper) line: 256 JavaSECMPInitializer.initialize(Map) line: 216
- We must partially process the accessor by calling setEmbeddedIdAccessor()' and moving the validation check higher from EmbeddedIdAccessor.process() to MetadataDescriptor.addAccessor() - with this change, the addition of a hasIdAccessor() to the existing hasIdAccessor in MetadataProject.addMetamodelMappedSuperclass() will stop the addition of the temporary PK MAPPED_SUPERCLASS_RESERVED_PK_NAME to the MAPPED_SUPERCLASS_RESERVED_TABLE_NAME temporary table.
Option 100-3: Remove __PK_METAMODEL_RESERVED_IN_MEM_ONLY_FIELD_NAME temp PK when IdClass or EmbeddedId exists
- In this option we remove the fake id when it turns out it is no longer needed.
Solution 100:
- We will need to use option 2 - move validation earlier and set the accessor field on the descriptor so that we can check for it (partially pre-process it).
- Fixed in SVN rev # 6784
DI 101: 20100218: Descriptor.javaClass is null on a container EM for a specific case
- 20110303: We have reproduced this issue in bug# 338837 in a Java SE environment. We have two workarounds and will be modifying the code to warn about the actual cause - which is a not fully specified data model in persistence.xml.
- See SVN rev # 9086
- See the following distributed Java EE application tutorial on how to define persistence units with WebLogic.
- This issue concerns the case where the metadata is not fully initialized - as in one or more of the descriptors has not passed through a Project.convertClassNamesToClasses() call after predeploy() but before using the persistence unit.
- The Metamodel API must assume that all RelationalDescriptors are initialized - with both their class and className set. If we try to create a Metamodel Type from this un-initialized RelationalDescriptor - the user may see the following exception.
java.lang.IllegalArgumentException: The type [null] is not the expected [EntityType] for the key class [class org.yourdomain.Enity(not-MappedSuperclass)].
- See
- http://jira.springframework.org/browse/SPR-6826
- http://bugs.eclipse.org/303063 - solved
- http://forums.java.net/jive/thread.jspa?threadID=74881&tstart=0
- http://fisheye2.atlassian.com/browse/eclipselink/trunk/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel/SingularAttributeImpl.java?r1=4969&r2=5050
- http://bugs.eclipse.org/282518
- http://bugs.eclipse.org/315041 - duplicate of 303063 - should also be solved
- eclipselink-users scenario may reproduce a null javaClass on the RelationalDescriptor - it is pre rev 8006 - pending verification - verified fixed
- There is a larger issue here in that the NetBeans API provides out-of-the-box MetaModel functionality that is on by default and at least 2-4 weeks behind the current stream. Users that get essentially internal exceptions my not be aware of the specific JPA 2.0 context they are getting from the IDE.
- For NetBeans IDE users - try upgrading to a newer version of NetBeans and refresh your application.
- 20110303 See related
- http://www.netbeans.com/projects/javaee/lists/issues/archive/2010-02/message/627
- http://netbeans.org/bugzilla/show_bug.cgi?id=181068
- http://stackoverflow.com/questions/3559691/jsf-crud-application-and-entities/5185057#5185057
- http://netbeans.org/bugzilla/show_bug.cgi?id=180810
- http://old.nabble.com/Re%3A-problem-with-criteria-The-type--null--is-not-the-expected--EntityType--for-the-key-class--class-br.com.thinkit.db.Ativos----try-upgrade-past-8141.-p30219742.html
- http://dev.eclipse.org/mhonarc/lists/eclipselink-users/msg05508.html
- http://forums.devshed.com/java-help-9/jpa-controller-not-work-in-ejb-706551.html
- not http://forum.springsource.org/archive/index.php/t-32323.html
- http://comments.gmane.org/gmane.comp.java.tynamo.user/173
- http://forums.netbeans.org/post-90076.html
- http://netbeans.org/bugzilla/show_bug.cgi?id=191936
- and
- http://www.mentby.com/daniel-gomes-think-it/eclipselink-problem-with-platform.html
- http://permalink.gmane.org/gmane.comp.java.ide.netbeans.user/158733
- http://netbeans-org.1045718.n5.nabble.com/Netbeans-6-9-problem-with-get-connect-to-database-with-eclipselink-td3205269.html
- http://old.nabble.com/problem-with-criteria-The-type--null--is-not-the-expected--EntityType--for-the-key-class--class-br.com.thinkit.db.Ativos-.-td30213876.html
- http://netbeans.org/projects/www/lists/nbusers/archive/2010-11/message/363
- Feb 2010
Analysis 101
- Some development history: It is assumed by the Metamodel API that metadata processing of RelationalDescriptors have their javaClass set (non-NULL)
- There has been several scenarios where the javaClass is null - usually on @MappedSuperclass objects documented above.
- I think we cannot in the future "assume" that all metadata processing is complete (aka convertClassNamesToClasses()
Reproduction 101
- 20110303: We have reproduced this issue in bug# 338837 in a Java SE environment. We have two workarounds and will be modifying the code to warn about the actual cause - which is a not fully specified data model in persistence.xml.
- The following specification mandated JPA 2.0 IllegalArgumentException occurs because entity classes were not processed because they are missing from the search path or not specified.
- This usually occurs in Java SE environments only.
- This is the result of an incomplete specification of the entities in persistence.xml for Java SE RESOURCE_LOCAL environments and possibly Java EE 6 Web Profile JTA environments.
- Occurs when running JPA 2.0 Criteria or Metamodel code where we require an EntityType like the following
EntityType unitOfWork_ = getEntityManager().getMetamodel().entity(UnitOfWork.class); Root<UnitOfWork> root = getEntityManager().getCriteriaBuilder().createQuery().from(UnitOfWork.class);
DDLGenerationClient [Java Application] org.eclipse.persistence.example.jpa.server.common.DDLGenerationClient at localhost:4353 Thread [main] (Suspended) MetamodelImpl.entity(Class<X>) line: 160 CriteriaQueryImpl<T>(AbstractQueryImpl<T>).from(Class<X>) line: 97 DDLGenerationClient.checkMetamodel() line: 436 DDLGenerationClient.doQuery() line: 460 DDLGenerationClient.main(String[]) line: 445
- in
public <X> EntityType<X> entity(Class<X> clazz) { Object aType = this.entities.get(clazz); if(aType instanceof EntityType) { return (EntityType<X>) aType; } else { --> throw new IllegalArgumentException(ExceptionLocalization.buildMessage(
- We see the following exception - which is not very descriptive of the actual issue of missing metadata because of missing entity classes found during EntityManager predeploy
[EL Finest]: 2011-03-03 12:14:36.141--ServerSession(11582167)--Thread(Thread[main,5,main])--End deploying Persistence Unit dao.create.tables.derby.client; session file:/C:/view_w36a/examples/org.eclipse.persistence.example.jpa.server.common.ddlgen/bin/_dao.create.tables.derby.client;state Deployed; factoryCount 1 Exception in thread "main" java.lang.IllegalArgumentException: The type [null] is not the expected [EntityType] for the key class [class org.eclipse.persistence.example.distributed.collatz.model.UnitOfWork]. at org.eclipse.persistence.internal.jpa.metamodel.MetamodelImpl.entity(MetamodelImpl.java:160) at org.eclipse.persistence.example.jpa.server.common.DDLGenerationClient.checkMetamodel(DDLGenerationClient.java:435) at org.eclipse.persistence.example.jpa.server.common.DDLGenerationClient.doQuery(DDLGenerationClient.java:459) at org.eclipse.persistence.example.jpa.server.common.DDLGenerationClient.main(DDLGenerationClient.java:444)
- using
<persistence-unit name="dao.create.tables.derby.client" transaction-type="RESOURCE_LOCAL"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <!-- exclude-unlisted-classes>false</exclude-unlisted-classes--> <!-- class>org.eclipse.persistence.example.distributed.collatz.model.UnitOfWork</class--> <properties> <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/> <property name="eclipselink.target-database" value="Derby"/> <property name="javax.persistence.jdbc.url" value="jdbc:derby://127.0.0.1:1527/dataparallel;create=true"/> <property name="javax.persistence.jdbc.user" value="APP"/> <property name="javax.persistence.jdbc.password" value="APP"/> <property name="eclipselink.logging.level" value="ALL"/> <!-- property name="eclipselink.ddl-generation" value="drop-and-create-tables"/> <property name="eclipselink.ddl-generation.output-mode" value="both"/--> </properties> </persistence-unit>
- workaround is
<exclude-unlisted-classes>false</exclude-unlisted-classes>
- or
<class>org.eclipse.persistence.example.distributed.collatz.model.UnitOfWork</class>
- Result is OK with either fix
unitOfWork_ EntityType: EntityTypeImpl@672904:UnitOfWork [ javaType: class org.eclipse.persistence.example.distributed.collatz.model.UnitOfWork descriptor: RelationalDescriptor(org.eclipse.persistence.example.distributed.collatz.model.UnitOfWork--> [DatabaseTable(EL_UNITOFWORK)]), mappings: 7]
- Solution:
- There are two ways into this problem.
- 1) We specify no entities at all and also do not specify
- <exclude-unlisted-classes>false</exclude-unlisted-classes>
- - the result of this is a completely empty metamodel
- 2) We specify only part of the entities via <class> properties
- - the result of this is selective IllegalArgumentException errors thrown when we attempt to use the missing EntityType classes - likely in a Criteria API query
- We need to warn both for no (0) entities found and later that a specific type was not found - possibly as a result of a missing class reference
- 1) warn the user that no entities were found during the predeploy so the deploy iterates nothing during metamodel.initialize - actually nothing is done during initialize
- MetamodelImpl@21350241 [ 0 Types: , 0 ManagedTypes: , 0 EntityTypes: , 0 MappedSuperclassTypes: , 0 EmbeddableTypes: ]
- or in
- protected void initializeCanonicalMetamodel(Metamodel model)
- 1) warn the user that no entities were found during the predeploy so the deploy iterates nothing during metamodel.initialize - actually nothing is done during initialize
DDLGenerationClient [Java Application] org.eclipse.persistence.example.jpa.server.common.DDLGenerationClient at localhost:3057 Thread [main] (Suspended) MetamodelImpl.initialize() line: 363 MetamodelImpl.<init>(DatabaseSession) line: 109 MetamodelImpl.<init>(EntityManagerSetupImpl) line: 128 EntityManagerSetupImpl.getMetamodel() line: 2209 EntityManagerSetupImpl.deploy(ClassLoader, Map) line: 413 EntityManagerFactoryImpl.getServerSession() line: 185 EntityManagerFactoryImpl.getMetamodel() line: 531 DDLGenerationClient.initialize(String) line: 137 DDLGenerationClient.doQuery() line: 453 DDLGenerationClient.main(String[]) line: 444
- Descriptors map is empty - we need to warn the user about this
this MetamodelImpl (id=37) session ServerSession (id=40) descriptors HashMap<K,V> (id=61) size 0
- 2) Change the IAE for a single missing entity to suggest that the entity class may not have been found or specified (it may be on purpose)
- Security note: A search for a non-existent entity class may come through a user-generated query and may be a way of testing the system for hidden classes
- - we may want to not print the name
- The error message needs to change from
- Security note: A search for a non-existent entity class may come through a user-generated query and may be a way of testing the system for hidden classes
The type [null] is not the expected [EntityType] for the key class [class org.eclipse.persistence.example.jpa.server.business.Cell].
- to something like
No [EntityType] was found for the key class [class org.eclipse.persistence.example.distributed.collatz.model.UnitOfWork] in the Metamodel - please verify that the Entity class was referenced in persistence.xml using a specific <class>org.eclipse.persistence.example.distributed.collatz.model.UnitOfWork</class> property or a global <exclude-unlisted-classes>false</exclude-unlisted-classes> element.
DI 102: 20100421: Fully initialize MappedSuperclass Descriptors and Refactor existing workarounds
- Because of bug# 309856 where we require a fully initialized MappedSuperclass Descriptor when merging orm.xml metadata (object model only) - we may need a 2 stage refactor.
- Guy basically implemented equals() in all metadata subclasses
- Note: Effective Java Item #8 does not apply yet - we don't have to override hashCode() as well because the metadata classes are not in hash based collections HashMap, HashSet and we are not serializing the classes yet.
- This will allow us to remove a lot of the handling and usage of the specialized map on the core API Project class.
package org.eclipse.persistence.sessions; public class Project implements Serializable, Cloneable { protected Map<Object, RelationalDescriptor> mappedSuperclassDescriptors;
DI 103: 20100601: 315287: Handle BasicType as inheritance root for ManagedTypes
- We were not returning correctly when the superType of a ManagedType was a BasicType (Transient)
- See SVN rev# 7444
- See bug# 302606 303722 fixed by 315287
DI 104: 20100614: 314906: PluralAttribute.getJavaType returns elementType instead of Collection Type
- Fixed in SVN rev# 7618
- Essentially the issue is what "we" mean by element type and what the "specification" describes as element type differs for the functions getJavaType() and getBindableType().
- Q1: in the case of BindableType' in Bindable - is SINGULAR_ATTRIBUTE the same as ENTITY for non-Basic attributes (IE: a OneToOne to an Entity)?
Analysis 104:
- This may be a bug, the return class is of type C which is not the element type of the collection V
- in
- public abstract class PluralAttributeImpl<X, C, V> extends AttributeImpl<X, C> implements PluralAttribute<X, C, V> {
we should be using * @param <C> The type of the represented collection not * @param <V> The element type of the represented collection
- The specification does not go into details of the implementation beyond the javadoc for the function above
- = "represented attribute"
- However, check the javadoc for getBindableJavaType()
- here we return the elementType
- we will need to discuss what the spec means by elementType and our own internal view of what the elementType instance variable is - and how they differ
* If the bindable type of the object is <code>PLURAL_ATTRIBUTE</code>, * the Java element type is returned. If the bindable type is public Class<V> getBindableJavaType() {
- Regression testing will be affected by...
- - this will affect functionality of implementor IdentifiableType.getIdType()
- - open bug # 289487 [DatabaseField type and typeName different for @BasicMap with @ObjectTypeConverter ] will be affected
- - Non initialized attributes like
- - lazy initialization
Decision 104:
Specification Discussion 104:
- I have a spec question about the function in the JPA Metamodel API for PluralAttribute.getJavaType(). The question is currently directed at the JPA 2.0 specification leads so we can have a majority decision on 1) or 2). The modified function would return the middle declaration class of C for each subclass of PluralAttribute.
- From the discussion below - I recommend that we do 2) - and modify the code to return the collection type - and fill the hole in the Impl of the API
- Q) For PluralAttributeImpl<X, C, V> should we...
- 1) continue to return Class<V> = element type of the represented collection - 1 of 4 of us
- or
- 2) (NEW) switch to Class<C> = type of the represented collection - 2 of 4 of us (as of end of day 20100614 - 3 of 4 of us)
JPA: /** * Return the Java type of the represented attribute. * @return Java type */ PluralAttributeImpl.getJavaType() --> Class
- To aide in interpreting the specification we have 3 points.
- 1) To me "represented attribute" for plural attributes means the whole collection - not the elements in the collection - which would never be Metamodel Attributes.
- 2) Missing a get*Type() that returns the collection type
- We have 3 functions that return the elementType (including a Type and Class version) - but we only have a Type enum version for collection Type
- 3) Already have an existing get*Type() that returns the elementType
- We can refer to the spec function getElementType() - which at this point returns the same thing (wrapped by a Type) as getJavaType(). The function getBindableJavaType() also currently returns the same thing as getJavaType()
/** * Return the type representing the element type of the * collection. * @return element type */ Type<E> getElementType();
- Therefore, I suggest that we move the API from providing the same elementType return for all 3 getJavaType(), getBindableJavaType() and getElementType() and match this pattern by changing getJavaType() to complement the existing getCollectionType() which returns the collection type enum but not the Class itself.
- So we will have the following structure if we modify the code
- Collection Element Type <V>
- Type
- getElementType()
- Class
- getBindableJavaType()
- Type
- Collection Type <C>
- enum (as there are only 4 collection types)
- getCollectionType()
- Class
- getJavaType()
- enum (as there are only 4 collection types)
- Collection Element Type <V>
- So we will have the following structure if we modify the code
- Currently, the code is the same as SingularAttribute where the elementType class is returned.
- The issue is that the spec is not fully specific on what to return if we interpret elementType the same as in SingularAttribute - see getBindableType for example
// If the bindable type of the object is <code>PLURAL_ATTRIBUTE</code>, // the Java element type is returned. If the bindable type is public Class<V> getBindableJavaType() {
- EclipseLink RT newsgroup posting that I will reply to when we resolve this - resolved - we will make the change for bug# 314906 in patch .
DI 105: 20100616: 316991: Attribute.getJavaMember() returns null for all Embeddable Attributes
Analysis 105:
- see DI 95
- Also verify
- Does this occur for all attributes including those from Entities and MappedSuperclasses (also check Basic)
- The fix is for Method level access, verify Field level access for Embeddables - however we should be OK as reflective code already existed for Field level in getJavaMember()
Fixed 105:
- See SVN rev# 7636
DI 106: 20100810: 322585:Metamodel initialization via EMF.getMetamodel() before EM deploy results in an invalid Metamodel because of UNINITIALIZED descriptor.initializationStage
- CRITICAL: The metamodel via EMF is not fully initialized until at least 1 em deploy - this affects both an emf.getMetamodel() and an emf.getCriteriaBuilder() - which internally calls emf.getMetamodel()
- Metamodel initialization via EMF.getMetamodel() before EM deploy will result in an invalid Metamodel because of UNINITIALIZED descriptor.initializationStage. The consequences may be any of the following issues.
Reproduction
- In SE the following sequence of operation will result in either an invalid metamodel and/or a NullPointerException accessing a null javaType on a ManagedType depending on the type of model. In all cases the underlying issue is that the metamodel does not have the javaClass for entities that are not finished processing - (their javaClassName is set but the javaClass is null on the ClassDescriptor). This will occur before a session login or entityManager deploy is done on at least on EM.
- Invalid operation
emf = Persistence.createEntityManagerFactory(puName); // get the Metamodel before it is ready metamodel = emf.getMetamodel(); // no em.deploy() or session.login() has occurred yet // or criteriaBuilder = emf.getCriteriaBuilder(); entityManager = emf.createEntityManager();
- Valid operation
emf = Persistence.createEntityManagerFactory(puName); entityManager = emf.createEntityManager(); // get the Metamodel after it is ready metamodel = emf.getMetamodel(); // or criteriaBuilder = emf.getCriteriaBuilder();
- In order to support emf.getMetamodel() in this case we need to initialize before deploy.
- History: in DI 92 (svn 5541 for 266912 in oct 2009) and later in bugs 297748(try/catch) and 297555 we moved from the EntityManagerFactoryImpl to the EntityMangerSetupImpl.
- NPE
- descriptor.javaClass == null
- The following bugs are related to or dependent on this fix
- The following Metamodel design issues are related to or deprecated by this fix
References:
- Directly related:
- See DI 32: 20090703: Metamodel superType requires javaClass set on custom descriptor on MappedSuperclassAccessor
- See DI 95: 20091017: Attribute.getJavaMember() returns null for a BasicType on a MappedSuperclass because of an uninitialized accessor
- See DI 99: 20091110: Metamodel.types LinkedHashMap appears to have null=null K,V pairs - expected Java SE behavior
- See DI 101: 20100218: Descriptor.javaClass is null on a container EM for a specific case
- Of interest:
- See DI 69: 20090831: Object.class as a default javaType is not compatible with JPA 1.0
- See DI 92: 20091008: Move metamodel instance field from EntityManagerFactory to EntityManagerSetupImpl
- Bugs
- 297748 - JPA2: non-lazy metamodel processing in EM requires try/catch wrapper to ensure persistence unit deployment finishes
- 297928 - emf.getMetamodel() invalid before at least one em deploy (this DI 106)
- 282518 (di 32) We are setting the javaClass later during metamodel processing instead of during metadata processing - so we can avoid persistence operations on our custom Relational Descriptor.
- http://jira.springframework.org/browse/SPR-6826
- http://bugs.eclipse.org/303063
- http://bugs.eclipse.org/303684 (duplicate 322585) - EntityManagerFactory.getMetamodel() throws NPE if Server Session not logged in first
- http://forums.java.net/jive/thread.jspa?threadID=74881&tstart=0
- http://fisheye2.atlassian.com/browse/eclipselink/trunk/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel/SingularAttributeImpl.java?r1=4969&r2=5050
- http://bugs.eclipse.org/315041 (customer raised)
- Maybe 320273 - JPA2: On Spring 3.0: Metamodel initialize() must filter (valid) null key/value pairs in session.getDescriptors Map
- http://dev.eclipse.org/mhonarc/lists/eclipselink-users/msg05511.html
- Bug Dependencies
- 297928
- 282518
- 303063
- 297748
- 297928
Analysis 106:
- What we need from the deploy() (after a predeploy()) is the following
- 1) The javaClass set on all Entity and Embeddable descriptors - via a session.getProject().convertClassNamesToClasses(realClassLoader); call in EntityManagerSetupImpl.deploy()
- 2) The CMP3Policy set on all descriptors (with PK fields set) - via later EntityManagerSetupImpl.initServerSession()
- 3) Optionally customizers (these are processed later in deploy() below.
- We do not require
- Everything after state = STATE_DEPLOYED in EntityManagerSetupImpl.deploy()
- DDL generation
- Actual login to the database
- Thread [main] (Suspended)
- ConnectionPool.startUp() line: 441
- ServerSession.connect() line: 495
- ServerSession(DatabaseSessionImpl).login() line: 627
- EntityManagerFactoryProvider.login(ServerSession, Map) line: 230
- EntityManagerSetupImpl.deploy(ClassLoader, Map) line: 381
- EntityManagerFactoryImpl.getServerSession() line: 157
- EntityManagerFactoryImpl.createEntityManagerImpl(Map) line: 214
- EntityManagerFactoryImpl.createEntityManager() line: 202
- see also DI 95
- Todo:
- EntityManagerSetupImpl
- - issue with getMetamodel init variable assignment - if canonical init fails - the variable is still set
- -I remove sessionsxml comment from non-sessions block in deploy
- EntityManagerImpl
- - comment should mention setup not factory
- EntityManagerSetupImpl
- EntityManagerSetupImpl
- the following property is named as though the persistence unit is "only" used for validation
- In fact it validates the persistence unit by doing a deploy just after the predeploy - without waiting for client code to login
- after that it behaves normally.
<property name="eclipselink.validation-only" value="True"/>
Alt 106-1: Fail-fast with ISE: emf.getMetamodel() fails with non-spec ISE if in State_predeployed (entity descriptors will be in state 0 not 3)
Alt 106-2: Add State warning and continue to do nothing
- No fail fast
- Add warning to use EM not EMF when descriptor in state 0 (not initialized)
Alt 106-3: Force full real deploy
- See patch for bug# 322585
- In this scenario we force a login() to the serverSession during EntityManagerFactory creation.
- We will also introduce a new persistence.xml property like LOGIN_AT_EMF where the default would be true - but clients running on EE servers for example could disable this and return to pre 2.2 behavior.
Issues 106:3
- What will we do with the connections created
- Performance degredation previously in the first EntityManager creation is now shifted earlier to EntityManagerFactory creation
- Names for the persistence.xml property LOGIN_AT_EMF_PROPERTY that essentially turns of validation by default.
- eclipselink.login-at-emf
- eclipselink.force-deploy-at-predeploy
- eclipselink.force-login-at-predeploy
- eclipselink.fully-process-emf-before-login
- eclipselink.enable-login-at-emf-predeploy
- eclipselink.enable-full-login-at-emf
- eclipselink.enable-deploy-during-predeploy
- eclipselink.enable-deploy-during-emf-predeploy
- eclipselink.enable-login-during-emf-predeploy
- eclipselink.enable-validation-by-default
- eclipselink.enable-default-login-at-predeploy - recommended
- Issue: this new property will essentially deprecate the validation flag unless we make validation=false also disable the new forced login flag
Results 106
- The following code including changes is run just after emf.predeploy()
- emf.verifyOpen();
- emf.getServerSession(); // calls ems.deploy()
- The result is that the entity descriptors have their javaClass set but their state is still set as lt POST_INITIALIZED
- There actually already is a mechanism to login or do a getServerSession() call this is in for validation.
PersistenceProvider:166 (SE) and 234 (EE) // 322586: This code has been added to allow validation to occur without actually calling createEntityManager if (emSetupImpl.shouldGetSessionOnCreateFactory(nonNullProperties)) { factory.getServerSession(); }
- or
PersistenceProvider:166 (SE) and 234 (EE) // 322586: This code has been added to allow validation to occur without actually calling createEntityManager if (emSetupImpl.shouldGetSessionOnCreateFactory(nonNullProperties)) { //factory.getServerSession(); factory.getEntityManagerSetupImpl().getSession().initializeDescriptors(); } EntityManagerFactoryImpl:685 public EntityManagerSetupImpl getEntityManagerSetupImpl() { // 322586 NEW return setupImpl; } EntityManagerSetupImpl.deploy():376 if (isValidationOnly(deployProperties, false)) { // 324213: Add loginAndDetectDatasource() call before calling initializeDescriptors when validation-only is True // we avoid a native sequence exception on a generic DatabasePlatform by auto-detecting the correct DB platform session.loginAndDetectDatasource(); // NEW session.initializeDescriptors();
- We add the following change
PersistenceUnitProperties /** Validate deployment, but do not connect. * Note: this <code>eclipselink.validation-only</code> property will perform login early * during the initial predeploy of the persistence unit. After the persistence unit can be used normally.<p> * The default is "False", set the value to "True" to enable validation.<p> * Note: setting this property to False will not disable the "LOGIN_AT_EMF_PROPERTY" - the two are independent. */ public static final String VALIDATION_ONLY_PROPERTY = "eclipselink.validation-only"; // See bug# 322585 /** This property will in effect turn the above "validation-only" flag to "True"<p> * Note: this <code>eclipselink.enable-default-login-at-predeploy</code> property will perform login early * during the initial predeploy of the persistence unit. After the persistence unit can be used normally.<p> * The default is "True", set the value to "False" to disable logging in the persistence unit at predeploy.<p> * Note: setting this property to False will not disable the "VALIDATION_ONLY_PROPERTY" - the two are independent. */ public static final String LOGIN_AT_EMF_PROPERTY = "eclipselink.enable-default-login-at-predeploy"; EntityManagerSetupImpl // 322585: added isLoginEnabledAtEMFCreate() check that defaults to true public boolean shouldGetSessionOnCreateFactory(Map m) { m = mergeWithExistingMap(m); return isValidationOnly(m, false) || isLoginEnabledAtEMFCreate(); } protected boolean isLoginEnabledAtEMFCreate(Map m, boolean shouldMergeMap) { if (shouldMergeMap) { m = mergeWithExistingMap(m); } String loginAtEMFString = getConfigPropertyAsStringLogDebug(PersistenceUnitProperties.LOGIN_AT_EMF_PROPERTY, m, session); if (loginAtEMFString != null) { return Boolean.parseBoolean(loginAtEMFString); } else { // default to true - also do a partial deploy at emf predeploy return true; } }
Analysis 106-3
- After trying 2 locations of a getServerSession() call in order to do a login before an official deploy() on createEntityManager - I came across code that already exists for JPA validation that will do a login for us and effect a deploy just after the predeploy finishes on the EMF.
Performance Results 106-3
- See enhancement request 324974 for general early EMF predeploy login
- The following show that that the time taken to update the session, do DDL generation and intialize the metamodel and canonical metamodel has shifted to the EMF creation from the EM creation. In essence an emf.createEntityManager() now takes just a couple microseconds instead of seconds.
- Doing all the following in the emf.predeploy
- updateServerSession
- session.login
- generateDDL
- metamodel initalization
- canonical metamodel initialization
- takes in SE
- with change
- predeploy() = 2.6 sec
- [EL Finest]: 2010-08-17 18:01:12.002--ServerSession(27196165)--Thread(Thread[main,5,main])--Begin predeploying Persistence Unit dao.create.tables.derby; session default-session; state Initial; factoryCount 0
- [EL Finest]: 2010-08-17 18:01:14.67--ServerSession(27196165)--Thread(Thread[main,5,main])--End deploying Persistence Unit dao.create.tables.derby; session default-session; state Deployed; factoryCount 1
- deploy() = 0.09 sec
- [EL Finest]: 2010-08-17 18:01:14.67--ServerSession(27196165)--Thread(Thread[main,5,main])--End deploying Persistence Unit dao.create.tables.derby; session default-session; state Deployed; factoryCount 1
- [EL Finer]: 2010-08-17 18:01:14.763--ServerSession(27196165)--Thread(Thread[main,5,main])--client acquired
- predeploy() = 2.6 sec
- without change
- predeploy() = 0.75 sec
- [EL Finest]: 2010-08-17 17:58:29.169--ServerSession(2279771)--Thread(Thread[main,5,main])--Begin predeploying Persistence Unit dao.create.tables.derby; session default-session; state Initial; factoryCount 0
- [EL Finest]: 2010-08-17 17:58:29.934--ServerSession(2279771)--Thread(Thread[main,5,main])--End predeploying Persistence Unit dao.create.tables.derby; session default-session; state Predeployed; factoryCount 1
- deploy() = 1.87 sec
- [EL Finest]: 2010-08-17 17:58:29.934--ServerSession(2279771)--Thread(Thread[main,5,main])--Begin deploying Persistence Unit dao.create.tables.derby; session default-session; state Predeployed; factoryCount 1
- [EL Finer]: 2010-08-17 17:58:31.806--ServerSession(2279771)--Thread(Thread[main,5,main])--client acquired
- predeploy() = 0.75 sec
- with change
- On emf.createEntityManager() we just skip deploy/login in getServerSession(), login in createEntityManagerImpl as well as deploy/login in getServerSession() in the EntityManagerImpl constructor
- because the emf.createEntityManager() does virtually nothing after this change
- EE Server testing on WebLogic 10.3.3.0 via standard @PersitenceContext injection on a SSB @EJB injected on a servlet client is OK
- Notice that a predeploy also does a deploy now - previously the deploy was only done when the client persisted or read from the persistence unit (via injection)
Daemon Thread [[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'] (Suspended (breakpoint at line 314 in EntityManagerSetupImpl)) EntityManagerSetupImpl.deploy(ClassLoader, Map) line: 314 EntityManagerFactoryImpl.getServerSession() line: 157 PersistenceProvider.createContainerEntityManagerFactory(PersistenceUnitInfo, Map) line: 243 PersistenceUnitInfoImpl.createEntityManagerFactory(boolean) line: 352 PersistenceUnitInfoImpl.createEntityManagerFactory() line: 332 PersistenceUnitInfoImpl.<init>(PersistenceUnitBean, PersistenceUnitConfigurationBean, GenericClassLoader, String, URL, URL) line: 134 ModulePersistenceUnitRegistry(AbstractPersistenceUnitRegistry).storeDescriptors(Map, Map) line: 336 ModulePersistenceUnitRegistry(AbstractPersistenceUnitRegistry).loadPersistenceDescriptor(VirtualJarFile, boolean, File) line: 250 ModulePersistenceUnitRegistry.<init>(GenericClassLoader, ApplicationContextInternal, Module, boolean) line: 69 EJBModule.setupPersistenceUnitRegistry() line: 221 EJBModule$1.execute() line: 322 PersistenceUnitRegistryInitializer.setupPersistenceUnitRegistries() line: 62 WebAppModule.prepare() line: 398 ScopedModuleDriver.prepare() line: 176 ModuleListenerInvoker.prepare() line: 199 DeploymentCallbackFlow$1.next(Object) line: 507 StateMachineDriver.nextState(StateChange, Object[]) line: 41 DeploymentCallbackFlow.prepare(Module[]) line: 149 DeploymentCallbackFlow.prepare() line: 45 BaseDeployment$1.next(Object) line: 1221 StateMachineDriver.nextState(StateChange, Object[]) line: 41 EarDeployment(BaseDeployment).prepare(DeploymentContext) line: 367 EarDeployment.prepare(DeploymentContext) line: 58 DeploymentStateChecker.prepare(DeploymentContext) line: 154 AppContainerInvoker.prepare(DeploymentContext) line: 60 RedeployOperation.createAndPrepareContainer() line: 98 RedeployOperation.doPrepare() line: 122 RedeployOperation(AbstractOperation).prepare() line: 217 DeploymentManager.handleDeploymentPrepare(Deployment, DeploymentManager$DeploymentRequestInfo) line: 747 DeploymentManager.prepareDeploymentList(ArrayList, DeploymentContext) line: 1216 DeploymentManager.handlePrepare(DeploymentContext) line: 250 DeploymentServiceDispatcher.prepare(DeploymentContext) line: 159 DeploymentReceiverCallbackDeliverer.doPrepareCallback(DeploymentContext) line: 171 DeploymentReceiverCallbackDeliverer.access$000(DeploymentReceiverCallbackDeliverer, DeploymentContext) line: 13 DeploymentReceiverCallbackDeliverer$1.run() line: 46 SelfTuningWorkManagerImpl$WorkAdapterImpl.run() line: 528 ExecuteThread.execute(Runnable) line: 201 ExecuteThread.run() line: 173
Alt 106-4: Force full internal discardable deploy only for metamodel
- Is this different than 5 below?
Alt 106-5: Partial deploy based on what metamodel needs (entity deployment for embeddables and MappedSuperclasses
- Something like at least
ClassLoader realLoader = setupImpl.getPersistenceUnitInfo().getClassLoader(); session.getProject().convertClassNamesToClasses(realClassLoader);
Alt 106-6: Full deploy during predeploy only for SE platform
- This option was suggested by Gordon 20100831 and is a valid alternative. Here we login on predeploy (new behavior) only on SE (Standard Edition platforms) and continue to have a separate predeploy and deploy for EE (Enterprise Edition platforms).
Alt 106-7: Do a login only for early emf.getMetamodel() users
- This alternative moves the emf.getServerSession() lazy initialization call that does a deploy/login and our required initializeDescriptors() call down from emf.predeploy() to emf.getMetamodel() which is also called from emf.getCriteriaBuilder().
- This changes behavior only for early calls to emf.getMetamodel before at least 1 login has occurred on an EntityManager.
Issues 106:
Issue 106-1: Does session.login() occur in predeploy() when validation=True on EE preDeploy()
- Here we need to verify that we currently do not login in on emf.predeploy() and we also do not introduce a database login() with the change to add validation by default. False - we do not login during validation
Issue 106-2: EE preDeploy() and deploy() are currently separate
- The xisting behavior for WebLogic 10 is a the following sequence of operations...
- On EAR deployment - predeploy()
- On user EM login (via a SSB) deploy() and associated login
- Here is the debug point for the predeploy on EAR deployment
Daemon Thread [[STANDBY] ExecuteThread: '4' for queue: 'weblogic.kernel.Default (self-tuning)'] (Suspended (breakpoint at line 837 in EntityManagerSetupImpl)) EntityManagerSetupImpl.predeploy(PersistenceUnitInfo, Map) line: 837 PersistenceProvider.createContainerEntityManagerFactory(PersistenceUnitInfo, Map) line: 205 PersistenceUnitInfoImpl.createEntityManagerFactory(boolean) line: 352 PersistenceUnitInfoImpl.createEntityManagerFactory() line: 332 PersistenceUnitInfoImpl.<init>(PersistenceUnitBean, PersistenceUnitConfigurationBean, GenericClassLoader, String, URL, URL) line: 134 ModulePersistenceUnitRegistry(AbstractPersistenceUnitRegistry).storeDescriptors(Map, Map) line: 336 ModulePersistenceUnitRegistry(AbstractPersistenceUnitRegistry).loadPersistenceDescriptor(VirtualJarFile, boolean, File) line: 250 ModulePersistenceUnitRegistry.<init>(GenericClassLoader, ApplicationContextInternal, Module, boolean) line: 69 EJBModule.setupPersistenceUnitRegistry() line: 221 EJBModule$1.execute() line: 322 PersistenceUnitRegistryInitializer.setupPersistenceUnitRegistries() line: 62 WebAppModule.prepare() line: 398 ScopedModuleDriver.prepare() line: 176 ModuleListenerInvoker.prepare() line: 199 DeploymentCallbackFlow$1.next(Object) line: 507 StateMachineDriver.nextState(StateChange, Object[]) line: 41 DeploymentCallbackFlow.prepare(Module[]) line: 149 DeploymentCallbackFlow.prepare() line: 45 BaseDeployment$1.next(Object) line: 1221 StateMachineDriver.nextState(StateChange, Object[]) line: 41 EarDeployment(BaseDeployment).prepare(DeploymentContext) line: 367 EarDeployment.prepare(DeploymentContext) line: 58 DeploymentStateChecker.prepare(DeploymentContext) line: 154 AppContainerInvoker.prepare(DeploymentContext) line: 60 ActivateOperation.createAndPrepareContainer() line: 207 ActivateOperation.doPrepare() line: 98 ActivateOperation(AbstractOperation).prepare() line: 217 DeploymentManager.handleDeploymentPrepare(Deployment, DeploymentManager$DeploymentRequestInfo) line: 747 DeploymentManager.prepareDeploymentList(ArrayList, DeploymentContext) line: 1216 DeploymentManager.handlePrepare(DeploymentContext) line: 250 DeploymentServiceDispatcher.prepare(DeploymentContext) line: 159 DeploymentReceiverCallbackDeliverer.doPrepareCallback(DeploymentContext) line: 171 DeploymentReceiverCallbackDeliverer.access$000(DeploymentReceiverCallbackDeliverer, DeploymentContext) line: 13 DeploymentReceiverCallbackDeliverer$1.run() line: 46 SelfTuningWorkManagerImpl$WorkAdapterImpl.run() line: 528 ExecuteThread.execute(Runnable) line: 201 ExecuteThread.run() line: 173
- Here is the debug point for deploy() during @PersistenceContext entityManager injection via http://127.0.0.1:7001/enterprise/FrontController?action=demo
Daemon Thread [[ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'] (Suspended (breakpoint at line 312 in EntityManagerSetupImpl)) EntityManagerSetupImpl.deploy(ClassLoader, Map) line: 312 EntityManagerFactoryImpl.getServerSession() line: 157 EntityManagerFactoryImpl.createEntityManagerImpl(Map) line: 214 EntityManagerFactoryImpl.createEntityManager() line: 202 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 EntityManagerFactoryProxyImpl.invoke(Object, Method, Object[]) line: 87 $Proxy94.createEntityManager() line: not available TransactionalEntityManagerProxyImpl.newPersistenceContext(PersistenceUnitInfoImpl) line: 75 TransactionalEntityManagerProxyImpl(BasePersistenceContextProxyImpl).getPersistenceContext(Transaction) line: 160 TransactionalEntityManagerProxyImpl(BasePersistenceContextProxyImpl).invoke(Object, Method, Object[], Transaction) line: 88 TransactionalEntityManagerProxyImpl.invoke(Object, Method, Object[], Transaction) line: 91 TransactionalEntityManagerProxyImpl(BasePersistenceContextProxyImpl).invoke(Object, Method, Object[]) line: 80 TransactionalEntityManagerProxyImpl.invoke(Object, Method, Object[]) line: 26 $Proxy95.toString() line: not available String.valueOf(Object) line: 2826 StringBuilder.append(Object) line: 115 ApplicationService_5ptwty_Impl(ApplicationService).insertObjects(List<Cell>) line: 154 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 AopUtils.invokeJoinpointUsingReflection(Object, Method, Object[]) line: 310 ReflectiveMethodInvocation.invokeJoinpoint() line: 182 ReflectiveMethodInvocation.proceed() line: 149 DelegatingIntroductionInterceptor.doProceed(MethodInvocation) line: 131 DelegatingIntroductionInterceptor.invoke(MethodInvocation) line: 119 ReflectiveMethodInvocation.proceed() line: 171 MethodInvocationVisitorImpl.visit() line: 37 EnvironmentInterceptorCallbackImpl.callback(MethodInvocationVisitor) line: 54 EnvironmentInterceptor.invoke(MethodInvocation) line: 50 ReflectiveMethodInvocation.proceed() line: 171 ExposeInvocationInterceptor.invoke(MethodInvocation) line: 89 ReflectiveMethodInvocation.proceed() line: 171 DelegatingIntroductionInterceptor.doProceed(MethodInvocation) line: 131 DelegatingIntroductionInterceptor.invoke(MethodInvocation) line: 119 ReflectiveMethodInvocation.proceed() line: 171 JdkDynamicAopProxy.invoke(Object, Method, Object[]) line: 204 $Proxy97.insertObjects(List) line: not available ApplicationService_5ptwty_ApplicationServiceLocalImpl.insertObjects(List<Cell>) line: 308 FrontController.generateGlider(PrintWriter) line: 237 FrontController.processGliderCommand(HttpServletRequest, HttpServletResponse, PrintWriter) line: 291 FrontController.processDemoCommand(HttpServletRequest, HttpServletResponse, PrintWriter) line: 360 FrontController.processAction(HttpServletRequest, HttpServletResponse) line: 389 FrontController.doGet(HttpServletRequest, HttpServletResponse) line: 460 FrontController(HttpServlet).service(HttpServletRequest, HttpServletResponse) line: 707 FrontController(HttpServlet).service(ServletRequest, ServletResponse) line: 820 StubSecurityHelper$ServletServiceAction.run() line: 227 StubSecurityHelper.invokeServlet(ServletRequest, HttpServletRequest, ServletRequestImpl, ServletResponse, HttpServletResponse, Servlet) line: 125 ServletStubImpl.execute(ServletRequest, ServletResponse, FilterChainImpl) line: 300 ServletStubImpl.execute(ServletRequest, ServletResponse) line: 183 WebAppServletContext$ServletInvocationAction.doIt(ServletStubImpl, HttpServletRequest, HttpServletResponse) line: 3686 WebAppServletContext$ServletInvocationAction.run() line: 3650 AuthenticatedSubject.doAs(AbstractSubject, PrivilegedAction) line: 321 SecurityManager.runAs(AuthenticatedSubject, AuthenticatedSubject, PrivilegedAction) line: 121 WebAppServletContext.securedExecute(HttpServletRequest, HttpServletResponse, boolean) line: 2268 WebAppServletContext.execute(ServletRequestImpl, ServletResponseImpl) line: 2174 ServletRequestImpl.run() line: 1446 ExecuteThread.execute(Runnable) line: 201 ExecuteThread.run() line: 173
Regression Testing 106
- We have issues with native sequencing as discovered by Doug. When I run the Core and JPA LRG with validation-only set to "True" for all persistence units - I get 2 failures and 12 errors as follows.
- <testsuite errors="12" failures="2" hostname="xps435" name="org.eclipse.persistence.testing.tests.jpa.AllCMP3TestRunModel" tests="2139" time="1284.741" timestamp="2010-08-30T17:16:51">
<testcase classname="org.eclipse.persistence.testing.tests.jpa.advanced.JPAAdvancedTestModel" name="JPAAdvancedTestModel" time="0.0"> Exception Description: SCIENTIST_SEQ: platform DatabasePlatform does not support NativeSequence. at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:411) at org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.getServerSession(EntityManagerFactoryImpl.java:156) at org.eclipse.persistence.jpa.PersistenceProvider.createEntityManagerFactory(PersistenceProvider.java:170) at org.eclipse.persistence.jpa.PersistenceProvider.createEntityManagerFactory(PersistenceProvider.java:67) at javax.persistence.Persistence.createEntityManagerFactory(Unknown Source) at org.eclipse.persistence.testing.framework.junit.JUnitTestCase.getEntityManagerFactory(JUnitTestCase.java:402) at org.eclipse.persistence.testing.framework.junit.JUnitTestCase.getEntityManagerFactory(JUnitTestCase.java:385) at org.eclipse.persistence.testing.framework.junit.JUnitTestCase.getEntityManagerFactory(JUnitTestCase.java:412) at org.eclipse.persistence.testing.framework.junit.JUnitTestCase.createEntityManager(JUnitTestCase.java:328) at org.eclipse.persistence.testing.tests.jpa.CMP3TestModel.createEntityManager(CMP3TestModel.java:95) at org.eclipse.persistence.testing.tests.jpa.CMP3TestModel.getEntityManager(CMP3TestModel.java:80) at org.eclipse.persistence.testing.tests.jpa.CMP3TestModel.getServerSession(CMP3TestModel.java:100) at org.eclipse.persistence.testing.tests.jpa.advanced.JPAAdvancedTestModel.setup(JPAAdvancedTestModel.java:32) Caused by: Exception [EclipseLink-7144] (Eclipse Persistence Services - 2.2.0.qualifier): org.eclipse.persistence.exceptions.ValidationException Exception Description: SCIENTIST_SEQ: platform DatabasePlatform does not support NativeSequence. at org.eclipse.persistence.exceptions.ValidationException.platformDoesNotSupportSequence(ValidationException.java:1959) at org.eclipse.persistence.sequencing.NativeSequence.onConnect(NativeSequence.java:143) at org.eclipse.persistence.sequencing.Sequence.onConnect(Sequence.java:270) at org.eclipse.persistence.internal.sequencing.SequencingManager.onConnectAllSequences(SequencingManager.java:843) at org.eclipse.persistence.internal.sequencing.SequencingManager.onConnect(SequencingManager.java:706) at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.initializeSequencing(DatabaseSessionImpl.java:148) at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.initializeDescriptors(DatabaseSessionImpl.java:420) at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.initializeDescriptors(DatabaseSessionImpl.java:406) at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:376) </testcase> <testcase classname="org.eclipse.persistence.testing.tests.jpa.advanced.EntityManagerJUnitTestSuite" name="testEMFWrapValidationException" time="4.368"> <error type="java.lang.NullPointerException">java.lang.NullPointerException at org.eclipse.persistence.testing.tests.jpa.advanced.EntityManagerJUnitTestSuite.testEMFWrapValidationException(EntityManagerJUnitTestSuite.java:4314) at org.eclipse.persistence.testing.framework.junit.JUnitTestCase.runBare(JUnitTestCase.java:466) at org.eclipse.persistence.testing.framework.TestExecutor.execute(TestExecutor.java:248) at org.eclipse.persistence.testing.framework.TestModel.execute(TestModel.java:208) at org.eclipse.persistence.testing.framework.TestCollection.run(TestCollection.java:313) </error> </testcase> <testcase classname="org.eclipse.persistence.testing.tests.jpa.xml.merge.inherited.EntityMappingsMergeInheritedJUnitTestCase" name="testVerifyOneToManyRelationships" time="0.0"> <error type="java.lang.NullPointerException">java.lang.NullPointerException at org.eclipse.persistence.testing.tests.jpa.xml.merge.inherited.EntityMappingsMergeInheritedJUnitTestCase.testVerifyOneToManyRelationships(EntityMappingsMergeInheritedJUnitTestCase.java:240) at org.eclipse.persistence.testing.framework.junit.JUnitTestCase.runBare(JUnitTestCase.java:466) at org.eclipse.persistence.testing.framework.TestExecutor.execute(TestExecutor.java:248) at org.eclipse.persistence.testing.framework.TestModel.execute(TestModel.java:208) at org.eclipse.persistence.testing.framework.TestCollection.run(TestCollection.java:313) </error> </testcase> <testcase classname="org.eclipse.persistence.testing.tests.jpa.delimited.DelimitedPUTestSuite" name="testReadEmployee" time="0.0"> <error message="getSingleResult() did not retrieve any entities." type="javax.persistence.NoResultException">javax.persistence.NoResultException: getSingleResult() did not retrieve any entities. at org.eclipse.persistence.internal.jpa.EJBQueryImpl.throwNoResultException(EJBQueryImpl.java:1246) at org.eclipse.persistence.internal.jpa.EJBQueryImpl.getSingleResult(EJBQueryImpl.java:750) at org.eclipse.persistence.testing.tests.jpa.delimited.DelimitedPUTestSuite.testReadEmployee(DelimitedPUTestSuite.java:105) at org.eclipse.persistence.testing.framework.junit.JUnitTestCase.runBare(JUnitTestCase.java:466) at org.eclipse.persistence.testing.framework.TestExecutor.execute(TestExecutor.java:248) at org.eclipse.persistence.testing.framework.TestModel.execute(TestModel.java:208) at org.eclipse.persistence.testing.framework.TestCollection.run(TestCollection.java:313) </error> </testcase>
Fixed 106:
DI 107: 20110228: Metamodel missing Dynamic JPA metadata requires Regeneration
- After an email request from product management surrounding the rethrow validation exception during metamodel initialization - see line 2245 of EntityManagerSetupImpl.initializeCanonicalMetamodel().
throw v;
- If entities are added at runtime via dynamic persistence - these entities are missing from the runtime and design time (_ underscore classes) Metamodel.
- One alternative may be to null and regenerate the metamodel.
- We regenerate the metamodel when it becomes inconsistent with dynamic entities. We ran into this issue when we moved the metamodel generation from "on-demand" during em creation to emf.predeploy.
- In this case we could call the existing internal API (out of spec) set/getMetamodel - by nulling the metamodel and regenerating - we should pick up the merged metadata from (xml, annotations and dynamic api - all three). However this will not solve the design time _ class generation - only the internal metamodel runtime instance
// clear the metamodel (INTERNAL) - clears the metamodel on emsetupimpl emf.setMetamodel(null); // re-get the metamodel - and invoke regeneration emf.getMetamodel()
- See EntityManagerFactory.......
/** * INTERNAL: * Convenience function to allow us to reset the Metamodel * in the possible case that we want to regenerate it. * This function is outside of the JPA 2.0 specification. * @param aMetamodel * @since Java Persistence 2.0 */ public void setMetamodel(Metamodel aMetamodel) { this.metaModel = aMetamodel; }
DI 108: 20110321: Expose JPA Metamodel with Extensible API relationships via JMX Management API
- After a quick discussion with Peter on exposing the Extensibility API via JMX - it may be a better solution to expose the entire Metamodel which would include any Extensible relationships added dynamically to the schema.
- This enhancement for EclipseLink will be documented as part of the change in the following design wiki.
- http://wiki.eclipse.org/EclipseLink/DesignDocs/332312#JPA_2.0_Metamodel_Exposure_via_JMX
- See overall Management API enhancements in bug # 332312
- See specific Management API enhancements in bug # 337033 for Metamodel exposure
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
Missing Test Model Attributes
- 315287:BasicType as inheritance root
- DirectCollection
- 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 - where the two entities have separate tables.
- Entity-->MappedSuperclass(Abstract)-->Entity hierarchy
- Location(E)-->CoordinateMS(MS)-->GalacticPosition(E)
- Subclassing a MappedSuperclass from a root Entity is currently supported in JPA 2.0, although this is not explicitly stated...
- See p.53 section 2.11.2 and p.398 section 11.1.34 of the JPA 2.0 specification - "A class designated with the MappedSuperclass annotation can be mapped in the same way as an entity except the the mappings will apply only to its subclasses since no table exists for the mapped superclass itself". Since Entities can extend MappedSuperclasses or other Entities.
- For references see p.302 listing 10-27 of the JPA 2.0 book Pro JPA2: Mastering the Java Persistence API - where a MappedSuperclass extends an Entity.
- 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
- Maps
- Embeddable as Map key
- Embeddable as Map value
- Map field in Embedded - see 297748
- A unidirectional @ManyToMany in Observation back to Person (as observer)
- Embeddable as ManagedType
- 20101130 - 300626: Nested Embeddable ObservationDetail to Observation on GalacticPosition added - diagram needs to be updated
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.
- org\eclipse\persistence\sessions
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="${release.version}",${line.separator}" file="META-INF/MANIFEST.MF" append="true"/> ... <echo message=" org.eclipse.persistence.internal.jpa.metamodel;version="${release.version}",${line.separator}" file="META-INF/MANIFEST.MF" append="true"/> ... <echo message=" javax.persistence.criteria;version="[1.99.0,2.1.0)",${line.separator}" file="META-INF/MANIFEST.MF" append="true"/> <echo message=" javax.persistence.metamodel;version="[1.99.0,2.1.0)",${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="${jpaproto.version}"${line.separator}" file="${jpaproto.resource.dir}/MANIFEST.MF" append="true"/> <echo message=" javax.persistence.metamodel;version="${jpaproto.version}"${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
- The EclipseLink descriptor off the session contains all the mappings we require to build implementations of the new Metamodel API interfaces
- 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
- The following issues may have occurred
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)
- See HashMap.put(K,V) "Associates the specified value with the specified key in this map. If the map previously contained a mapping for the key, the old value is replaced" in http://java.sun.com/javase/6/docs/api/java/util/HashMap.html
- 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
- Added 2 new exports for (metamodel and criteria) to the manifest - correction, it is generated - we do this in
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)
- /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/AbstractCollection.java 4265 deleted
- 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
- /trunk/jpa/plugins/javax.persistence/src/javax/persistence/metamodel/PluralAttribute.java 4265 (+56) 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
- http://fisheye2.atlassian.com/browse/eclipselink/trunk/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metadata/accessors/objects/MetadataAccessibleObject.java?r1=4170&r2=4254
- the getLocation() function has changed to a more specific getClass() instead of one of URL, Class, Method or Field or we could use the new ORMetadata.getLocation().
- From: Object key = this.getClassAccessor().getAccessibleObject().getLocation();
- To: Object key = this.getClassAccessor().getAccessibleObject().getClass();
- Attribute.java
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
- 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()
- verify no validation for
- 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
- 456: verify we can skip compositePrimaryKey JoinColumn Metadata processing when
- 9) MetadataAnnotatedElement
- 240: verify Void parameterized generics override
- 10) InheritanceMetadata
- 81: no change to accessor.processInheritancePrimaryKeyJoinColumns();
- 11) MetadataDescriptor
- 872: verify early return with empty DatabaseTable is ok for getPrimaryTable() - same as for aggregateDescriptor
- if(hasDescriptorReservedForMetamodelAsParent()) {
- return new DatabaseTable();
- if(hasDescriptorReservedForMetamodelAsParent()) {
- 872: verify early return with empty DatabaseTable is ok for getPrimaryTable() - same as for aggregateDescriptor
- 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
- By picking up our MS descriptors here - we end up causing them to be processed by initialize() during login() which we do not want
- 972: verify secondary check of our custom map of MappedSuperclass descriptors for entities
- 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)
Version 20090725 PFD1 and 20090831 PFD2 Differences
- The following changes need to be made to the following location for enhancement# 288267.
- See parallel corresponding Criteria API changes in 5088.
- <trunk>\jpa\plugins\javax.persistence\src\javax\persistence\metamodel
- Metamodel changes :
- The only code changes are ManagedType.get*Collections to get*PluralAttributes
- IdentifiableType
- - getDeclaredId and getVersion - switched places - no update required
- ManagedType
- - added javadoc
* Returns empty set if the managed type has no declared * attributes. - reordered <E> CollectionAttribute<X, E> getDeclaredCollection(String name, Class<E> elementType); <E> SetAttribute<X, E> getDeclaredSet(String name, Class<E> elementType); <E> ListAttribute<X, E> getDeclaredList(String name,
Appendix B: Deprecated Design Documentation Sections
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
- Investigate EclipseLink Metamodel
- 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
- Create test model
- D Metamodel Build modifications
- P Metamodel XML/XSD modifications
- Metamodel integration with EclipseLink Workbench
- P Metamodel Design Documentation
- Metamodel User Documentation
- 20110112:
- Changes in bug# 333656 (if it is an issue) may affect design issues 98, 79 and 86
- http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_98:_20091109:_MapAttribute_keyType_processing_should_offload_more_to_MappedKeyContainerPolicy.keyMapping
- http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_79:_20090910:_MapAttribute_keyType_requires_.40MapKey_handler_in_the_template_case_when_CMP3Policy.getPKClass.28.29_is_null
- http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_86:_20090921:_Handle_Embeddable_Type_keyType_in_MapAttributeImpl_constructor
User Use Cases of the Metamodel API
- Now that JPA 2.0 has a metadata introspection API for the persistence unit in the form of the Metamodel - what can we use the Metamodel API for - outside of its use in the Criteria API?
- Examining the model
- Creating a query on the fly - thank you Laird Nelson
References
- See ELUG documentation on Canonical Metamodel Classes Generation http://wiki.eclipse.org/UserGuide/JPA/Using_the_Canonical_Model_Generator_%28ELUG%29
- EclipseLink Development on 64 bit Windows 7 or Vista
- NetBeans bug# 289616: JPA 1.0 project should not require generating JPA 2.0 Metamodel classes
- NetBeans bug# 189697: JPA application does not run on WebLogic with TopLink provider because of the generated metamodel